minimal-emacs.d - Emacs init.el and early-init.el for Better Defaults and Optimized Startup, intended to serve as a solid foundation for your vanilla Emacs configuration | Emacs Starter Kit
897
stars
1,046
commits
Emacs Lisp
primary language
Sep 10, 2026
updated
init.el and early-init.el for Better Defaults and Optimized StartupThe minimal-emacs.d project is a fast and lightweight minimal Emacs starter kit (init.el and early-init.el) that gives you full control over your configuration. It provides better defaults, an optimized startup, and a clean foundation for building your own vanilla Emacs setup.
Each setting in minimal-emacs.d is carefully chosen to answer this question: does it provide a better default that modernizes Emacs while keeping it lightweight, fast and stable?
In just a few minutes of applying what's in this README.md file, you will have a fully functional, high-performance Emacs configuration ready for work. You will bypass hours of configuration and the heavy overhead of frameworks like Doom or Spacemacs, gaining access to optimized garbage collection, sensible defaults, and a fast startup.
NOTE: If this project helps your workflow, please consider supporting it by ⭐ starring minimal-emacs.d on GitHub and sharing it on your website, blog, Mastodon, Reddit, X, LinkedIn, or other social media platforms so other Emacs users can discover its benefits.
Ready to start? Install minimal-emacs.d
Building the minimal-emacs.d init.el and early-init.el was the result of extensive research and testing to fine-tune the best parameters and optimizations for an Emacs configuration. (More information about the minimal-emacs.d features can be found here: Features.)
The minimal-emacs.d project is:
The minimal-emacs.d project includes two initialization files:
early-init.el: Loaded early in the Emacs startup process, before the graphical interface is initialized. Introduced in Emacs 27, this file configures settings that influence startup performance and GUI behavior prior to package loading.init.el: Loaded after the graphical interface is initialized. This file contains user customizations, including variable settings, package loading, mode configurations, and keybindings.Skip to: Install minimal-emacs.d
Excluding empty lines, comments, and docstrings, the minimal-emacs.d configuration is approximately 450 lines long. It does not introduce additional functionality beyond offering improved default settings. You retain full control over which packages to install and which modes to enable.
(The theme shown in the screenshot above is ef-melissa-light, which is part of the ef-themes collection available on MELPA.)
(The theme shown in the screenshot above is doom-one, which is part of the doom-themes collection available on MELPA.)
(The theme shown in the screenshot above is the tomorrow-night-deepblue-theme.el, available on MELPA.)
The author uses minimal-emacs.d as his early-init.el and init.el, alongside 146 packages (See the packages that the author is using here). Yet, thanks to its efficient design, Emacs still starts in just 0.22 seconds:

Startup speed depends on hardware and disk speed. For consistent comparisons, test on the same computer and Emacs version. While startup time is significant, factors like native compilation are also important for long-term performance.
Skip to: Install minimal-emacs.d
init.el and early-init.el files with those from the project.Please share your configuration. It could serve as inspiration for other users.
~/.emacs and ~/.emacs.el files do not exist. These files cause Emacs to ignore ~/.emacs.d/init.el. This behavior is due to the way Emacs searches for initialization files (more information). Simply delete the ~/.emacs and ~/.emacs.el files avoid this issue.emacs --debug-init to enable debug mode and obtain the backtrace.~/.emacs.dExecute the following command install this repository into ~/.emacs.d:
git clone --depth 1 https://github.com/jamescherti/minimal-emacs.d ~/.emacs.d
~/.minimal-emacs.dTo install minimal-emacs.d in a non-default directory, use the --init-directory Emacs option to specify your desired configuration path. For example, to install minimal-emacs.d in ~/.minimal-emacs.d/, follow these steps:
Clone the repository into ~/.minimal-emacs.d/ using:
git clone --depth 1 https://github.com/jamescherti/minimal-emacs.d ~/.minimal-emacs.d
Then start Emacs with the new configuration directory:
emacs --init-directory ~/.minimal-emacs.d/
To keep your Emacs configuration up to date, you can pull the latest changes from the repository. Run the following command in your terminal:
git -C ~/.emacs.d pull
The init.el and early-init.el files should never be modified directly because they are intended to be managed by Git during an update.
The minimal-emacs.d init files support additional customization files that are loaded at different stages of the Emacs startup process. These files allow you to further customize the initialization sequence:
~/.emacs.d/pre-init.el: This file is loaded before init.el. Use it to set up variables or configurations that need to be available early in the initialization process but after early-init.el.
~/.emacs.d/post-init.el: This file is loaded after init.el. It is useful for additional configurations or package setups that depend on the configurations in init.el.
~/.emacs.d/pre-early-init.el: This file is loaded before early-init.el. Use it for configurations that need to be set even earlier in the startup sequence, typically affecting the initial setup of the Emacs environment.
~/.emacs.d/post-early-init.el: This file is loaded after early-init.el but before init.el. It is useful for setting up configurations that depend on the early initialization but need to be set before the main initialization begins.
Always begin your pre-init.el, post-init.el, post-early-init.el, and pre-early-init.el files with the following header to prevent them from being byte-compiled and to activate lexical binding:
;;; FILENAME.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*-
Replace FILENAME.el with the actual name and DESCRIPTION with a brief description of its purpose.
(Only if you know what you're doing: Removing no-byte-compile: t; from your init files allows Emacs to compile them, improving load and execution speed. However, if you do so, you may need to add required dependencies. For example, if you're using use-package, add (require 'use-package) at the top of post-init.el to ensure all necessary use-package variables and functions are loaded. The only init file where no-byte-compile: t should never be removed is early-init.el, because if this file is compiled, Emacs may load an outdated compiled version.)
Important: The examples in this README reference pre/post init files in the ~/.emacs.d/ directory, but the files pre-early-init.el, post-early-init.el, pre-init.el, and post-init.el should be placed in the same directory as init.el and early-init.el, regardless of their location.
All use-package declarations in this README use deferred loading, so you can safely copy and paste them into your configuration.
To ensure your configuration remains fast and responsive, always defer package loading so that libraries are initialized only when they are needed. The use-package macro makes this effortless; simply adding :commands or :bind to your package declarations automatically configures them for deferred loading.
Note: Enabling the tool-bar or menu-bar may slightly increase your startup time.
To customize your Emacs setup to include various user interface elements, you can use the following settings in your ~/.emacs.d/pre-early-init.el:
(setq minimal-emacs-ui-features '(context-menu tool-bar menu-bar dialogs tooltips))
These settings control the visibility of dialogs, context menus, toolbars, menu bars, and tooltips.
~/.emacs.d by redirecting files to ~/.emacs.d/var/Emacs, by default, stores various configuration files, caches, backups, and other data in the ~/.emacs.d directory. Over time, this directory can become cluttered with numerous files, making it difficult to manage and maintain.
A common solution to this issue is installing the no-littering package; however, this package is not essential.
An alternative lightweight approach is to simply change the default ~/.emacs.d directory to ~/.emacs.d/var/, which will contain all the files that Emacs typically stores in the base directory. This can be accomplished by adding the following code to ~/.emacs.d/pre-early-init.el:
;;; Reducing clutter in ~/.emacs.d by redirecting files to ~/.emacs.d/var/
;; NOTE: This must be placed in 'pre-early-init.el'.
(setq user-emacs-directory (expand-file-name "var/" minimal-emacs-user-directory))
(setq package-user-dir (expand-file-name "elpa" user-emacs-directory))
IMPORTANT: The code above should be added to ~/.emacs.d/pre-early-init.el, not the other files, as it modifies the behavior of all subsequent init files.
This README.md offers guidance on installing optional external packages. While Emacs and minimal-emacs.d are fully functional without them, the recommended packages can enhance your experience and introduce additional features, which is why they are suggested.
Native compilation enhances Emacs performance by converting Elisp code into native machine code, resulting in faster execution and improved responsiveness.
To check if native compilation is enabled, evaluate:
(native-comp-available-p)
(A non-nil result indicates that native compilation is available.)
Ensure all libraries are byte-compiled and native-compiled using compile-angel.el. To install compile-angel, add the following code to the ~/.emacs.d/post-init.el file:
;; Native compilation enhances Emacs performance by converting Elisp code into
;; native machine code, resulting in faster execution and improved
;; responsiveness.
;;
;; Ensure adding the following compile-angel code at the very beginning
;; of your `~/.emacs.d/post-init.el` file, before all other packages.
(use-package compile-angel
:demand t
:config
;; Set `compile-angel-verbose' to nil to disable compile-angel messages.
;; (When set to nil, compile-angel won't show which file is being compiled.)
(setq compile-angel-verbose t)
;; The following directive prevents compile-angel from compiling your init
;; files. If you choose to remove this push to `compile-angel-excluded-files'
;; and compile your pre/post-init files, ensure you understand the
;; implications and thoroughly test your code. For example, if you're using
;; the `use-package' macro, you'll need to explicitly add:
;; (eval-when-compile (require 'use-package))
;; at the top of your init file.
(push "/init.el" compile-angel-excluded-files)
(push "/early-init.el" compile-angel-excluded-files)
(push "/pre-init.el" compile-angel-excluded-files)
(push "/post-init.el" compile-angel-excluded-files)
(push "/pre-early-init.el" compile-angel-excluded-files)
(push "/post-early-init.el" compile-angel-excluded-files)
;; A local mode that compiles .el files whenever the user saves them.
;; (add-hook 'emacs-lisp-mode-hook #'compile-angel-on-save-local-mode)
;; A global mode that compiles .el files prior to loading them via `load' or
;; `require'. Additionally, it compiles all packages that were loaded before
;; the mode `compile-angel-on-load-mode' was activated.
(compile-angel-on-load-mode 1))
On macOS, GUI applications (launched from the Finder, Dock, or Spotlight) do not inherit the user's shell environment variables by default. This often causes errors where Emacs cannot find external tools like git, grep, pip, or language servers (LSP), even if they work perfectly in your terminal.
To fix this, add exec-path-from-shell to ~/.emacs.d/post-init.el:
(use-package exec-path-from-shell
:if (and (or (display-graphic-p) (daemonp))
(eq system-type 'darwin)) ; macOS only
:demand t
:functions exec-path-from-shell-initialize
:config
(dolist (var '("TMPDIR"
"SSH_AUTH_SOCK" "SSH_AGENT_PID"
"GPG_AGENT_INFO"
;; "FZF_DEFAULT_COMMAND" "FZF_DEFAULT_OPTS" ; fzf
;; "VIRTUAL_ENV" ; Python
;; "GOPATH" "GOROOT" "GOBIN" ; Go
;; "CARGO_HOME" "RUSTUP_HOME" ; Rust
;; "NVM_DIR" "NODE_PATH" ; Node/JS
"LANG" "LC_CTYPE"))
(add-to-list 'exec-path-from-shell-variables var))
;; Initialize
(exec-path-from-shell-initialize))
The recentf, savehist, saveplace, and auto-revert built-in packages are already configured by minimal-emacs.d. All you need to do is activate them by adding the following to ~/.emacs.d/post-init.el:
;; Auto-revert in Emacs is a feature that automatically updates the
;; contents of a buffer to reflect changes made to the underlying file
;; on disk.
(use-package autorevert
:ensure nil
:init
;; (setq auto-revert-verbose t)
(setq auto-revert-interval 3)
(setq auto-revert-remote-files nil)
(setq auto-revert-use-notify t)
(setq auto-revert-avoid-polling nil)
(global-auto-revert-mode 1))
;; Recentf is an Emacs package that maintains a list of recently
;; accessed files, making it easier to reopen files you have worked on
;; recently.
(use-package recentf
:ensure nil
:init
(setq recentf-auto-cleanup (if (daemonp) 300 'never))
(setq recentf-exclude
(list "\\.tar$" "\\.tbz2$" "\\.tbz$" "\\.tgz$" "\\.bz2$"
"\\.bz$" "\\.gz$" "\\.gzip$" "\\.xz$" "\\.zip$"
"\\.7z$" "\\.rar$"
"COMMIT_EDITMSG\\'"
"\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\|bmp\\|xpm\\)$"
"-autoloads\\.el$" "autoload\\.el$"))
;; Enable `recentf-mode'
(recentf-mode 1)
:config
;; A cleanup depth of -90 ensures that `recentf-cleanup' runs before
;; `recentf-save-list', allowing stale entries to be removed before the list
;; is saved by `recentf-save-list', which is automatically added to
;; `kill-emacs-hook' by `recentf-mode'.
(add-hook 'kill-emacs-hook #'recentf-cleanup -90))
;; savehist is an Emacs feature that preserves the minibuffer history between
;; sessions. It saves the history of inputs in the minibuffer, such as commands,
;; search strings, and other prompts, to a file. This allows users to retain
;; their minibuffer history across Emacs restarts.
(use-package savehist
:ensure nil
:init
(setq history-length 300)
(setq savehist-autosave-interval 600)
(savehist-mode 1))
;; save-place-mode enables Emacs to remember the last location within a file
;; upon reopening. This feature is particularly beneficial for resuming work at
;; the precise point where you previously left off.
(use-package saveplace
:ensure nil
:init
(setq save-place-limit 400)
(save-place-mode 1))
Enabling auto-save-mode mitigates the risk of data loss in the event of a crash. Auto-saved data can be recovered using the recover-file or recover-session functions.
To enable autosave, add the following to ~/.emacs.d/post-init.el:
;; Enable `auto-save-mode' to prevent data loss. Use `recover-file' or
;; `recover-session' to restore unsaved changes.
(setq auto-save-default t)
;; Trigger an auto-save after 300 keystrokes
(setq auto-save-interval 300)
;; Trigger an auto-save 30 seconds of idle time.
(setq auto-save-timeout 30)
Corfu enhances in-buffer completion by displaying a compact popup with current candidates, positioned either below or above the point. Candidates can be selected by navigating up or down.
Cape, or Completion At Point Extensions, extends the capabilities of in-buffer completion. It integrates with Corfu or the default completion UI, by providing additional backends through completion-at-point-functions.

To configure corfu and cape, add the following to ~/.emacs.d/post-init.el:
;; Corfu enhances in-buffer completion by displaying a compact popup with
;; current candidates, positioned either below or above the point. Candidates
;; can be selected by navigating up or down.
(use-package corfu
:init
(setq text-mode-ispell-word-completion nil)
;; Hide commands in M-x which do not apply to the current mode.
(setq read-extended-command-predicate #'command-completion-default-include-p)
;; Disable Ispell completion function. As an alternative try `cape-dict'.
(setq tab-always-indent 'complete)
(global-corfu-mode 1))
;; Cape, or Completion At Point Extensions, extends the capabilities of
;; in-buffer completion. It integrates with Corfu or the default completion UI,
;; by providing additional backends through completion-at-point-functions.
(use-package cape
:commands (cape-dabbrev cape-file cape-elisp-block)
:bind ("C-c p" . cape-prefix-map)
:init
;; Add to the global default value of `completion-at-point-functions' which is
;; used by `completion-at-point'.
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-elisp-block))
Note: Setting corfu-auto to t to enable automatic completion is discouraged for both performance and security reasons. Continuous polling of heavy completion-at-point functions (CAPFs) and external LSP servers on every keystroke introduces latency. More significantly, automatic completion introduces security vulnerabilities when working with untrusted code. This background parsing forces external binaries and language servers to process buffer contents without explicit user intent, creating attack vectors for arbitrary code execution. It is recommended to activate corfu-auto only in directories where all files are trusted.
Vertico, Consult, and Embark collectively enhance Emacs' completion and navigation capabilities.
Vertico provides a vertical completion interface, making it easier to navigate and select from completion candidates (e.g., when M-x is pressed).
Consult offers a suite of commands for efficient searching, previewing, and interacting with buffers, file contents, and more, improving various tasks.
Embark integrates with these tools to provide context-sensitive actions and quick access to commands based on the current selection, further improving user efficiency and workflow within Emacs. Together, they create a cohesive environment for managing completions and interactions.

Add the following to ~/.emacs.d/post-init.el to set up Vertico, Consult, and Embark:
;; Vertico provides a vertical completion interface, making it easier to
;; navigate and select from completion candidates (e.g., when `M-x` is pressed).
(use-package vertico
:init
;; (setq vertico-scroll-margin 0) ;; Different scroll margin
;; (setq vertico-count 20) ;; Show more candidates
;; (setq vertico-resize t) ;; Grow and shrink the Vertico minibuffer
;; (setq vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
(vertico-mode 1))
;; Vertico leverages Orderless' flexible matching capabilities, allowing users
;; to input multiple patterns separated by spaces, which Orderless then
;; matches in any order against the candidates.
(use-package orderless
:init
(setq completion-styles '(orderless basic))
(setq completion-category-overrides '((file (styles partial-completion))))
;; Emacs 31: partial-completion behaves like substring
(setq completion-pcm-leading-wildcard t))
;; Marginalia allows Embark to offer you preconfigured actions in more contexts.
;; In addition to that, Marginalia also enhances Vertico by adding rich
;; annotations to the completion candidates displayed in Vertico's interface.
(use-package marginalia
;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding
;; available in the *Completions* buffer, add it to the
;; `completion-list-mode-map'.
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
;; The :init section is always executed.
:init
;; Marginalia must be activated in the :init section of use-package such that
;; the mode gets enabled right away. Note that this forces loading the
;; package.
(marginalia-mode 1))
;; Embark integrates with Consult and Vertico to provide context-sensitive
;; actions and quick access to commands based on the current selection, further
;; improving user efficiency and workflow within Emacs. Together, they create a
;; cohesive environment for managing completions and interactions.
(use-package embark
:bind
(("C-." . embark-act) ;; pick some comfortable binding
("C-;" . embark-dwim) ;; good alternative: M-.
("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
:init
;; Optionally replace the key help with a completing-read interface
(setq prefix-help-command #'embark-prefix-help-command)
;; Show the Embark target at point via Eldoc. You may adjust the
;; Eldoc strategy, if you want to see the documentation from
;; multiple providers. Beware that using this can be a little
;; jarring since the message shown in the minibuffer can be more
;; than one line, causing the modeline to move up and down:
;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)
;; Add Embark to the mouse context menu. Also enable `context-menu-mode'.
;; (context-menu-mode 1)
;; (add-hook 'context-menu-functions #'embark-context-menu 100)
:config
;; Hide the mode line of the Embark live/completions buffers
(add-to-list 'display-buffer-alist
'("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
nil
(window-parameters (mode-line-format . none)))))
(use-package embark-consult)
;; Consult offers a suite of commands for efficient searching, previewing, and
;; interacting with buffers, file contents, and more, improving various tasks.
(use-package consult
;; Replace bindings. Lazily loaded by `use-package'.
:bind (;; C-c bindings in `mode-specific-map'
("C-c M-x" . consult-mode-command)
("C-c h" . consult-history)
("C-c k" . consult-kmacro)
("C-c m" . consult-man)
("C-c i" . consult-info)
([remap Info-search] . consult-info)
;; C-x bindings in `ctl-x-map'
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
;; Custom M-# bindings for fast register access
("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
("C-M-#" . consult-register)
;; Other custom bindings
("M-y" . consult-yank-pop) ;; orig. yank-pop
;; M-g bindings in `goto-map'
("M-g e" . consult-compile-error)
("M-g r" . consult-grep-match)
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
("M-g g" . consult-goto-line) ;; orig. goto-line
("M-g M-g" . consult-goto-line) ;; orig. goto-line
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
("M-g m" . consult-mark)
("M-g k" . consult-global-mark)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
;; M-s bindings in `search-map'
("M-s d" . consult-find) ;; Alternative: consult-fd
("M-s c" . consult-locate)
("M-s g" . consult-grep)
("M-s G" . consult-git-grep)
("M-s r" . consult-ripgrep)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)
("M-s k" . consult-keep-lines)
("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
;; Minibuffer history
:map minibuffer-local-map
("M-s" . consult-history) ;; orig. next-matching-history-element
("M-r" . consult-history)) ;; orig. previous-matching-history-element
;; The :init configuration is always executed (Not lazy)
:init
;; Tweak the register preview for `consult-register-load',
;; `consult-register-store' and the built-in commands. This improves the
;; register formatting, adds thin separator lines, register sorting and hides
;; the window mode line.
(advice-add #'register-preview :override #'consult-register-window)
(setq register-preview-delay 0.5)
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; Configure other variables and modes in the :config section,
;; after lazily loading the package.
:config
;; Optionally configure preview. The default value
;; is 'any, such that any key triggers the preview.
;; (setq consult-preview-key 'any)
;; (setq consult-preview-key "M-.")
;; (setq consult-preview-key '("S-<down>" "S-<up>"))
;; For some commands and buffer sources it is useful to configure the
;; :preview-key on a per-command basis using the `consult-customize' macro.
(consult-customize
consult-theme :preview-key '(:debounce 0.2 any)
consult-ripgrep consult-git-grep consult-grep consult-man
consult-bookmark consult-recent-file consult-xref
consult-source-bookmark consult-source-file-register
consult-source-recent-file consult-source-project-recent-file
;; :preview-key "M-."
:preview-key '(:debounce 0.4 any))
;; Optionally configure the narrowing key.
;; Both < and C-+ work reasonably well.
(setq consult-narrow-key "<") ;; "C-+"
;; Optionally make narrowing help available in the minibuffer.
;; You may want to use `embark-prefix-help-command' or which-key instead.
;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
)
The undo-fu package is a lightweight wrapper around Emacs' built-in undo system, providing more convenient undo/redo functionality while preserving access to the full undo history. The undo-fu-session package complements undo-fu by enabling the saving and restoration of undo history across Emacs sessions, even after restarting.
The default undo system in Emacs has two main issues that undo-fu fixes:
To install and configure these packages, add the following to ~/.emacs.d/post-init.el:
;; The undo-fu package is a lightweight wrapper around Emacs' built-in undo
;; system, providing more convenient undo/redo functionality.
(use-package undo-fu
:commands (undo-fu-only-undo
undo-fu-only-redo
undo-fu-only-redo-all
undo-fu-disable-checkpoint)
:init
(global-unset-key (kbd "C-z"))
(global-set-key (kbd "C-z") 'undo-fu-only-undo)
(global-set-key (kbd "C-S-z") 'undo-fu-only-redo))
;; The undo-fu-session package complements undo-fu by enabling the saving
;; and restoration of undo history across Emacs sessions, even after restarting.
(use-package undo-fu-session
:init
(undo-fu-session-global-mode 1))
For instance, to switch to a another theme than the default one, add the following to the ~/.emacs.d/post-init.el file:
(let ((inhibit-redisplay t))
;; Disable all active themes
(mapc #'disable-theme custom-enabled-themes)
;; Load the built-in theme
(load-theme 'modus-operandi t))
(If you prefer dark themes, replace modus-operandi with modus-vivendi.)
Emacs includes several built-in themes that you can use without installing additional packages:
tango-dark (Face colors using the Tango palette. Dark background.)tango (Face colors using the Tango palette. Light background.)modus-operandimodus-operandi-deuteranopiamodus-operandi-tintedmodus-operandi-tritanopiamodus-vivendimodus-vivendi-deuteranopiamodus-vivendi-tintedmodus-vivendi-tritanopiatsdh-dark (A dark theme used and created by Tassilo Horn.)tsdh-light (A light Emacs theme.)adwaita (Face colors similar to the default theme of Gnome 3 / Adwaita.)deeper-blue (Face colors using a deep blue background.)dichromacy (Face colors suitable for red/green color-blind users.)leuven-dark (Face colors with a dark background.)leuven (Face colors with a light background.)light-blue (Face colors utilizing a light blue background.)manoj-dark (Very high contrast faces with a black background.)misterioso (Predominantly blue/cyan faces on a dark cyan background.)wheatgrass (High-contrast green/blue/brown faces on a black background.)whiteboard (Face colors similar to markers on a whiteboard.)wombat (Medium-contrast faces with a dark gray background.)(To experiment with different themes, use M-x customize-themes.)
If you're interested in exploring third-party Emacs themes, consider the following:
ef-themes (available on MELPA): A collection of light and dark themes for GNU Emacs, designed to offer colorful yet highly legible options. They are aimed at users seeking something with more visual flair compared to the more minimalist modus-themes.doom-themes (available on MELPA): An extensive collection of high-quality, visually appealing themes for Emacs, designed to offer a sleek and modern aesthetic, while drawing inspiration from popular community themes.tomorrow-night-deepblue-theme (available on MELPA): A beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette. It features a deep blue background color that creates a calming atmosphere. This theme is a great choice for those who miss the blue themes that were trendy a few years ago. (The theme was inspired by classic text editors such as QuickBASIC, RHIDE, and Turbo Pascal, as well as tools such as Midnight Commander.)Configuring Vim keybindings in Emacs can greatly enhance your editing efficiency if you are accustomed to Vim's modal editing style. Add the following to ~/.emacs.d/post-init.el to set up Evil mode:
;; Uncomment the following if you are using undo-fu
;; (setq evil-undo-system 'undo-fu)
;; Vim emulation
(use-package evil
:init
;; It has to be defined before evil
(setq evil-want-integration t)
(setq evil-want-keybinding nil)
;; Make :s in visual mode operate only on the actual visual selection
;; (character or block), instead of the full lines covered by the selection
(setq evil-ex-visual-char-range t)
;; Use Vim-style regular expressions in search and substitute commands,
;; allowing features like \v (very magic), \zs, and \ze for precise matches
(setq evil-ex-search-vim-style-regexp t)
;; Enable automatic horizontal split below
(setq evil-split-window-below t)
;; Enable automatic vertical split to the right
(setq evil-vsplit-window-right t)
;; Disable echoing Evil state to avoid replacing eldoc
(setq evil-echo-state nil)
;; Do not move cursor back when exiting insert state
(setq evil-move-cursor-back nil)
;; Make `v$` exclude the final newline
(setq evil-v$-excludes-newline t)
;; Enable fine-grained undo behavior
(setq evil-want-fine-undo t)
;; Disable wrapping of search around buffer
(setq evil-search-wrap nil)
;; Allow C-h to delete in insert state
(setq evil-want-C-h-delete t)
;; Enable C-u to delete back to indentation in insert state
(setq evil-want-C-u-delete t)
;; Whether Y yanks to the end of the line
(setq evil-want-Y-yank-to-eol t)
;; Start `evil-mode'
(evil-mode 1)
:config
;; Occasionally, `evil' fails to respect `evil-search-module' when it is
;; defined inside the :custom block. This fix ensures the search module
;; is correctly set to `evil-search'.
(setq evil-search-module 'evil-search)
(evil-select-search-module 'evil-search-module 'evil-search))
(use-package evil-collection
:after evil
:init
;; It has to be defined before evil-collection
(setq evil-collection-setup-minibuffer t)
(evil-collection-init))
;; The goto-chg package is useful with Evil to jump directly to the most recent
;; edit location. This mirrors Vim's change navigation, allowing fast return to
;; where text was last modified without relying on the jump list or search.
;;
;; The goto-chg commands are bound to g; and g,
(use-package goto-chg
:commands (goto-last-change
goto-last-change-reverse))
You can also install the vim-tab-bar package to enhance the built-in Emacs tab-bar with a minimalist, Vim-inspired design that automatically adapts to the active Emacs theme. Beyond its Vim-inspired design, the vim-tab-bar package is valued by users who prioritize theme consistency, as it integrates the Emacs tab-bar with any Emacs theme, producing a visually coherent and polished interface:
;; Give Emacs tab-bar a style similar to Vim's
(use-package vim-tab-bar
:init
(vim-tab-bar-mode 1))

(The screenshot above showcases how vim-tab-bar modifies the built-in Emacs tab-bar.)
The evil-surround package simplifies handling surrounding characters, such as parentheses, brackets, quotes, etc. It provides key bindings to easily add, change, or delete these surrounding characters in pairs. For instance, you can surround the currently selected text with double quotes in visual state using S" or gS":
;; The evil-surround package simplifies handling surrounding characters, such as
;; parentheses, brackets, quotes, etc. It provides key bindings to easily add,
;; change, or delete these surrounding characters in pairs. For instance, you
;; can surround the currently selected text with double quotes in visual state
;; using S" or gS".
(use-package evil-surround
:after evil
:init
(setq evil-surround-pairs-alist
'((?\( . ("(" . ")"))
(?\[ . ("[" . "]"))
(?\{ . ("{" . "}"))
(?\) . ("(" . ")"))
(?\] . ("[" . "]"))
(?\} . ("{" . "}"))
(?< . ("<" . ">"))
(?> . ("<" . ">"))))
:config
(global-evil-surround-mode 1))
You can also add the following code to enable commenting and uncommenting by pressing gcc in normal mode and gc in visual mode (thanks you to the Reddit user u/mistakenuser for this contribution, which replaces the evil-commentary package):
;; The following code enables commenting and uncommenting by pressing gcc in
;; normal mode and gc in visual mode.
(with-eval-after-load "evil"
(evil-define-operator my-evil-comment-or-uncomment (beg end)
"Toggle comment for the region between BEG and END."
(interactive "<r>")
(comment-or-uncomment-region beg end))
(evil-define-key 'normal 'global (kbd "gc") 'my-evil-comment-or-uncomment))
The easysession package provides a comprehensive session management for Emacs. It is capable of persisting and restoring file-visiting buffers, indirect buffers (clones), buffer narrowing, Dired buffers, window configurations, the built-in tab-bar (including tabs, their buffers, and associated windows), as well as entire Emacs frames.
With easysession, your Emacs setup is restored automatically when you restart. All files, Dired buffers, and window layouts come back as they were, so you can continue working right where you left off. While editing, you can also switch to another session, switch back, rename sessions, or delete them, giving you full control over multiple work environments.
Easysession also supports extensions, enabling the restoration of Magit buffers and the scratch buffer. Custom extensions can also be created to extend its functionality.
To configure easysession, add the following to ~/.emacs.d/post-init.el:
;; The easysession Emacs package is a session manager for Emacs that can persist
;; and restore file editing buffers, indirect buffers/clones, Dired buffers,
;; windows/splits, the built-in tab-bar (including tabs, their buffers, and
;; windows), and Emacs frames. It offers a convenient and effortless way to
;; manage Emacs editing sessions and utilizes built-in Emacs functions to
;; persist and restore frames.
(use-package easysession
;; ':demand t' ensures the package is loaded immediately upon startup
:demand t
:config
;; Key mappings
(global-set-key (kbd "C-c sl") #'easysession-switch-to) ; Load session
(global-set-key (kbd "C-c ss") #'easysession-save) ; Save session
(global-set-key (kbd "C-c sL") #'easysession-switch-to-and-restore-geometry)
(global-set-key (kbd "C-c sr") #'easysession-rename)
(global-set-key (kbd "C-c sR") #'easysession-reset)
(global-set-key (kbd "C-c su") #'easysession-unload)
(global-set-key (kbd "C-c sd") #'easysession-delete)
;; Save every 10 minutes
(setq easysession-save-interval (* 10 60))
;; Save the current session when using `easysession-switch-to'
(setq easysession-switch-to-save-session t)
;; Do not exclude the current session when switching sessions
(setq easysession-switch-to-exclude-current nil)
;; Display the active session name in the mode-line lighter.
;; (setq easysession-save-mode-lighter-show-session-name t)
;; Optionally, the session name can be shown in the modeline info area:
;; (setq easysession-mode-line-misc-info t)
;; non-nil: Make `easysession-setup' load the session automatically.
;; (nil: session is not loaded automatically; the user can load it manually.)
(setq easysession-setup-load-session t)
;; The `easysession-setup' function adds hooks:
;; - To enable automatic session loading during `emacs-startup-hook', or
;; `server-after-make-frame-hook' when running in daemon mode.
;; - To save the session at regular intervals, and when Emacs exits.
(easysession-setup))
The markdown-mode package provides a major mode for Emacs 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).
To configure markdown-mode, add the following to ~/.emacs.d/post-init.el:
;; The markdown-mode package provides a major mode for Emacs 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).
(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)))
This configuration sets up markdown-mode with deferred loading to improve startup performance. The :commands and :mode keywords ensure that the mode is loaded only when needed-for example, when opening .md, .markdown, or README.md files. Files named README.md are specifically associated with gfm-mode, which is for GitHub Flavored Markdown syntax. The markdown-command variable is set to "multimarkdown" to specify the Markdown processor used for previews and exports. Additionally, a keybinding (C-c C-e) is defined in markdown-mode-map to invoke markdown-do, which can be customized to perform common Markdown-related actions.
Table of contents: To generate a table of contents when editing Markdown files, add the following to your ~/.emacs.d/post-init.el:
;; Automatically generate a table of contents when editing Markdown files
(use-package markdown-toc
:commands (markdown-toc-generate-toc
markdown-toc-generate-or-refresh-toc
markdown-toc-delete-toc
markdown-toc--toc-already-present-p)
:init
(setq markdown-toc-header-toc-title "**Table of Contents**"))
Once installed:
M-x markdown-toc-generate-tocM-x markdown-toc-generate-or-refresh-tocM-x markdown-toc-delete-tocThese commands work on any Markdown buffer and rely on properly formatted headers (e.g., #, ##) to build the table of contents.
The author also recommends reading the following article: Emacs: Automating Table of Contents Update for Markdown Documents (e.g., README.md).
NOTE: The following article provides a comprehensive guide on installing and enabling the supported folding modes: The Definitive Guide to Code Folding in Emacs.
The kirigami package provides a unified method to fold and unfold text in Emacs across a diverse set of Emacs modes.
Supported modes include: outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, gfm-mode, vdiff-mode, vdiff-3way-mode, hide-ifdef-mode, vimish-fold-mode, TeX-fold-mode (AUCTeX), fold-this-mode, origami-mode, yafolding-mode, folding-mode, ts-fold-mode, treesit-fold-mode, and hs-minor-mode (hideshow).
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 experience for opening and closing folds. The available commands include:
kirigami-open-fold: Open the fold at point.kirigami-open-fold-rec: Open the fold at point recursively.kirigami-close-fold: Close the fold at point.kirigami-open-folds: Open all folds in the buffer.kirigami-close-folds: Close all folds in the buffer.kirigami-toggle-fold: Toggle the fold at point.To configure kirigami, add the following to ~/.emacs.d/post-init.el:
(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
(("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 point
:init
(kirigami-global-mode 1))
;; 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))
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 experience for opening and closing folds.
In addition to unified interface for opening and closing folds, the kirigami package:
outline-mode, outline-minor-mode, markdown-mode, gfm-mode, and org-mode. It ensures that deep folds open reliably and permits closing folds even when the cursor is positioned within the content body. Additionally, it maintains window-start heading stability by automatically adjusting the scroll position to keep folded headings visible, preventing the context from disappearing when closing a fold that is partially scrolled off-screen.kirigami-pre-action-predicates and kirigami-post-action-functions, let external code run before and after every folding operation. The pre-action hook runs just before a fold is opened or closed and can allow or block the action. The post-action hook runs once the change is complete and can be used to update UI elements or keep external packages in sync with the new folding state.One of the modes that provide code folding is outline-minor-mode provides structured code folding in modes such as Emacs Lisp and Python, allowing users to collapse and expand sections based on headings or indentation levels. This feature enhances navigation and improves the management of large files with hierarchical structures.
Alternatively, hs-minor-mode offers basic code folding for blocks defined by curly braces, functions, or other language-specific delimiters. However, for more flexible folding that supports multiple nested levels, outline-minor-mode is generally the preferred choice, as it enables finer control over section visibility in deeply structured code.
For example, to enable outline-minor-mode:
;; The built-in outline-minor-mode provides structured code folding in modes
;; such as Emacs Lisp and Python, allowing users to collapse and expand sections
;; based on headings or indentation levels. This feature enhances navigation and
;; improves the management of large files with hierarchical structures.
(use-package outline
:ensure nil
:commands outline-minor-mode
:hook
(;; Use " ▼" instead of the default ellipsis "..." for folded text to make
;; folds more visually distinctive and readable.
(outline-minor-mode
.
(lambda()
(let* ((display-table (or buffer-display-table (make-display-table)))
(face-offset (* (face-id 'shadow) (ash 1 22)))
(value (vconcat (mapcar (lambda (c) (+ face-offset c)) " ▼"))))
(set-display-table-slot display-table 'selective-display value)
(setq buffer-display-table display-table))))))
;; Enable the mode
(add-hook 'emacs-lisp-mode-hook #'outline-minor-mode)
(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)
To enable hs-minor-mode, which is ideal for C-style languages and others that use braces {}:
(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 'sh-mode-hook #'hs-minor-mode)
(add-hook 'html-mode-hook #'hs-minor-mode)
For folding based on indentation levels, the outline-indent Emacs package provides a minor mode that enables folding according to the indentation structure:
;; The outline-indent Emacs package provides a minor mode that enables code
;; folding based on indentation levels.
;; In addition to code folding, outline-indent allows:
;; - Moving indented blocks up and down
;; - Indenting/unindenting to adjust indentation levels
;; - Inserting a new line with the same indentation level as the current line
;; - Move backward/forward to the indentation level of the current line
;; - and other features.
(use-package outline-indent
:commands outline-indent-minor-mode
:init
(setq 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)

It is also recommended to install treesit-fold, which 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. This allows for faster and more precise folding behavior that respects the grammar of the programming language, ensuring that fold boundaries are always syntactically correct even in complex or nested code structures.
;; 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. This allows for faster and more
;; precise folding behavior that respects the grammar of the programming
;; language, ensuring that fold boundaries are always syntactically correct even
;; in complex or nested code structures.
(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)
:init
(setq treesit-fold-line-count-show t)
(setq treesit-fold-line-count-format " ▼")
:config
(set-face-attribute 'treesit-fold-replacement-face nil
:foreground "#808080"
:box nil
:weight 'bold))
;; A few examples
(add-hook 'c-ts-mode-hook #'treesit-fold-mode)
(add-hook 'c++-ts-mode-hook #'treesit-fold-mode)
(add-hook 'php-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 'bash-ts-mode-hook #'treesit-fold-mode)
Apheleia is an Emacs package designed to run code formatters asynchronously without disrupting the cursor position. Code formatters like Shfmt, Black and Prettier ensure consistency and improve collaboration by automating formatting, but running them on save can introduce latency (e.g., Black takes around 200ms on an empty file) and unpredictably move the cursor when modifying nearby text.
Apheleia solves both problems across all languages, replacing language-specific packages like Blacken and prettier-js. It does this by invoking formatters in an after-save-hook, ensuring changes are applied only if the buffer remains unmodified.
To maintain cursor stability, Apheleia generates an RCS patch, applies it selectively, and employs a dynamic programming algorithm to reposition the cursor if necessary. If the formatting alters the vertical position of the cursor in the window, Apheleia adjusts the scroll position to preserve visual continuity across all displayed instances of the buffer. This allows enjoying automated code formatting without sacrificing editor responsiveness or usability.
To configure apheleia, add the following to ~/.emacs.d/post-init.el:
;; Apheleia is an Emacs package designed to run code formatters (e.g., Shfmt,
;; Black and Prettier) asynchronously without disrupting the cursor position.
(use-package apheleia
:commands (apheleia-mode
apheleia-global-mode)
:hook ((prog-mode . apheleia-mode)))
The dumb-jump package provides context-aware 'go to definition' functionality for 50+ programming languages without requiring a language server. It works by using simple heuristics and regular expression searches to locate the definitions of functions, variables, and symbols across project files.
Unlike more sophisticated language-aware tools (e.g., eglot or lsp-mode), dumb-jump' does not parse code semantically, which makes it lightweight and fast, but sometimes less precise. It integrates with popular navigation packages like xref', allowing users to jump to definitions or references.
To configure dumb-jump, add the following to ~/.emacs.d/post-init.el:
(use-package dumb-jump
:commands dumb-jump-xref-activate
:init
;; Register `dumb-jump' as an xref backend so it integrates with
;; `xref-find-definitions'. A priority of 80 ensures it is used only when no
;; more specific backend is available.
(with-eval-after-load 'xref
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate 80))
(setq dumb-jump-aggressive nil)
;; (setq dumb-jump-quiet t)
;; Number of seconds a rg/grep/find command can take before being warned to
;; use ag and config.
(setq dumb-jump-max-find-time 3)
;; Use `completing-read' so that selection of jump targets integrates with the
;; active completion framework (e.g., Vertico, Ivy, Helm, Icomplete),
;; providing a consistent minibuffer-based interface whenever multiple
;; definitions are found.
(setq dumb-jump-selector 'completing-read)
:config
;; If ripgrep is available, force `dumb-jump' to use it because it is
;; significantly faster and more accurate than the default searchers (grep,
;; ag, etc.).
(when (executable-find "rg")
(setq dumb-jump-force-searcher 'rg)
(setq dumb-jump-prefer-searcher 'rg)))
The yasnippet package provides a template system that enhances text editing by enabling users to define and use snippets, which are predefined templates of code or text. The user triggers snippet expansion by pressing the Tab key after typing an abbreviation, such as if. Upon pressing Tab, YASnippet replaces the abbreviation with the corresponding full template, allowing the user to fill in placeholders or fields within the expanded snippet.
The yasnippet-snippets package with a comprehensive collection of bundled templates for numerous programming and markup languages, including C, C++, C#, Perl, Python, Ruby, SQL, LaTeX, HTML, CSS...
(NOTE: Users of UltiSnips, a popular snippet engine for Vim, can export their snippets to YASnippet format using the tool ultyas)
;; The official collection of snippets for yasnippet.
(use-package yasnippet-snippets)
;; YASnippet is a template system designed that enhances text editing by
;; enabling users to define and use snippets. When a user types a short
;; abbreviation, YASnippet automatically expands it into a full template, which
;; can include placeholders, fields, and dynamic content.
(use-package yasnippet
:after yasnippet-snippets
:init
(setq yas-also-auto-indent-first-line t) ; Indent first line of snippet
(setq yas-also-indent-empty-lines t)
(setq yas-snippet-revival nil) ; Setting this to t causes issues with undo
(setq yas-wrap-around-region nil) ; Do not wrap region when expanding snippets
(setq yas-indent-line 'fixed) ; Do not auto-indent snippet content
;; (setq yas-triggers-in-field nil) ; Disable nested snippet expansion
;; (setq yas-prompt-functions '(yas-no-prompt)) ; No prompt for snippet choices
;; Suppress verbose messages
(setq yas-verbosity 0)
(yas-global-mode 1))
Trailing whitespace refers to any spaces or tabs that appear after the last non-whitespace character on a line. These characters have no semantic value and can lead to unnecessary diffs in version control, inconsistent formatting, or visual clutter. Removing them improves code clarity and consistency.
The stripspace Emacs package provides stripspace-local-mode, a minor mode that automatically removes trailing whitespace and blank lines at the end of the buffer when saving.
To enable stripspace and automatically delete trailing whitespace, add the following configuration to ~/.emacs.d/post-init.el:
;; The stripspace Emacs package provides stripspace-local-mode, a minor mode
;; that automatically removes trailing whitespace and blank lines at the end of
;; the buffer when saving.
(use-package stripspace
:commands stripspace-local-mode
;; Enable for prog-mode-hook, text-mode-hook, conf-mode-hook
:hook ((prog-mode . stripspace-local-mode)
(text-mode . stripspace-local-mode)
(conf-mode . stripspace-local-mode))
:init
;; The `stripspace-only-if-initially-clean' option:
;; - nil to always delete trailing whitespace.
;; - Non-nil to only delete whitespace when the buffer is clean initially.
;; (The initial cleanliness check is performed when `stripspace-local-mode'
;; is enabled.)
(setq stripspace-only-if-initially-clean nil)
;; Enabling `stripspace-restore-column' preserves the cursor's column position
;; even after stripping spaces. This is useful in scenarios where you add
;; extra spaces and then save the file. Although the spaces are removed in the
;; saved file, the cursor remains in the same position, ensuring a consistent
;; editing experience without affecting cursor placement.
(setq stripspace-restore-column t))
The diff-hl package highlights uncommitted changes in the window margin, enabling navigation between them. Also known as source control gutter indicators, it displays added, modified, and deleted lines in real time. In Git-controlled buffers, changes can be staged and unstaged directly, providing a clear view of version-control changes without running git diff. By default, the module does not start diff-hl-mode automatically.

To configure the diff-hl package, add the following to your ~/.emacs.d/post-init.el:
(use-package diff-hl
:commands (diff-hl-mode
global-diff-hl-mode)
:hook (prog-mode . diff-hl-mode)
:init
(setq diff-hl-flydiff-delay 0.4) ; Faster
(setq diff-hl-show-staged-changes nil) ; Realtime feedback
(setq diff-hl-update-async t) ; Do not block Emacs
(setq diff-hl-global-modes '(not pdf-view-mode image-mode)))
Org mode is a major mode designed for organizing notes, planning, task management, and authoring documents using plain text with a simple and expressive markup syntax. It supports hierarchical outlines, TODO lists, scheduling, deadlines, time tracking, and exporting to multiple formats including HTML, LaTeX, PDF, and Markdown.
To configure org-mode, add the following to ~/.emacs.d/post-init.el:
;; Org mode is a major mode designed for organizing notes, planning, task
;; management, and authoring documents using plain text with a simple and
;; expressive markup syntax. It supports hierarchical outlines, TODO lists,
;; scheduling, deadlines, time tracking, and exporting to multiple formats
;; including HTML, LaTeX, PDF, and Markdown.
(use-package org
:commands (org-mode org-version)
:mode
("\\.org\\'" . org-mode)
:init
(setq org-hide-leading-stars t)
(setq org-startup-indented t)
(setq org-adapt-indentation nil)
(setq org-edit-src-content-indentation 0)
;; (setq org-fontify-done-headline t)
;; (setq org-fontify-todo-headline t)
;; (setq org-fontify-whole-heading-line t)
;; (setq org-fontify-quote-and-verse-blocks t)
(setq org-startup-truncated t))
The org-appear package temporarily reveals normally hidden elements (such as emphasis markers, links, or entities) when the cursor enters them, and hides them again when the cursor leaves. To configure org-appear, add the following to ~/.emacs.d/post-init.el:
(use-package org-appear
:commands org-appear-mode
:hook (org-mode . org-appear-mode))
To set up Language Server Protocol (LSP) servers using Eglot, you can configure it, add the following to ~/.emacs.d/post-init.el:
;; Set up the Language Server Protocol (LSP) servers using Eglot.
(use-package eglot
:ensure nil
:commands (eglot-ensure
eglot-rename
eglot-format-buffer))
Here is an example of how to configure Eglot to enable or disable certain options for the pylsp server in Python development. (Note that a third-party tool, python-lsp-server, must be installed):
;; Configure Eglot to enable or disable certain options for the pylsp server
;; in Python development. (Note that a third-party tool,
;; https://github.com/python-lsp/python-lsp-server, must be installed),
(add-hook 'python-mode-hook #'eglot-ensure)
(add-hook 'python-ts-mode-hook #'eglot-ensure)
(setq-default eglot-workspace-configuration
`(:pylsp (:plugins
(;; Fix imports and syntax using `eglot-format-buffer`
:isort (:enabled t)
:autopep8 (:enabled t)
;; Syntax checkers (works with Flymake)
:pylint (:enabled t)
:pycodestyle (:enabled t)
:flake8 (:enabled t)
:pyflakes (:enabled t)
:pydocstyle (:enabled t)
:mccabe (:enabled t)
:yapf (:enabled :json-false)
:rope_autoimport (:enabled :json-false)))))
Related articles:
The buffer-terminator Emacs package automatically and safely kills buffers, ensuring a clean and efficient workspace while enhancing the performance of Emacs by reducing open buffers, which minimizes active modes, timers, processes...
Beyond performance, buffer-terminator provides other benefits. For instance, if you occasionally need to close annoying or unused buffers, buffer-terminator can handle this automatically, eliminating the need for manual intervention. (The default configuration is suitable for most users. However, the buffer-terminator package is highly customizable. You can define specific rules for retaining or terminating buffers by modifying the buffer-terminator-rules-alist with your preferred set of rules.)
To configure buffer-terminator, add the following to ~/.emacs.d/post-init.el:
(use-package buffer-terminator
:init
;; Enable/Disable verbose mode to log buffer cleanup events
(setq buffer-terminator-verbose nil)
;; Set the inactivity timeout (in seconds) after which buffers are considered
;; inactive (default is 30 minutes):
(setq buffer-terminator-inactivity-timeout (* 30 60)) ; 30 minutes
;; Define how frequently the cleanup process should run (default is every 10
;; minutes):
(setq buffer-terminator-interval (* 10 60)) ; 10 minutes
(buffer-terminator-mode 1))
(By default, buffer-terminator automatically determines which buffers are safe to terminate. However, if you need to define specific rules for keeping or terminating certain buffers, you can configure them using buffer-terminator-rules-alist.)
The treemacs package is a file and project explorer for Emacs that provides a visually structured tree layout similar to file browsers in modern IDEs. It integrates well with various Emacs packages such as projectile, lsp-mode, and magit, allowing users to navigate their project structure efficiently.

To configure treemacs, add the following to ~/.emacs.d/post-init.el:
;; A file and project explorer for Emacs that displays a structured tree
;; layout, similar to file browsers in modern IDEs. It functions as a sidebar
;; in the left window, providing a persistent view of files, projects, and
;; other elements.
(use-package treemacs
:commands (treemacs
treemacs-select-window
treemacs-delete-other-windows
treemacs-select-directory
treemacs-bookmark
treemacs-find-file
treemacs-find-tag)
:bind
(:map global-map
("M-0" . treemacs-select-window)
("C-x t 1" . treemacs-delete-other-windows)
("C-x t t" . treemacs)
("C-x t d" . treemacs-select-directory)
("C-x t B" . treemacs-bookmark)
("C-x t C-t" . treemacs-find-file)
("C-x t M-t" . treemacs-find-tag))
:init
(with-eval-after-load 'winum
(define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
:config
(setq treemacs-collapse-dirs (if treemacs-python-executable 3 0)
treemacs-deferred-git-apply-delay 0.5
treemacs-directory-name-transformer #'identity
treemacs-display-in-side-window t
treemacs-eldoc-display 'simple
treemacs-file-event-delay 2000
treemacs-file-extension-regex treemacs-last-period-regex-value
treemacs-file-follow-delay 0.2
treemacs-file-name-transformer #'identity
treemacs-follow-after-init t
treemacs-expand-after-init t
treemacs-find-workspace-method 'find-for-file-or-pick-first
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-header-scroll-indicators '(nil . "^^^^^^")
treemacs-hide-dot-git-directory t
treemacs-indentation 2
treemacs-indentation-string " "
treemacs-is-never-other-window nil
treemacs-max-git-entries 5000
treemacs-missing-project-action 'ask
treemacs-move-files-by-mouse-dragging t
treemacs-move-forward-on-expand nil
treemacs-no-png-images nil
treemacs-no-delete-other-windows t
treemacs-project-follow-cleanup nil
treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
treemacs-position 'left
treemacs-read-string-input 'from-child-frame
treemacs-recenter-distance 0.1
treemacs-recenter-after-file-follow nil
treemacs-recenter-after-tag-follow nil
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-litter-directories '("/node_modules" "/.venv" "/.cask")
treemacs-project-follow-into-home nil
treemacs-show-cursor nil
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-asc
treemacs-select-when-already-in-treemacs 'move-back
treemacs-space-between-root-nodes t
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 1.5
treemacs-text-scale nil
treemacs-user-mode-line-format nil
treemacs-user-header-line-format nil
treemacs-wide-toggle-width 70
treemacs-width 35
treemacs-width-increment 1
treemacs-width-is-initially-locked t
treemacs-workspace-switch-cleanup nil)
;; The default width and height of the icons is 22 pixels. If you are
;; using a Hi-DPI display, uncomment this to double the icon size.
;; (treemacs-resize-icons 44)
(treemacs-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
;;(when treemacs-python-executable
;; (treemacs-git-commit-diff-mode t))
(pcase (cons (not (null (executable-find "git")))
(not (null treemacs-python-executable)))
(`(t . t)
(treemacs-git-mode 'deferred))
(`(t . _)
(treemacs-git-mode 'simple)))
(treemacs-hide-gitignored-files-mode nil))
;; (use-package treemacs-evil
;; :after (treemacs evil))
;;
;; (use-package treemacs-icons-dired
;; :hook (dired-mode . treemacs-icons-dired-enable-once))
;;
;; (use-package treemacs-tab-bar ; treemacs-tab-bar if you use tab-bar-mode
;; :after (treemacs)
;; :config (treemacs-set-scope-type 'Tabs))
;;
;; (treemacs-start-on-boot)
Helpful is an alternative to the built-in Emacs help that provides much more contextual information.
To configure helpful, add the following to ~/.emacs.d/post-init.el:
;; Helpful is an alternative to the built-in Emacs help that provides much more
;; contextual information.
(use-package helpful
:commands (helpful-callable
helpful-variable
helpful-key
helpful-command
helpful-at-point
helpful-function)
:bind
([remap describe-command] . helpful-command)
([remap describe-function] . helpful-callable)
([remap describe-key] . helpful-key)
([remap describe-symbol] . helpful-symbol)
([remap describe-variable] . helpful-variable)
:init
(setq helpful-max-buffers 7))
The avy package is a navigation framework designed for jumping directly to any visible text on the screen with minimal keystrokes. The primary benefit of avy is a substantial increase in navigational efficiency, as it minimizes keystrokes compared to iterative methods like arrow keys or standard search.
It operates by generating a dynamic, temporary mapping: upon invocation, such as with the command avy-goto-char or avy-goto-char-2, the user inputs a target character, and avy highlights all visible instances on the screen with unique key sequences. Typing the short sequence corresponding to the desired location instantly moves the point directly there.
To configure avy, add the following to ~/.emacs.d/post-init.el:
(use-package avy
:commands (avy-goto-char
avy-goto-char-2
avy-next)
:init
(global-set-key (kbd "C-'") 'avy-goto-char-2))
The author recommends using avy-goto-char-2 (typically bound to C-'). Upon invocation, avy prompts the user to input a two-character sequence. Subsequently, all visible instances of this sequence are highlighted with unique, concise labels (e.g., single letters or numbers). The user then simply presses the key corresponding to the desired label, and avy instantly transports the cursor to that specific occurrence.
This bufferfile.el package provides helper functions to delete, rename, or copy buffer files:
bufferfile-rename: Renames the file visited by the current buffer, ensures that the destination directory exists, and updates the buffer name for all associated buffers, including clones/indirect buffers. It also ensures that buffer-local features referencing the file, such as Eglot or dired buffers, are correctly updated to reflect the new file name.bufferfile-delete: Delete the file associated with a buffer and kill all buffers visiting the file, including clones/indirect buffers.bufferfile-copy: Ensures that the destination directory exists and copies the file visited by the current buffer to a new file.The functions above also ensures that any modified buffers are saved prior to executing operations like renaming, deleting, or copying.
To configure bufferfile, add the following to ~/.emacs.d/post-init.el:
(use-package bufferfile
:commands (bufferfile-copy
bufferfile-rename
bufferfile-delete)
:init
;; If non-nil, display messages during file renaming operations
(setq bufferfile-verbose nil)
;; If non-nil, enable using version control (VC) when available
(setq bufferfile-use-vc nil)
;; Specifies the action taken after deleting a file and killing its buffer.
(setq bufferfile-delete-switch-to 'parent-directory))
The bufferfile package overcomes limitations in Emacs' built-in functions:
The bufferfile package resolves these issues by updating buffer names when a file is renamed and removing all related buffers, including indirect ones, when a file is deleted.
To enhance the Elisp development experience, add the following to ~/.emacs.d/post-init.el:
;; Enables automatic indentation of code while typing
(use-package aggressive-indent
:commands aggressive-indent-mode
:hook
(emacs-lisp-mode . aggressive-indent-mode))
;; Highlights function and variable definitions in Emacs Lisp mode
(use-package highlight-defined
:commands highlight-defined-mode
:hook
(emacs-lisp-mode . highlight-defined-mode))
Other optional packages that may be useful include:
;; Prevent parenthesis imbalance
(use-package paredit
:commands paredit-mode
:hook
(emacs-lisp-mode . paredit-mode)
:config
(define-key paredit-mode-map (kbd "RET") nil))
;; For paredit+Evil mode users: enhances paredit with Evil mode compatibility
;; --------------------------------------------------------------------------
;; (use-package enhanced-evil-paredit
;; :commands enhanced-evil-paredit-mode
;; :hook
;; (paredit-mode . enhanced-evil-paredit-mode))
;; Displays visible indicators for page breaks
(use-package page-break-lines
:commands (page-break-lines-mode
global-page-break-lines-mode)
:hook
(emacs-lisp-mode . page-break-lines-mode))
;; Provides functions to find references to functions, macros, variables,
;; special forms, and symbols in Emacs Lisp
(use-package elisp-refs
:commands (elisp-refs-function
elisp-refs-macro
elisp-refs-variable
elisp-refs-special
elisp-refs-symbol))
The inhibit-mouse package disables mouse input in Emacs.
This package is useful for users who want to disable the mouse to:
To configure inhibit-mouse, add the following to ~/.emacs.d/post-init.el:
;; This package is useful for users who want to disable the mouse to:
;; - Prevent accidental clicks or cursor movements that may unexpectedly change
;; the cursor position.
;; - Reinforce a keyboard-centric workflow by discouraging reliance on the mouse
;; for navigation.
(use-package inhibit-mouse
:init
(if (daemonp)
(add-hook 'server-after-make-frame-hook #'inhibit-mouse-mode)
(inhibit-mouse-mode 1)))
NOTE: inhibit-mouse-mode allows users to disable and re-enable mouse functionality, giving them the flexibility to use the mouse when needed.
Configure the tab-bar-show variable to 1 to display the tab bar exclusively when multiple tabs are open:
;; Configure the `tab-bar-show` variable to 1 to display the tab bar exclusively
;; when multiple tabs are open:
(setopt tab-bar-show 1)
The quick-sdcv.el package serves as a lightweight Emacs interface for the sdcv command-line interface, which is the console version of the StarDict dictionary application.
This package enables Emacs to function as an offline dictionary.
To enable quick-sdcv, add the following to your ~/.emacs.d/post-init.el:
(use-package quick-sdcv
:commands (quick-sdcv-search-at-point
quick-sdcv-search-input)
:init
;; When non-nil, a distinct buffer is created for each word searched.
(setq quick-sdcv-unique-buffers t)
;; Change the prefix character used before dictionary names, replacing the
;; default `-->`:
(setq quick-sdcv-dictionary-prefix-symbol "►")
;; Change the quick-sdcv dictionaries ellipsis from … to " ▼"
;; (In quick-sdcv buffers, `outline-minor-mode' is enabled by default, which
;; allows sections corresponding to individual dictionaries to be folded. The
;; ellipsis … indicates a folded section, making it easy to collapse all
;; dictionaries and expand only those of interest.)
(setq quick-sdcv-ellipsis " ▼")
;; Automatically fold all dictionary entries when performing a search.
;; You can then unfold the dictionaries you want to read.
(setq quick-sdcv-fold-on-search t))
Here are the main interactive functions:
M-x quick-sdcv-search-at-point: Searches the word around the cursor and displays the result in a buffer.M-x quick-sdcv-search-input: Searches the input word and displays the result in a buffer.Prerequisite:
sdcv package.)/usr/share/stardict/dic/, or configure the variable quick-sdcv-dictionary-data-dir in the Emacs configuration to specify an alternative dictionary path.To customize the default font, add the following expression to your ~/.emacs.d/post-init.el:
;; Set the default font to DejaVu Sans Mono with specific size and weight
(set-face-attribute 'default nil
:height 130 :weight 'normal :family "DejaVu Sans Mono")
':weight' value to control the font thickness/boldness. It must be one of the following symbols: 'ultra-heavy, 'heavy (a.k.a. 'black), 'ultra-bold (a.k.a. 'extra-bold), 'bold, 'semi-bold (a.k.a. 'demi-bold), 'medium, 'normal (a.k.a. 'regular, a.k.a. 'book), 'semi-light (a.k.a. 'demi-light), 'light, 'extra-light (a.k.a. 'ultra-light), or 'thin.:family value to specify a different font, according to your preference. You can replace it with, for example, "Iosevka Term", "Inconsolata", "JetBrains Mono", "Source Code Pro", or "Hack". (The authors preferred font family is "Iosevka Term", medium weight.)On Linux, you can display a comprehensive list of all installed font families by executing the following command:
fc-list : family | sed 's/,/\n/g' | sort -u
The persist-text-scale Emacs package provides persist-text-scale-mode, which ensures that all adjustments made with text-scale-increase and text-scale-decrease are persisted and restored across sessions. As a result, the text size in each buffer remains consistent, even after restarting Emacs.
This package also facilitates grouping buffers into categories, allowing buffers within the same category to share a consistent text scale. This ensures uniform font sizes when adjusting text scaling. By default:
This category-based behavior can be further customized by assigning a function to the persist-text-scale-buffer-category-function variable. The function determines how buffers are categorized by returning a category identifier (string) based on the buffer's context. Buffers within the same category will share the same text scale.
To configure the persist-text-scale package, add the following to your ~/.emacs.d/post-init.el:
(use-package persist-text-scale
:init
(setq text-scale-mode-step 1.07)
(persist-text-scale-mode 1))
NOTE: The vterm package requires external system dependencies, specifically cmake (>= 3.11), libtool-bin, and libvterm. Because it contains a C component, Emacs will prompt you to compile the module the first time you run it. Ensure your environment variables are correctly configured so Emacs can locate your C compiler and build tools.
The vterm package provides is an Emacs terminal emulator that provides a fully interactive shell experience within Emacs, supporting features such as color, cursor movement, and advanced terminal capabilities.
Unlike standard Emacs terminal modes, vterm utilizes the libvterm C library for high-performance emulation. This ensures accurate terminal behavior when running shell programs, text-based applications, and REPLs.
To configure the vterm package, add the following to your ~/.emacs.d/post-init.el:
;; `vterm' is an Emacs terminal emulator that provides a fully interactive shell
;; experience within Emacs, supporting features such as color, cursor movement,
;; and advanced terminal capabilities. Unlike standard Emacs terminal modes,
;; `vterm' utilizes the libvterm C library for high-performance emulation. This
;; ensures accurate terminal behavior when running shell programs, text-based
;; applications, and REPLs.
(use-package vterm
:if (bound-and-true-p module-file-suffix)
:commands (vterm
vterm-send-string
vterm-send-return
vterm-send-key
vterm-module-compile)
:preface
(when noninteractive
;; vterm unnecessarily triggers compilation of vterm-module.so upon loading.
;; This prevents that during byte-compilation (`use-package' eagerly loads
;; packages when compiling).
(advice-add #'vterm-module-compile :override #'ignore))
(defun my-vterm--setup ()
;; Hide the mode-line
(setq mode-line-format nil)
;; Inhibit early horizontal scrolling
(setq-local hscroll-margin 0)
;; Suppress prompts for terminating active processes when closing vterm
(setq-local confirm-kill-processes nil))
:init
(add-hook 'vterm-mode-hook #'my-vterm--setup)
(setq vterm-timer-delay 0.05) ; Faster vterm
(setq vterm-kill-buffer-on-exit t)
(setq vterm-max-scrollback 5000))
The vterm terminal emulator can be started with M-x vterm.
The ispell package serves as the underlying interface in Emacs for communicating with external spell checking programs. Building upon this, the flyspell package is a built-in minor mode that provides on-the-fly spell checking. It highlights misspelled words as you type and offers interactive corrections.
This article presents a configuration that sets up the Emacs spell checker: Emacs Spell Checker: Configuring Flyspell, Ispell, and Aspell to Minimize False Positives in Source Code and Prose.
While writing code, variables such as filepath or buffername in comments or docstrings are often flagged as errors, generating visual noise in your programming buffers. This article contains a specific configuration to prevent these false positives.
The Emacs server allows external programs such as emacsclient to connect to a single running instance of Emacs. This makes it possible to open files in the existing session rather than starting a new Emacs process each time.
To start the Emacs server after initialization, add the following form to your ~/.emacs.d/post-init.el:
;; The Emacs server allows external programs such as `emacsclient' to connect to
;; a single running instance of Emacs. This makes it possible to open files in
;; the existing session rather than starting a new Emacs process each time.
;;
;; Once the server is running, the `emacsclient' command can be used in the
;; terminal to open files in the active Emacs session. For example, running the
;; following command opens the file in the existing Emacs frame without blocking
;; the terminal process.
;; emacsclient -n filename.txt
;;
(use-package server
:ensure nil
:if (not (daemonp))
:preface
(defun my-server-start ()
"Start the Emacs server if no server process is currently active."
(unless (server-running-p)
(server-start)))
:init
;; Defer starting the server until after Emacs has finished initializing
(add-hook 'emacs-startup-hook #'my-server-start))
This configuration safely checks that Emacs is not running as a daemon and ensures that no existing server process is active, preventing conflicts.
Once the server is running, the emacsclient command can be used in the terminal to open files in the active Emacs session. For example, running emacsclient -n filename.txt opens the file in the existing Emacs frame without blocking the terminal process.
NOTE: The author advises against loading custom.el. Users are instead encouraged to define their configuration programmatically in files such as post-init.el. Maintaining configuration programmatically offers several advantages: it ensures reproducibility and facilitates version control. This makes it easier to understand, audit, and evolve the configuration over time.
In Emacs, customization variables modified via the UI (e.g., M-x customize) are typically stored in a separate file, commonly named custom.el. To ensure these settings are loaded during Emacs initialization, it is necessary to explicitly load this file if it exists. To accomplish this, add the following form to your ~/.emacs.d/post-init.el:
;; In Emacs, customization variables modified via the UI (e.g., M-x customize)
;; are typically stored in a separate file, commonly named 'custom.el'. To
;; ensure these settings are loaded during Emacs initialization, it is necessary
;; to explicitly load this file if it exists.
(load custom-file 'noerror 'no-message)
Read the following article from the same author: Essential Emacs Packages for Efficient Software Development and Text Editing
You can also add the following to ~/.emacs.d/post-init.el:
;;; Enable automatic insertion and management of matching pairs of characters
;;; (e.g., (), {}, "") globally using `electric-pair-mode'.
(use-package elec-pair
:ensure nil
:init
(electric-pair-mode 1))
;; Set the fringes to match the pixel height of a character. This ensures the
;; fringe is wide enough, scaling dynamically with the current font size.
(fringe-mode (frame-char-width))
;; When Delete Selection mode is enabled, typed text replaces the selection
;; if the selection is active.
(delete-selection-mode 1)
;; Display the current line and column numbers in the mode line
(setq line-number-mode t)
(setq column-number-mode t)
(setq mode-line-position-column-line-format '("%l:%C"))
;; Display of line numbers in the buffer:
(setq-default display-line-numbers-type 'relative)
(dolist (hook '(prog-mode-hook text-mode-hook conf-mode-hook))
(add-hook hook #'display-line-numbers-mode))
;; Set the maximum level of syntax highlighting for Tree-sitter modes
(setq treesit-font-lock-level 4)
(use-package which-key
:ensure nil
:init
(setq which-key-idle-delay 1.5)
(setq which-key-idle-secondary-delay 0.25)
(setq which-key-add-column-padding 1)
(setq which-key-max-description-length 40)
(which-key-mode 1))
(unless (and (eq window-system 'mac)
(bound-and-true-p mac-carbon-version-string))
;; Enables `pixel-scroll-precision-mode' on all operating systems and Emacs
;; versions, except for emacs-mac.
;;
;; Enabling `pixel-scroll-precision-mode' is unnecessary with emacs-mac, as
;; this version of Emacs natively supports smooth scrolling.
;; https://bitbucket.org/mituharu/emacs-mac/commits/65c6c96f27afa446df6f9d8eff63f9cc012cc738
(setq pixel-scroll-precision-use-momentum nil) ; Precise/smoother scrolling
(pixel-scroll-precision-mode 1))
;; Display the time in the modeline
(display-time-mode 1)
;; Paren match highlighting
(show-paren-mode 1)
;; Track changes in the window configuration, allowing undoing actions such as
;; closing windows.
(setq winner-boring-buffers '("*Completions*"
"*Minibuf-0*"
"*Minibuf-1*"
"*Minibuf-2*"
"*Minibuf-3*"
"*Minibuf-4*"
"*Compile-Log*"
"*inferior-lisp*"
"*Fuzzy Completions*"
"*Apropos*"
"*Help*"
"*cvs*"
"*Buffer List*"
"*Ibuffer*"
"*esh command on file*"))
(winner-mode 1)
(setq uniquify-buffer-name-style 'reverse)
(setq uniquify-separator "•")
(setq uniquify-after-kill-buffer-p t)
;; Window dividers separate windows visually. Window dividers are bars that can
;; be dragged with the mouse, thus allowing you to easily resize adjacent
;; windows.
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Window-Dividers.html
(window-divider-mode 1)
;; Constrain vertical cursor movement to lines within the buffer
(setq dired-movement-style 'bounded-files)
;; Dired buffers: Automatically hide file details (permissions, size,
;; modification date, etc.) and all the files in the `dired-omit-files' regular
;; expression for a cleaner display.
(add-hook 'dired-mode-hook #'dired-hide-details-mode)
;; Hide files from dired
(setq dired-omit-files (concat "\\`[.]\\'"
"\\|\\(?:\\.js\\)?\\.meta\\'"
"\\|\\.\\(?:elc\\|a\\|o\\|pyc\\|pyo\\|swp\\|class\\)\\'"
"\\|^\\.DS_Store\\'"
"\\|^\\.\\(?:svn\\|git\\)\\'"
"\\|^\\.ccls-cache\\'"
"\\|^__pycache__\\'"
"\\|^\\.project\\(?:ile\\)?\\'"
"\\|^flycheck_.*"
"\\|^flymake_.*"))
(add-hook 'dired-mode-hook #'dired-omit-mode)
;; dired: Group directories first
(with-eval-after-load 'dired
(let ((args "--group-directories-first -ahlv"))
(when (or (eq system-type 'darwin) (eq system-type 'berkeley-unix))
(if-let* ((gls (executable-find "gls")))
(setq insert-directory-program gls)
(setq args nil)))
(when args
(setq dired-listing-switches args))))
;; Enables visual indication of minibuffer recursion depth after initialization.
(minibuffer-depth-indicate-mode 1)
;; Configure Emacs to ask for confirmation before exiting
(setq confirm-kill-emacs 'y-or-n-p)
;; Enabled backups save your changes to a file intermittently
(setq make-backup-files t)
(setq vc-make-backup-files t)
(setq kept-old-versions 10)
(setq kept-new-versions 10)
;; When tooltip-mode is enabled, certain UI elements (e.g., help text,
;; mouse-hover hints) will appear as native system tooltips (pop-up windows),
;; rather than as echo area messages. This is useful in graphical Emacs sessions
;; where tooltips can appear near the cursor.
(setq tooltip-hide-delay 20) ; Time in seconds before a tooltip disappears (default: 10)
(setq tooltip-delay 0.4) ; Delay before showing a tooltip after mouse hover (default: 0.7)
(setq tooltip-short-delay 0.08) ; Delay before showing a short tooltip (Default: 0.1)
(tooltip-mode 1)
;; Keep unmodified buffers A/B/C at session end
(setq ediff-keep-variants t)
;; Automatically apply verified, safe file-local variables. This eliminates
;; confirmation prompts when loading files, while ensuring that unauthorized or
;; risky configurations are silently ignored.
(setq enable-local-variables :safe)
It is also recommended to read the following articles:
The following additional file types may be enabled to extend language support beyond the core set.
These modes are optional and can be added selectively to ~/.emacs.d/post-init.el, depending on the languages and formats commonly encountered in a given workflow.
;; Support for Git files (.gitconfig, .gitignore, .gitattributes...)
(use-package git-modes
:commands (gitattributes-mode
gitconfig-mode
gitignore-mode)
:mode (("/\\.gitignore\\'" . gitignore-mode)
("/info/exclude\\'" . gitignore-mode)
("/git/ignore\\'" . gitignore-mode)
("/\\.gitconfig\\'" . gitconfig-mode)
("/\\.git/config\\'" . gitconfig-mode)
("/modules/.*/config\\'" . gitconfig-mode)
("/git/config\\'" . gitconfig-mode)
("/\\.gitmodules\\'" . gitconfig-mode)
("/etc/gitconfig\\'" . gitconfig-mode)
("/\\.gitattributes\\'" . gitattributes-mode)
("/info/attributes\\'" . gitattributes-mode)
("/git/attributes\\'" . gitattributes-mode)))
;; Configure built-in sgml-mode to automatically enable
;; `sgml-electric-tag-pair-mode' in `html-mode' and `mhtml-mode', providing
;; automatic insertion of matching closing tags.
(use-package sgml-mode
:ensure nil
:commands (sgml-mode sgml-electric-tag-pair-mode)
:hook ((html-mode mhtml-mode) . sgml-electric-tag-pair-mode))
;; Support for YAML files.
;;
;; NOTE: Prefer the tree-sitter-based yaml-ts-mode over yaml-mode when
;; available, as it provides more accurate syntax parsing and enhanced editing
;; features.
(use-package yaml-mode
:commands yaml-mode
:mode (("\\.yaml\\'" . yaml-mode)
("\\.yml\\'" . yaml-mode)))
;; Support for Dockerfile files.
;;
;; NOTE: Prefer the tree-sitter-based dockerfile-ts-mode over dockerfile-mode
;; when available, as it provides more accurate syntax parsing and enhanced
;; editing features.
(use-package dockerfile-mode
:commands dockerfile-mode
:mode ("Dockerfile\\'" . dockerfile-mode))
;; Support for Gnuplot files
(use-package gnuplot
:commands gnuplot-mode
:mode ("\\.gp\\'" . gnuplot-mode))
;; Support for *.lua files.
;;
;; Prefer the tree-sitter-based lua-ts-mode over lua-mode when available, as it
;; provides more accurate syntax parsing and enhanced editing features.
(use-package lua-mode
:commands lua-mode
:mode ("\\.lua\\'" . lua-mode))
;; Jinja2 template support for files commonly used in configuration management
;; systems and web frameworks. This mode enables syntax highlighting and basic
;; editing facilities for templates written using the Jinja2 templating
;; language.
(use-package jinja2-mode
:commands jinja2-mode
:mode ("\\.j2\\'" . jinja2-mode))
;; CSV file support with automatic column alignment. This configuration enables
;; csv-align-mode whenever a CSV file is opened, improving readability by
;; keeping columns visually aligned according to a configurable maximum width
;; and a set of recognized field separators.
(use-package csv-mode
:commands (csv-mode
csv-align-mode
csv-guess-set-separator)
:mode ("\\.csv\\'" . csv-mode)
:hook ((csv-mode . csv-align-mode)
(csv-mode . csv-guess-set-separator))
:init
(setq csv-align-max-width 100)
(setq csv-separators '("," ";" " " "|" "\t")))
;; Support for Go
;;
;; NOTE: Prefer the tree-sitter-based go-ts-mode over go-mode
;; when available, as it provides more accurate syntax parsing and enhanced
;; editing features.
(use-package go-mode
:commands go-mode
:mode ("\\.go\\'" . go-mode))
;; Support for Rust
(use-package rust-mode
:commands rust-mode
:mode ("\\.rs\\'" . rust-mode)
:init
(setq rust-indent-offset 2))
;; Major mode for editing crontab files
(use-package crontab-mode
:commands crontab-mode
:mode ("/crontab\\(\\.X*[[:alnum:]]+\\)?\\'" . crontab-mode))
;; Major mode for editing Nginx configuration files
(use-package nginx-mode
:commands nginx-mode
:mode (("nginx\\.conf\\'" . nginx-mode)
("/nginx/.+\\.conf\\'" . nginx-mode)))
;; Major mode for HashiCorp Configuration Language (HCL) files
(use-package hcl-mode
:commands hcl-mode
:mode ("\\.hcl\\'" . hcl-mode))
;; Major mode for Nix expression language files
(use-package nix-mode
:commands nix-mode
:mode ("\\.nix\\'" . nix-mode))
;; Major mode for editing Fish shell scripts
(use-package fish-mode
:commands fish-mode
:mode ("\\.fish\\'" . fish-mode))
;; Vim configuration file support. This mode provides syntax highlighting and
;; editing support for various Vim configuration files, including vimrc, gvimrc,
;; local overrides, and project-specific configuration files.
(use-package vimrc-mode
:commands vimrc-mode
:mode ("\\.vim\\(rc\\)?\\'" . vimrc-mode))
;; Support for Jenkinsfile files
(use-package jenkinsfile-mode
:commands jenkinsfile-mode
:mode ("Jenkinsfile\\'" . jenkinsfile-mode))
;; Support for Haskell
;; (use-package haskell-mode
;; :commands haskell-mode
;; :mode ("\\.hs\\'" . haskell-mode))
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:
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.
To configure the buffer-guardian package, add the following to your ~/.emacs.d/post-init.el:
(use-package buffer-guardian
:init
;; When non-nil, include remote files in the auto-save process
(setq buffer-guardian-inhibit-saving-remote-files t)
;; When non-nil, buffers visiting nonexistent files are not saved
(setq buffer-guardian-inhibit-saving-nonexistent-files nil)
;; Save the buffer even if the window change results in the same buffer
(setq buffer-guardian-save-on-same-buffer-window-change t)
;; Non-nil to enable verbose mode to log when a buffer is automatically saved
(setq buffer-guardian-verbose nil)
;; Save all buffers after N seconds of user idle time. (Disabled by default)
;; (setq 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))
(buffer-guardian-mode 1))
NOTE: Despite its name, the evil-matchit package is fully compatible with vanilla Emacs and does not require evil-mode.
The evil-matchit package is an extensible package that allows jumping between matching syntactic pairs. It provides out-of-the-box support for a wide array of syntaxes, ranging from Python indentation blocks and Bash shell scripting constructs to Git merge conflicts. Although designed to integrate cleanly with evil-mode, the package operates entirely independently and offers native commands for standard usage. Additionally, it features an accessible Elisp API, enabling developers to quickly write custom matching rules and integrate third-party jump functions for any unsupported languages.
To configure the evil-matchit package, add the following to your ~/.emacs.d/post-init.el:
;; The `evil-matchit' package is a extensible package that allows jumping
;; between matching syntactic pairs. It provides out-of-the-box support for a
;; wide array of syntaxes, ranging from Python indentation blocks and Bash shell
;; scripting constructs to Git merge conflicts.
;;
;; Although designed to integrate cleanly with `evil-mode', the package operates
;; entirely independently and offers native commands for standard usage.
;; Additionally, it features an accessible Elisp API, enabling developers to
;; quickly write custom matching rules and integrate third-party jump functions
;; for any unsupported languages.
(use-package evil-matchit
:commands turn-on-evil-matchit-mode
:hook (prog-mode . turn-on-evil-matchit-mode)
:init
;; Setting this to t disables the advanced Python matching algorithm.
;; Set to nil if you prefer the advanced block-matching behavior in Python.
(setq evilmi-always-simple-jump t)
;; Uncomment the following if you are an `evil-mode' user:
;; (with-eval-after-load 'evil
;; (require 'evil-matchit-evil-setup))
:config
;; Because evil-matchit maps its rules to traditional major modes by default,
;; we must manually associate the new tree-sitter modes with their respective
;; matching rules until the package incorporates them natively.
(when (fboundp 'evilmi-load-plugin-rules)
(evilmi-load-plugin-rules '(cmake-ts-mode) '(cmake))
(evilmi-load-plugin-rules '(c-ts-mode c++-ts-mode) '(c simple))
(evilmi-load-plugin-rules '(bash-ts-mode) '(simple sh))
(evilmi-load-plugin-rules '(css-ts-mode) '(simple))
(evilmi-load-plugin-rules '(php-ts-mode) '(simple template html))
(evilmi-load-plugin-rules '(lua-ts-mode) '(simple script))
(evilmi-load-plugin-rules '(python-ts-mode) '(simple python))
(evilmi-load-plugin-rules '(yaml-ts-mode) '(simple yaml))))
NOTE: Using straight.el or Elpaca is optional. Emacs already has a built-in package manager.
NOTE: When using the :hook keyword with use-package, replace after-init and emacs-startup with elpaca-after-init. Similarly, when using add-hook, replace after-init-hook, emacs-startup-hook with elpaca-after-init-hook to ensure they execute only after Elpaca has activated all queued packages.
Elpaca is a modern, asynchronous package manager for Emacs designed to be a drop-in replacement for package.el and straight.el, with enhanced performance and flexibility. Unlike traditional Emacs package managers, Elpaca installs packages asynchronously, allowing Emacs to remain responsive during installation and updates.
Add to ~/.emacs.d/pre-early-init.el:
;; By default, minimal-emacs-package-initialize-and-refresh is set to t, which
;; makes minimal-emacs.d call the built-in package manager. Since Elpaca will
;; replace the package manager, there is no need to call it.
(setq minimal-emacs-package-initialize-and-refresh nil)
And add the Elpaca bootstrap code to ~/.emacs.d/pre-init.el:
;; Elpaca bootstrap
(defvar elpaca-installer-version 0.12)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-sources-directory (expand-file-name "sources/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil :depth 1 :inherit ignore
:files (:defaults "elpaca-test.el" (:exclude "extensions"))
:build (:not elpaca-activate)))
(let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(when (<= emacs-major-version 28) (require 'subr-x))
(condition-case-unless-debug err
(if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (apply #'call-process `("git" nil ,buffer t "clone"
,@(when-let* ((depth (plist-get order :depth)))
(list (format "--depth=%d" depth) "--no-single-branch"))
,(plist-get order :repo) ,repo))))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
;; Enable 'elpaca-no-symlink-mode' on Windows, as symlink creation
;; often fails without Administrator privileges or Developer Mode.
(when (eq system-type 'windows-nt)
(elpaca-no-symlink-mode 1))
;; Install use-package support
(elpaca elpaca-use-package
;; Enable use-package :ensure support for Elpaca.
(elpaca-use-package-mode))
Note: For straight.el to function correctly, replace :ensure nil with :straight nil across all use-package declarations.
Note: It is highly recommended to use elpaca instead of straight.el because elpaca operates asynchronously. Unlike Straight, which blocks the Emacs UI during package installation and updates, Elpaca processes operations in the background.
The straight.el package is a declarative package manager for Emacs that aims to replace traditional systems like package.el by providing more precise control over package installation and management. Unlike package.el, which relies on downloading pre-built packages from ELPA archives, straight.el clones packages directly from their source repositories (typically Git), enabling reproducible and fully source-controlled package configurations.
Add the straight.el bootstrap code to ~/.emacs.d/pre-init.el:
;; Straight bootstrap
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name
"straight/repos/straight.el/bootstrap.el"
(or (bound-and-true-p straight-base-dir)
user-emacs-directory)))
(bootstrap-version 7))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(setq straight-use-package-by-default t)
;; Limit Git clone depth to a single commit when using straight.el. This
;; performs shallow clones, reducing download size the cost of full
;; repository history.
;; (setq straight-vc-git-default-clone-depth 1)
setq instead of setopt?The minimal-emacs.d configuration prioritizes an optimized, fast startup. Using setopt introduces overhead due to its type checking and function execution. For the vast majority of variables, this overhead is unnecessary during the initial startup phase. Read: Emacs startup: Why setq beats setopt, customize-set-variable, and use-package :custom?
During the development of your init files, the author strongly recommends adding the following line at the very beginning of your ~/.emacs.d/pre-early-init.el file:
(setq debug-on-error t)
Enabling debug-on-error at this stage allows you to catch errors that might otherwise cause Emacs to fail silently or behave unpredictably.
It is recommended to read: Configuring Emacs scrolling for better performance and usability
By default, minimal-emacs.d sets scroll-conservatively to 20:
(setq scroll-conservatively 20) ; Default minimal-emacs.d value
This makes Emacs recenters the window when the cursor moves past scroll-conservatively lines beyond the window edge.
You can override this in your post-init.el file. Setting it to 0 forces Emacs to recenter the point aggressively, typically positioning it in the middle of the window (NOT RECOMMENDED):
(setq scroll-conservatively 0) ; NOT RECOMMENDED
Although this offers more surrounding context, it results in frequent and pronounced screen movement, which can disrupt navigation. A value of 0 is generally discouraged unless this behavior is explicitly desired.
A value of 101 minimizes screen movement and maintains point visibility with minimal adjustment:
(setq scroll-conservatively 101)
The main drawback of 101 is that Emacs will avoid recentering almost entirely, only adjusting the window just enough to keep point visible at the very top or very bottom of the screen. Point can stick to the top or bottom edge of the window, giving you very little context above or below, which can make editing harder if you want surrounding lines visible.
Related articles:
site-run-file and inhibit-default-initEmacs performs a multi-stage initialization sequence that may include system-level configuration before and after user configuration. For a minimal and fully deterministic setup, these stages can be disabled.
Emacs can load two optional system-wide files:
site-start.el: Executed before the user configuration. Controlled by the variable site-run-file.default.el: Executed after the user configuration. Controlled by the variable inhibit-default-init.Both files are typically maintained by operating systems or system administrators to provide global defaults, package path adjustments, or distribution-specific behavior.
site-start.el (Pre-Initialization Stage)By default, Emacs evaluates site-start.el early in the startup process. While useful in managed environments, this introduces two disadvantages for a minimal configuration:
load-path, or introduce behavior that differs across machines.To ensure a clean and reproducible startup, disable this stage in ~/.emacs.d/pre-early-init.el:
(setq site-run-file nil)
This guarantees that no system-level configuration executes before the user configuration.
Note: On conventional GNU/Linux distributions such as Ubuntu, Fedora, or Arch Linux, site-start.el is often optional and may only introduce distribution defaults. However, on functional systems such as NixOS or Guix System, it may be required to populate essential load-path entries. Disabling it in such environments can prevent Emacs from locating required libraries.
default.el (Post-Initialization Stage)After loading the user configuration, Emacs may evaluate default.el. This file can override user-defined settings or introduce additional global behavior.
To prevent any system configuration from executing after the user initialization, add the following to pre-early-init.el:
(setq inhibit-default-init t)
Disabling both site-run-file and default.el removes system-level interference, reduces startup variability, and establishes a fully controlled initialization environment suitable for minimal and reproducible configurations.
As your configuration grows, your post-init.el might become difficult to navigate. A standard practice in Elisp development is to split a large configuration into smaller, modular files organized by topic (e.g., UI, programming languages, Org mode).
Here is how you can organize your custom Elisp files:
Step 1: Create a directory to store your modular configuration files, for example, ~/.emacs.d/lisp/.
Step 2: Create your individual configuration files inside the ~/.emacs.d/lisp/ directory. For example, create a file named ``~/.emacs.d/lisp/my-ui-config.el`.
Step 3: At the end of my-ui-config.el, add the provide function so Emacs knows the feature has been loaded:
;;; my-ui-config.el --- UI customizations -*- no-byte-compile: t; lexical-binding: t; -*-
;; Your UI configuration goes here...
(setq display-line-numbers-type 'relative)
(provide 'my-ui-config)
Step 4: Add this new directory to your Emacs load-path. You can do this by adding the following line to the top of your ~/.emacs.d/post-init.el (or pre-init.el):
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
Step 5: Finally, load this file in your ~/.emacs.d/post-init.el using require:
(require 'my-ui-config)
This keeps your post-init.el clean and makes it much easier to maintain your setup.
By default, minimal-emacs.d is configured to prioritize packages from GNU ELPA and NonGNU ELPA repositories over MELPA, ensuring greater stability.
If you prefer to obtain the latest packages from MELPA to access new features and improvements, you can adjust the priority so that Emacs use-package retrieves the newest versions from MELPA before consulting the stable GNU and NonGNU repositories. While MELPA packages are generally regarded as less stable, actual breakages are uncommon; over the past year, only a single package (package-lint) out of 146 packages in the author's configuration experienced a brief disruption, which was quickly resolved.
Benefit:
Drawback:
To ensure that Emacs always installs or updates to the newest versions of all packages, add the following configuration to ~/.emacs.d/post-early-init.el:
;; Obtain the latest packages from MELPA to access new features and
;; improvements. While MELPA packages are generally regarded as less stable,
;; actual breakages are uncommon; over the past year, only a single package
;; (package-lint) out of 146 packages in the minimal-emacs.d author's
;; configuration experienced a brief disruption, which was quickly resolved.
(setq package-archive-priorities '(("melpa" . 90)
("gnu" . 70)
("nongnu" . 60)
("melpa-stable" . 50)))
This setup prioritizes MELPA over the stable GNU and NonGNU repositories. When multiple archives provide the same package, Emacs will choose the version from the archive with the highest priority. As a result, you will consistently receive the latest available versions from MELPA while still having access to stable GNU and NonGNU packages when MELPA does not provide them.
In the event of a package breakage, you can direct Emacs to install a package from a specific repository. For instance, to ensure that evil and evil-collection are installed from melpa-stable, add the following configuration to ~/.emacs.d/post-early-init.el:
(setq package-pinned-packages
'((evil . "melpa-stable")
(evil-collection . "melpa-stable")))
Here is a comprehensive package-pinned-packages configuration to guarantee that essential packages, such as consult or corfu, are retrieved from a stable repository, while all remaining packages are obtained from MELPA according to the `package-archive-priorities' priorities above:
(setq package-pinned-packages
'((annalist . "melpa-stable")
(ansible-doc . "melpa-stable")
(apheleia . "melpa-stable")
(basic-mode . "melpa-stable")
(consult-dir . "melpa-stable")
(corfu-prescient . "melpa-stable")
(dtrt-indent . "melpa-stable")
(dumb-jump . "melpa-stable")
(elisp-refs . "melpa-stable")
(evil-collection . "melpa-stable")
(f . "melpa-stable")
(flymake-quickdef . "melpa-stable")
(groovy-mode . "melpa-stable")
(highlight-defined . "melpa-stable")
(markdown-toc . "melpa-stable")
(org-appear . "melpa-stable")
(package-lint-flymake . "melpa-stable")
(parent-mode . "melpa-stable")
(php-mode . "melpa-stable")
(prescient . "melpa-stable")
(s . "melpa-stable")
(tocus . "melpa-stable")
(treesit-auto . "melpa-stable")
(vertico-prescient . "melpa-stable")
(visual-fill-column . "melpa-stable")
(yasnippet-snippets . "melpa-stable")
(aggressive-indent . "gnu")
(cape . "gnu")
(compat . "gnu")
(consult . "gnu")
(corfu . "gnu")
(csv-mode . "gnu")
(dash . "gnu")
(diff-hl . "gnu")
(diminish . "gnu")
(easy-escape . "gnu")
(embark . "gnu")
(embark-consult . "gnu")
(expand-region . "gnu")
(gcmh . "gnu")
(indent-bars . "gnu")
(marginalia . "gnu")
(modus-themes . "gnu")
(orderless . "gnu")
(org . "gnu")
(rainbow-mode . "gnu")
(transient . "gnu")
(vertico . "gnu")
(yasnippet . "gnu")
(ztree . "gnu")
(eat . "nongnu")
(edit-indirect . "nongnu")
(evil-visualstar . "nongnu")
(exec-path-from-shell . "nongnu")
(git-modes . "nongnu")
(golden-ratio . "nongnu")
(goto-chg . "nongnu")
(gptel . "nongnu")
(lua-mode . "nongnu")
(magit . "nongnu")
(markdown-mode . "nongnu")
(package-lint . "nongnu")
(page-break-lines . "nongnu")
(paredit . "nongnu")
(popup . "nongnu")
(rainbow-delimiters . "nongnu")
(undo-fu . "nongnu")
(undo-fu-session . "nongnu")
(wgrep . "nongnu")
(with-editor . "nongnu")
(ws-butler . "nongnu")
(yaml-mode . "nongnu")))
Note: The minimal-emacs.d author does not recommend using MELPA Stable. Use MELPA instead, which is enabled by default in the minimal-emacs.d configuration.
By default, minimal-emacs.d uses MELPA instead of MELPA Stable because MELPA Stable offers outdated packages that lack essential features. If you prefer to use MELPA Stable, you may follow the instructions below.
Here are the key differences between MELPA (the default repository used in minimal-emacs.d) and MELPA Stable:
If you prefer MELPA Stable over MELPA, you can add MELPA Stable and prioritize it. To ensure packages are fetched from MELPA Stable first, add the following configuration to ~/.emacs.d/post-early-init.el:
;; This change increases MELPA Stable priority to 70, above MELPA,
;; ensuring that MELPA is preferred for package installations
;; over MELPA Stable.
;; (Note: The minimal-emacs.d author does not assign higher priority to MELPA
;; Stable than to MELPA.)
;;
;; (setq package-archive-priorities '(("gnu" . 90)
;; ("nongnu" . 80)
;; ("melpa-stable" . 70)
;; ("melpa" . 60)))
Add the following line to the end of your post-init.el file:
(minimal-emacs-load-user-init "local.el")
This allows local.el to load, enabling custom configurations specific to the machine.
(Ensure that local.el is in the same directory as post-init.el.)
Alternatively, the target file can be loaded from a subdirectory relative to the location of post-init.el:
(minimal-emacs-load-user-init "sub-directory/local.el")
In certain Emacs configurations, specific files may be recompiled repeatedly during startup.
Compiling /snap/emacs/current/usr/share/emacs/lisp/org/org-loaddefs.el.gz...
Compiling /snap/emacs/current/usr/share/emacs/etc/themes/modus-vivendi-theme.el...
This behavior arises because Emacs performs native compilation on specific Elisp files, and in many scenarios, it is desirable to prevent compilation of files that fail during the process.
Emacs can be configured to bypass native compilation for files whose paths match a list of regular expression patterns by setting native-comp-jit-compilation-deny-list. For example:
(let ((deny-list '("\\(?:[/\\\\]\\.dir-locals\\.el\\(?:\\.gz\\)?$\\)"
"\\(?:[/\\\\]modus-vivendi-theme\\.el\\(?:\\.gz\\)?$\\)"
"\\(?:[/\\\\][^/\\\\]+-loaddefs\\.el\\(?:\\.gz\\)?$\\)"
"\\(?:[/\\\\][^/\\\\]+-autoloads\\.el\\(?:\\.gz\\)?$\\)")))
(setq native-comp-jit-compilation-deny-list deny-list)
;; Deprecated
(with-no-warnings
(setq native-comp-deferred-compilation-deny-list deny-list)
(setq comp-deferred-compilation-deny-list deny-list)))
This deny list instructs Emacs to bypass native compilation for files matching the specified patterns, preventing unnecessary or error-prone recompilation while permitting all other files to be compiled normally.
To load customizations saved by Emacs (M-x customize), add the following code snippet to the post-init.el file. This ensures that the custom file, typically set to a separate file for user preferences, is loaded without errors or messages during startup:
(when custom-file
(load custom-file 'noerror 'nomessage))
However, rather than relying on customizations loaded with the code above, the author recommends configuring Emacs through init files (just as you are doing by reading this README.md and customizing packages using use-package with the :custom keyword).
Add the following to ~/.emacs.d/pre-early-init.el to ensure that minimal-emacs.d restores the specified amount after startup:
(setq minimal-emacs-gc-cons-threshold (* 64 1024 1024))
By default, Emacs loads .dir-locals.el from the current directory or its parents and applies project-specific settings such as indentation, compilation commands, or custom minor modes. While useful in many cases, this behavior can introduce unintended overrides, inconsistencies, or even security risks when working with untrusted projects.
If you want to prevent Emacs from applying these directory-local settings, you can disable .dir-locals.el by setting enable-dir-local-variables to nil:
(setq enable-dir-local-variables nil)
Add the following to the top of the ~/.emacs.d/pre-early-init.el file to make minimal-emacs.d use the MINIMAL_EMACS_USER_DIRECTORY environment variable to change ~/.emacs.d to another directory:
;; Place this at the very beginning of pre-early-init.el
(let ((previous-minimal-emacs-user-directory (expand-file-name
minimal-emacs-user-directory))
(env-dir (getenv "MINIMAL_EMACS_USER_DIRECTORY")))
(setq minimal-emacs-user-directory (if env-dir
(expand-file-name env-dir)
(expand-file-name user-emacs-directory)))
(unless (string= minimal-emacs-user-directory
previous-minimal-emacs-user-directory)
;; Load pre-early-init.el from the new directory
(minimal-emacs-load-user-init "pre-early-init.el")))
During the execution of early-init.el (and pre-early-init.el and post-early-init.el), Emacs has not yet loaded the graphical user interface (GUI). This file is used for configurations that need to be applied before the GUI is initialized, such as settings that affect the early stages of the Emacs startup process.
Thus, post-early-init.el and pre-init.el serve different purposes and are not the same.
The menu bar is disabled by default in minimal-emacs.d to provide a minimal, distraction-free environment, which many experienced users prefer.
The menu bar can be re-enabled by adding the following configuration to ~/.emacs.d/pre-early-init.el:
(setq minimal-emacs-ui-features '(menu-bar))
Other UI features can also be enabled by adding the following to ~/.emacs.d/pre-early-init.el:
(setq minimal-emacs-ui-features '(context-menu tool-bar menu-bar dialogs tooltips))
The author began working on it after realizing that no existing starter kit offered a truly minimal setup with the flexibility for users to choose exactly what to include in their configuration.
To ensure the minimal-emacs.d configuration loads post-early-init.el, pre-init.el, and post-init.el from a different directory, such as ~/.config/minimal-emacs.d/, modify the minimal-emacs-user-directory variable by adding the following to your ~/.emacs.d/pre-early-init.el file:
(setq minimal-emacs-user-directory "~/.config/minimal-emacs.d/")
This will ensure that the minimal-emacs.d configuration loads post-early-init.el, pre-init.el, and post-init.el from ~/.config/minimal-emacs.d/.
Keep in mind that if you change the minimal-emacs-user-directory, minimal-emacs.d will attempt to load the rest of the configuration from that directory (e.g., ~/.config/minimal-emacs/post-early-init.el, ~/.config/minimal-emacs/pre-init.el and ~/.config/minimal-emacs/post-init.el, etc.).
NOTE: Running package initialization and installation during the early-init phase is NOT RECOMMENDED because this stage occurs before the GUI system, windowing, and comprehensive error-handling buffers are fully initialized. When package-install or package-refresh-contents triggers a failure-such as a TLS handshake error or a lost network connection-Emacs cannot yet render a graphical window to display the backtrace or warning. This results in a "silent" hang or a crash that provides no visual feedback to the user, forcing a pivot to a terminal to inspect standard output. Furthermore, many packages expect a fully functional frame and loaded user environment to configure themselves correctly; forcing them to load during early-init bypasses the intentional separation designed to let you set up UI-independent variables before the package system and GUI logic complicate the startup sequence.
To install and load packages during the early-init phase, add the following to post-early-init.el:
;; THIS IS NOT RECOMMENDED
;;
;; Running package initialization and installation during the early-init phase
;; is NOT RECOMMENDED because this stage occurs before the GUI system,
;; windowing, and comprehensive error-handling buffers are fully initialized.
;; When package-install or `package-refresh-contents` triggers a failure-such as
;; a TLS handshake error or a lost network connection-Emacs cannot yet render a
;; graphical window to display the backtrace or warning. This results in a
;; "silent" hang or a crash that provides no visual feedback to the user,
;; forcing a pivot to a terminal to inspect standard output. Furthermore, many
;; packages expect a fully functional frame and loaded user environment to
;; configure themselves correctly; forcing them to load during early-init
;; bypasses the intentional separation designed to let you set up UI-independent
;; variables before the package system and GUI logic complicate the startup
;; sequence.
;;
;; File: `post-early-init.el'
(setq minimal-emacs-package-initialize-and-refresh nil)
;; If you want to ignore the warning:
;; "Warning (package): Unnecessary call to package-initialize in init file."
;; Uncomment the following setq:
;; (setq warning-suppress-types '((package)))
;; Initialize packages in the early-init phase instead of init
(when (bound-and-true-p minimal-emacs-package-initialize-and-refresh)
;; Initialize and refresh package contents again if needed
(package-initialize)
(unless (package-installed-p 'use-package)
(unless (seq-empty-p package-archive-contents)
(package-refresh-contents))
(package-install 'use-package))
(require 'use-package))
;; TODO: Add your use-package packages here
If you want to maintain a strictly version-controlled, declarative configuration, you should prevent the Emacs customization interface from automatically appending custom-set-variables blocks to your files.
;; Prevent Emacs from writing custom settings to any file
(with-eval-after-load 'cus-edit
(advice-add 'custom-save-all :override #'ignore))
Optimizing Emacs startup - Guide to deferred package loading with use-package: As an Emacs user, your configuration can easily grow from a few lightweight adjustments to a massive, hundred-package IDE. Without careful management, Emacs startup time can degrade from sub-second execution to several seconds, or minutes, in the worst cases. Eager package loading is one common source of startup overhead. This guide explains how Emacs loads libraries, how use-package configures package loading, and how deferred loading can reduce startup time.
Emacs: Preventing Org-Mode formatting bleed when copy-pasting: Copying text from an Org buffer often results in unwanted colors, backgrounds, or text weights bleeding into the destination buffer. By default, vanilla Emacs preserves explicit text formatting (face properties) when copying and pasting to support rich-text environments. While standard syntax highlighting (font-lock-face) is automatically stripped, modes like org-mode rely heavily on the face property for their visual styling.
Securing and reducing prompts for Emacs .dir-locals.el and local variables: Emacs automatically applies project-specific configurations through file-local and directory-local (.dir-locals.el) variables when opening a file or directory. While this feature ensures consistent settings across environments, it can cause security risks and persistent prompt fatigue when editing source code. Malicious .dir-locals.el files or file-local variables containing eval forms can execute arbitrary Lisp code if Emacs is configured to evaluate them, or if the user approves the relevant prompt by mistake. This article outlines configurations for securing file-local and directory-local variables while reducing prompts.
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. Compiling 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. 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...
Measuring Emacs Startup Time More Accurately Than the Built-in emacs-init-time Function: As an Emacs configuration grows, startup time can gradually increase. Measuring that increase accurately makes it easier to identify regressions. However, the built-in emacs-init-time function does not measure the entire startup sequence.
The minimal-emacs.d base provides a sensible foundation for your personal configuration. It addresses common pain points in vanilla Emacs to provide a responsive and clean environment from the start, without forcing a specific workflow.
--histogram diff algorithm and automatically follow file renames in logs.dired-dwim-target for easier file operations between panes. Auto-updates Dired buffers and cleans up deleted directories silently.~/.emacs files.pre-early-init.el, post-early-init.el, pre-init.el, and post-init.el) to hook into different stages of the startup process.~/.authinfo.gpg) and directs the GPG agent to use the minibuffer for passphrase entry.The minimal-emacs.d project has been written by James Cherti and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version.
Copyright (C) 2024-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 (in the .LICENSE file).
Contribution from the minimal-emacs.d community:
Other Emacs packages by the same author:
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.Hacker News (1)
Emacs Lisp
100.0%
minimal-emacs.d - Emacs init.el and early-init.el for Better Defaults and Optimized Startup, intended to serve as a solid foundation for your vanilla Emacs configuration | Emacs Starter Kit
897
stars
1,046
commits
Emacs Lisp
primary language
Sep 10, 2026
updated
init.el and early-init.el for Better Defaults and Optimized StartupThe minimal-emacs.d project is a fast and lightweight minimal Emacs starter kit (init.el and early-init.el) that gives you full control over your configuration. It provides better defaults, an optimized startup, and a clean foundation for building your own vanilla Emacs setup.
Each setting in minimal-emacs.d is carefully chosen to answer this question: does it provide a better default that modernizes Emacs while keeping it lightweight, fast and stable?
In just a few minutes of applying what's in this README.md file, you will have a fully functional, high-performance Emacs configuration ready for work. You will bypass hours of configuration and the heavy overhead of frameworks like Doom or Spacemacs, gaining access to optimized garbage collection, sensible defaults, and a fast startup.
NOTE: If this project helps your workflow, please consider supporting it by ⭐ starring minimal-emacs.d on GitHub and sharing it on your website, blog, Mastodon, Reddit, X, LinkedIn, or other social media platforms so other Emacs users can discover its benefits.
Ready to start? Install minimal-emacs.d
Building the minimal-emacs.d init.el and early-init.el was the result of extensive research and testing to fine-tune the best parameters and optimizations for an Emacs configuration. (More information about the minimal-emacs.d features can be found here: Features.)
The minimal-emacs.d project is:
The minimal-emacs.d project includes two initialization files:
early-init.el: Loaded early in the Emacs startup process, before the graphical interface is initialized. Introduced in Emacs 27, this file configures settings that influence startup performance and GUI behavior prior to package loading.init.el: Loaded after the graphical interface is initialized. This file contains user customizations, including variable settings, package loading, mode configurations, and keybindings.Skip to: Install minimal-emacs.d
Excluding empty lines, comments, and docstrings, the minimal-emacs.d configuration is approximately 450 lines long. It does not introduce additional functionality beyond offering improved default settings. You retain full control over which packages to install and which modes to enable.
(The theme shown in the screenshot above is ef-melissa-light, which is part of the ef-themes collection available on MELPA.)
(The theme shown in the screenshot above is doom-one, which is part of the doom-themes collection available on MELPA.)
(The theme shown in the screenshot above is the tomorrow-night-deepblue-theme.el, available on MELPA.)
The author uses minimal-emacs.d as his early-init.el and init.el, alongside 146 packages (See the packages that the author is using here). Yet, thanks to its efficient design, Emacs still starts in just 0.22 seconds:

Startup speed depends on hardware and disk speed. For consistent comparisons, test on the same computer and Emacs version. While startup time is significant, factors like native compilation are also important for long-term performance.
Skip to: Install minimal-emacs.d
init.el and early-init.el files with those from the project.Please share your configuration. It could serve as inspiration for other users.
~/.emacs and ~/.emacs.el files do not exist. These files cause Emacs to ignore ~/.emacs.d/init.el. This behavior is due to the way Emacs searches for initialization files (more information). Simply delete the ~/.emacs and ~/.emacs.el files avoid this issue.emacs --debug-init to enable debug mode and obtain the backtrace.~/.emacs.dExecute the following command install this repository into ~/.emacs.d:
git clone --depth 1 https://github.com/jamescherti/minimal-emacs.d ~/.emacs.d
~/.minimal-emacs.dTo install minimal-emacs.d in a non-default directory, use the --init-directory Emacs option to specify your desired configuration path. For example, to install minimal-emacs.d in ~/.minimal-emacs.d/, follow these steps:
Clone the repository into ~/.minimal-emacs.d/ using:
git clone --depth 1 https://github.com/jamescherti/minimal-emacs.d ~/.minimal-emacs.d
Then start Emacs with the new configuration directory:
emacs --init-directory ~/.minimal-emacs.d/
To keep your Emacs configuration up to date, you can pull the latest changes from the repository. Run the following command in your terminal:
git -C ~/.emacs.d pull
The init.el and early-init.el files should never be modified directly because they are intended to be managed by Git during an update.
The minimal-emacs.d init files support additional customization files that are loaded at different stages of the Emacs startup process. These files allow you to further customize the initialization sequence:
~/.emacs.d/pre-init.el: This file is loaded before init.el. Use it to set up variables or configurations that need to be available early in the initialization process but after early-init.el.
~/.emacs.d/post-init.el: This file is loaded after init.el. It is useful for additional configurations or package setups that depend on the configurations in init.el.
~/.emacs.d/pre-early-init.el: This file is loaded before early-init.el. Use it for configurations that need to be set even earlier in the startup sequence, typically affecting the initial setup of the Emacs environment.
~/.emacs.d/post-early-init.el: This file is loaded after early-init.el but before init.el. It is useful for setting up configurations that depend on the early initialization but need to be set before the main initialization begins.
Always begin your pre-init.el, post-init.el, post-early-init.el, and pre-early-init.el files with the following header to prevent them from being byte-compiled and to activate lexical binding:
;;; FILENAME.el --- DESCRIPTION -*- no-byte-compile: t; lexical-binding: t; -*-
Replace FILENAME.el with the actual name and DESCRIPTION with a brief description of its purpose.
(Only if you know what you're doing: Removing no-byte-compile: t; from your init files allows Emacs to compile them, improving load and execution speed. However, if you do so, you may need to add required dependencies. For example, if you're using use-package, add (require 'use-package) at the top of post-init.el to ensure all necessary use-package variables and functions are loaded. The only init file where no-byte-compile: t should never be removed is early-init.el, because if this file is compiled, Emacs may load an outdated compiled version.)
Important: The examples in this README reference pre/post init files in the ~/.emacs.d/ directory, but the files pre-early-init.el, post-early-init.el, pre-init.el, and post-init.el should be placed in the same directory as init.el and early-init.el, regardless of their location.
All use-package declarations in this README use deferred loading, so you can safely copy and paste them into your configuration.
To ensure your configuration remains fast and responsive, always defer package loading so that libraries are initialized only when they are needed. The use-package macro makes this effortless; simply adding :commands or :bind to your package declarations automatically configures them for deferred loading.
Note: Enabling the tool-bar or menu-bar may slightly increase your startup time.
To customize your Emacs setup to include various user interface elements, you can use the following settings in your ~/.emacs.d/pre-early-init.el:
(setq minimal-emacs-ui-features '(context-menu tool-bar menu-bar dialogs tooltips))
These settings control the visibility of dialogs, context menus, toolbars, menu bars, and tooltips.
~/.emacs.d by redirecting files to ~/.emacs.d/var/Emacs, by default, stores various configuration files, caches, backups, and other data in the ~/.emacs.d directory. Over time, this directory can become cluttered with numerous files, making it difficult to manage and maintain.
A common solution to this issue is installing the no-littering package; however, this package is not essential.
An alternative lightweight approach is to simply change the default ~/.emacs.d directory to ~/.emacs.d/var/, which will contain all the files that Emacs typically stores in the base directory. This can be accomplished by adding the following code to ~/.emacs.d/pre-early-init.el:
;;; Reducing clutter in ~/.emacs.d by redirecting files to ~/.emacs.d/var/
;; NOTE: This must be placed in 'pre-early-init.el'.
(setq user-emacs-directory (expand-file-name "var/" minimal-emacs-user-directory))
(setq package-user-dir (expand-file-name "elpa" user-emacs-directory))
IMPORTANT: The code above should be added to ~/.emacs.d/pre-early-init.el, not the other files, as it modifies the behavior of all subsequent init files.
This README.md offers guidance on installing optional external packages. While Emacs and minimal-emacs.d are fully functional without them, the recommended packages can enhance your experience and introduce additional features, which is why they are suggested.
Native compilation enhances Emacs performance by converting Elisp code into native machine code, resulting in faster execution and improved responsiveness.
To check if native compilation is enabled, evaluate:
(native-comp-available-p)
(A non-nil result indicates that native compilation is available.)
Ensure all libraries are byte-compiled and native-compiled using compile-angel.el. To install compile-angel, add the following code to the ~/.emacs.d/post-init.el file:
;; Native compilation enhances Emacs performance by converting Elisp code into
;; native machine code, resulting in faster execution and improved
;; responsiveness.
;;
;; Ensure adding the following compile-angel code at the very beginning
;; of your `~/.emacs.d/post-init.el` file, before all other packages.
(use-package compile-angel
:demand t
:config
;; Set `compile-angel-verbose' to nil to disable compile-angel messages.
;; (When set to nil, compile-angel won't show which file is being compiled.)
(setq compile-angel-verbose t)
;; The following directive prevents compile-angel from compiling your init
;; files. If you choose to remove this push to `compile-angel-excluded-files'
;; and compile your pre/post-init files, ensure you understand the
;; implications and thoroughly test your code. For example, if you're using
;; the `use-package' macro, you'll need to explicitly add:
;; (eval-when-compile (require 'use-package))
;; at the top of your init file.
(push "/init.el" compile-angel-excluded-files)
(push "/early-init.el" compile-angel-excluded-files)
(push "/pre-init.el" compile-angel-excluded-files)
(push "/post-init.el" compile-angel-excluded-files)
(push "/pre-early-init.el" compile-angel-excluded-files)
(push "/post-early-init.el" compile-angel-excluded-files)
;; A local mode that compiles .el files whenever the user saves them.
;; (add-hook 'emacs-lisp-mode-hook #'compile-angel-on-save-local-mode)
;; A global mode that compiles .el files prior to loading them via `load' or
;; `require'. Additionally, it compiles all packages that were loaded before
;; the mode `compile-angel-on-load-mode' was activated.
(compile-angel-on-load-mode 1))
On macOS, GUI applications (launched from the Finder, Dock, or Spotlight) do not inherit the user's shell environment variables by default. This often causes errors where Emacs cannot find external tools like git, grep, pip, or language servers (LSP), even if they work perfectly in your terminal.
To fix this, add exec-path-from-shell to ~/.emacs.d/post-init.el:
(use-package exec-path-from-shell
:if (and (or (display-graphic-p) (daemonp))
(eq system-type 'darwin)) ; macOS only
:demand t
:functions exec-path-from-shell-initialize
:config
(dolist (var '("TMPDIR"
"SSH_AUTH_SOCK" "SSH_AGENT_PID"
"GPG_AGENT_INFO"
;; "FZF_DEFAULT_COMMAND" "FZF_DEFAULT_OPTS" ; fzf
;; "VIRTUAL_ENV" ; Python
;; "GOPATH" "GOROOT" "GOBIN" ; Go
;; "CARGO_HOME" "RUSTUP_HOME" ; Rust
;; "NVM_DIR" "NODE_PATH" ; Node/JS
"LANG" "LC_CTYPE"))
(add-to-list 'exec-path-from-shell-variables var))
;; Initialize
(exec-path-from-shell-initialize))
The recentf, savehist, saveplace, and auto-revert built-in packages are already configured by minimal-emacs.d. All you need to do is activate them by adding the following to ~/.emacs.d/post-init.el:
;; Auto-revert in Emacs is a feature that automatically updates the
;; contents of a buffer to reflect changes made to the underlying file
;; on disk.
(use-package autorevert
:ensure nil
:init
;; (setq auto-revert-verbose t)
(setq auto-revert-interval 3)
(setq auto-revert-remote-files nil)
(setq auto-revert-use-notify t)
(setq auto-revert-avoid-polling nil)
(global-auto-revert-mode 1))
;; Recentf is an Emacs package that maintains a list of recently
;; accessed files, making it easier to reopen files you have worked on
;; recently.
(use-package recentf
:ensure nil
:init
(setq recentf-auto-cleanup (if (daemonp) 300 'never))
(setq recentf-exclude
(list "\\.tar$" "\\.tbz2$" "\\.tbz$" "\\.tgz$" "\\.bz2$"
"\\.bz$" "\\.gz$" "\\.gzip$" "\\.xz$" "\\.zip$"
"\\.7z$" "\\.rar$"
"COMMIT_EDITMSG\\'"
"\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\|bmp\\|xpm\\)$"
"-autoloads\\.el$" "autoload\\.el$"))
;; Enable `recentf-mode'
(recentf-mode 1)
:config
;; A cleanup depth of -90 ensures that `recentf-cleanup' runs before
;; `recentf-save-list', allowing stale entries to be removed before the list
;; is saved by `recentf-save-list', which is automatically added to
;; `kill-emacs-hook' by `recentf-mode'.
(add-hook 'kill-emacs-hook #'recentf-cleanup -90))
;; savehist is an Emacs feature that preserves the minibuffer history between
;; sessions. It saves the history of inputs in the minibuffer, such as commands,
;; search strings, and other prompts, to a file. This allows users to retain
;; their minibuffer history across Emacs restarts.
(use-package savehist
:ensure nil
:init
(setq history-length 300)
(setq savehist-autosave-interval 600)
(savehist-mode 1))
;; save-place-mode enables Emacs to remember the last location within a file
;; upon reopening. This feature is particularly beneficial for resuming work at
;; the precise point where you previously left off.
(use-package saveplace
:ensure nil
:init
(setq save-place-limit 400)
(save-place-mode 1))
Enabling auto-save-mode mitigates the risk of data loss in the event of a crash. Auto-saved data can be recovered using the recover-file or recover-session functions.
To enable autosave, add the following to ~/.emacs.d/post-init.el:
;; Enable `auto-save-mode' to prevent data loss. Use `recover-file' or
;; `recover-session' to restore unsaved changes.
(setq auto-save-default t)
;; Trigger an auto-save after 300 keystrokes
(setq auto-save-interval 300)
;; Trigger an auto-save 30 seconds of idle time.
(setq auto-save-timeout 30)
Corfu enhances in-buffer completion by displaying a compact popup with current candidates, positioned either below or above the point. Candidates can be selected by navigating up or down.
Cape, or Completion At Point Extensions, extends the capabilities of in-buffer completion. It integrates with Corfu or the default completion UI, by providing additional backends through completion-at-point-functions.

To configure corfu and cape, add the following to ~/.emacs.d/post-init.el:
;; Corfu enhances in-buffer completion by displaying a compact popup with
;; current candidates, positioned either below or above the point. Candidates
;; can be selected by navigating up or down.
(use-package corfu
:init
(setq text-mode-ispell-word-completion nil)
;; Hide commands in M-x which do not apply to the current mode.
(setq read-extended-command-predicate #'command-completion-default-include-p)
;; Disable Ispell completion function. As an alternative try `cape-dict'.
(setq tab-always-indent 'complete)
(global-corfu-mode 1))
;; Cape, or Completion At Point Extensions, extends the capabilities of
;; in-buffer completion. It integrates with Corfu or the default completion UI,
;; by providing additional backends through completion-at-point-functions.
(use-package cape
:commands (cape-dabbrev cape-file cape-elisp-block)
:bind ("C-c p" . cape-prefix-map)
:init
;; Add to the global default value of `completion-at-point-functions' which is
;; used by `completion-at-point'.
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-elisp-block))
Note: Setting corfu-auto to t to enable automatic completion is discouraged for both performance and security reasons. Continuous polling of heavy completion-at-point functions (CAPFs) and external LSP servers on every keystroke introduces latency. More significantly, automatic completion introduces security vulnerabilities when working with untrusted code. This background parsing forces external binaries and language servers to process buffer contents without explicit user intent, creating attack vectors for arbitrary code execution. It is recommended to activate corfu-auto only in directories where all files are trusted.
Vertico, Consult, and Embark collectively enhance Emacs' completion and navigation capabilities.
Vertico provides a vertical completion interface, making it easier to navigate and select from completion candidates (e.g., when M-x is pressed).
Consult offers a suite of commands for efficient searching, previewing, and interacting with buffers, file contents, and more, improving various tasks.
Embark integrates with these tools to provide context-sensitive actions and quick access to commands based on the current selection, further improving user efficiency and workflow within Emacs. Together, they create a cohesive environment for managing completions and interactions.

Add the following to ~/.emacs.d/post-init.el to set up Vertico, Consult, and Embark:
;; Vertico provides a vertical completion interface, making it easier to
;; navigate and select from completion candidates (e.g., when `M-x` is pressed).
(use-package vertico
:init
;; (setq vertico-scroll-margin 0) ;; Different scroll margin
;; (setq vertico-count 20) ;; Show more candidates
;; (setq vertico-resize t) ;; Grow and shrink the Vertico minibuffer
;; (setq vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
(vertico-mode 1))
;; Vertico leverages Orderless' flexible matching capabilities, allowing users
;; to input multiple patterns separated by spaces, which Orderless then
;; matches in any order against the candidates.
(use-package orderless
:init
(setq completion-styles '(orderless basic))
(setq completion-category-overrides '((file (styles partial-completion))))
;; Emacs 31: partial-completion behaves like substring
(setq completion-pcm-leading-wildcard t))
;; Marginalia allows Embark to offer you preconfigured actions in more contexts.
;; In addition to that, Marginalia also enhances Vertico by adding rich
;; annotations to the completion candidates displayed in Vertico's interface.
(use-package marginalia
;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding
;; available in the *Completions* buffer, add it to the
;; `completion-list-mode-map'.
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
;; The :init section is always executed.
:init
;; Marginalia must be activated in the :init section of use-package such that
;; the mode gets enabled right away. Note that this forces loading the
;; package.
(marginalia-mode 1))
;; Embark integrates with Consult and Vertico to provide context-sensitive
;; actions and quick access to commands based on the current selection, further
;; improving user efficiency and workflow within Emacs. Together, they create a
;; cohesive environment for managing completions and interactions.
(use-package embark
:bind
(("C-." . embark-act) ;; pick some comfortable binding
("C-;" . embark-dwim) ;; good alternative: M-.
("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
:init
;; Optionally replace the key help with a completing-read interface
(setq prefix-help-command #'embark-prefix-help-command)
;; Show the Embark target at point via Eldoc. You may adjust the
;; Eldoc strategy, if you want to see the documentation from
;; multiple providers. Beware that using this can be a little
;; jarring since the message shown in the minibuffer can be more
;; than one line, causing the modeline to move up and down:
;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)
;; Add Embark to the mouse context menu. Also enable `context-menu-mode'.
;; (context-menu-mode 1)
;; (add-hook 'context-menu-functions #'embark-context-menu 100)
:config
;; Hide the mode line of the Embark live/completions buffers
(add-to-list 'display-buffer-alist
'("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
nil
(window-parameters (mode-line-format . none)))))
(use-package embark-consult)
;; Consult offers a suite of commands for efficient searching, previewing, and
;; interacting with buffers, file contents, and more, improving various tasks.
(use-package consult
;; Replace bindings. Lazily loaded by `use-package'.
:bind (;; C-c bindings in `mode-specific-map'
("C-c M-x" . consult-mode-command)
("C-c h" . consult-history)
("C-c k" . consult-kmacro)
("C-c m" . consult-man)
("C-c i" . consult-info)
([remap Info-search] . consult-info)
;; C-x bindings in `ctl-x-map'
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
;; Custom M-# bindings for fast register access
("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
("C-M-#" . consult-register)
;; Other custom bindings
("M-y" . consult-yank-pop) ;; orig. yank-pop
;; M-g bindings in `goto-map'
("M-g e" . consult-compile-error)
("M-g r" . consult-grep-match)
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
("M-g g" . consult-goto-line) ;; orig. goto-line
("M-g M-g" . consult-goto-line) ;; orig. goto-line
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
("M-g m" . consult-mark)
("M-g k" . consult-global-mark)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
;; M-s bindings in `search-map'
("M-s d" . consult-find) ;; Alternative: consult-fd
("M-s c" . consult-locate)
("M-s g" . consult-grep)
("M-s G" . consult-git-grep)
("M-s r" . consult-ripgrep)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)
("M-s k" . consult-keep-lines)
("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
;; Minibuffer history
:map minibuffer-local-map
("M-s" . consult-history) ;; orig. next-matching-history-element
("M-r" . consult-history)) ;; orig. previous-matching-history-element
;; The :init configuration is always executed (Not lazy)
:init
;; Tweak the register preview for `consult-register-load',
;; `consult-register-store' and the built-in commands. This improves the
;; register formatting, adds thin separator lines, register sorting and hides
;; the window mode line.
(advice-add #'register-preview :override #'consult-register-window)
(setq register-preview-delay 0.5)
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; Configure other variables and modes in the :config section,
;; after lazily loading the package.
:config
;; Optionally configure preview. The default value
;; is 'any, such that any key triggers the preview.
;; (setq consult-preview-key 'any)
;; (setq consult-preview-key "M-.")
;; (setq consult-preview-key '("S-<down>" "S-<up>"))
;; For some commands and buffer sources it is useful to configure the
;; :preview-key on a per-command basis using the `consult-customize' macro.
(consult-customize
consult-theme :preview-key '(:debounce 0.2 any)
consult-ripgrep consult-git-grep consult-grep consult-man
consult-bookmark consult-recent-file consult-xref
consult-source-bookmark consult-source-file-register
consult-source-recent-file consult-source-project-recent-file
;; :preview-key "M-."
:preview-key '(:debounce 0.4 any))
;; Optionally configure the narrowing key.
;; Both < and C-+ work reasonably well.
(setq consult-narrow-key "<") ;; "C-+"
;; Optionally make narrowing help available in the minibuffer.
;; You may want to use `embark-prefix-help-command' or which-key instead.
;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
)
The undo-fu package is a lightweight wrapper around Emacs' built-in undo system, providing more convenient undo/redo functionality while preserving access to the full undo history. The undo-fu-session package complements undo-fu by enabling the saving and restoration of undo history across Emacs sessions, even after restarting.
The default undo system in Emacs has two main issues that undo-fu fixes:
To install and configure these packages, add the following to ~/.emacs.d/post-init.el:
;; The undo-fu package is a lightweight wrapper around Emacs' built-in undo
;; system, providing more convenient undo/redo functionality.
(use-package undo-fu
:commands (undo-fu-only-undo
undo-fu-only-redo
undo-fu-only-redo-all
undo-fu-disable-checkpoint)
:init
(global-unset-key (kbd "C-z"))
(global-set-key (kbd "C-z") 'undo-fu-only-undo)
(global-set-key (kbd "C-S-z") 'undo-fu-only-redo))
;; The undo-fu-session package complements undo-fu by enabling the saving
;; and restoration of undo history across Emacs sessions, even after restarting.
(use-package undo-fu-session
:init
(undo-fu-session-global-mode 1))
For instance, to switch to a another theme than the default one, add the following to the ~/.emacs.d/post-init.el file:
(let ((inhibit-redisplay t))
;; Disable all active themes
(mapc #'disable-theme custom-enabled-themes)
;; Load the built-in theme
(load-theme 'modus-operandi t))
(If you prefer dark themes, replace modus-operandi with modus-vivendi.)
Emacs includes several built-in themes that you can use without installing additional packages:
tango-dark (Face colors using the Tango palette. Dark background.)tango (Face colors using the Tango palette. Light background.)modus-operandimodus-operandi-deuteranopiamodus-operandi-tintedmodus-operandi-tritanopiamodus-vivendimodus-vivendi-deuteranopiamodus-vivendi-tintedmodus-vivendi-tritanopiatsdh-dark (A dark theme used and created by Tassilo Horn.)tsdh-light (A light Emacs theme.)adwaita (Face colors similar to the default theme of Gnome 3 / Adwaita.)deeper-blue (Face colors using a deep blue background.)dichromacy (Face colors suitable for red/green color-blind users.)leuven-dark (Face colors with a dark background.)leuven (Face colors with a light background.)light-blue (Face colors utilizing a light blue background.)manoj-dark (Very high contrast faces with a black background.)misterioso (Predominantly blue/cyan faces on a dark cyan background.)wheatgrass (High-contrast green/blue/brown faces on a black background.)whiteboard (Face colors similar to markers on a whiteboard.)wombat (Medium-contrast faces with a dark gray background.)(To experiment with different themes, use M-x customize-themes.)
If you're interested in exploring third-party Emacs themes, consider the following:
ef-themes (available on MELPA): A collection of light and dark themes for GNU Emacs, designed to offer colorful yet highly legible options. They are aimed at users seeking something with more visual flair compared to the more minimalist modus-themes.doom-themes (available on MELPA): An extensive collection of high-quality, visually appealing themes for Emacs, designed to offer a sleek and modern aesthetic, while drawing inspiration from popular community themes.tomorrow-night-deepblue-theme (available on MELPA): A beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette. It features a deep blue background color that creates a calming atmosphere. This theme is a great choice for those who miss the blue themes that were trendy a few years ago. (The theme was inspired by classic text editors such as QuickBASIC, RHIDE, and Turbo Pascal, as well as tools such as Midnight Commander.)Configuring Vim keybindings in Emacs can greatly enhance your editing efficiency if you are accustomed to Vim's modal editing style. Add the following to ~/.emacs.d/post-init.el to set up Evil mode:
;; Uncomment the following if you are using undo-fu
;; (setq evil-undo-system 'undo-fu)
;; Vim emulation
(use-package evil
:init
;; It has to be defined before evil
(setq evil-want-integration t)
(setq evil-want-keybinding nil)
;; Make :s in visual mode operate only on the actual visual selection
;; (character or block), instead of the full lines covered by the selection
(setq evil-ex-visual-char-range t)
;; Use Vim-style regular expressions in search and substitute commands,
;; allowing features like \v (very magic), \zs, and \ze for precise matches
(setq evil-ex-search-vim-style-regexp t)
;; Enable automatic horizontal split below
(setq evil-split-window-below t)
;; Enable automatic vertical split to the right
(setq evil-vsplit-window-right t)
;; Disable echoing Evil state to avoid replacing eldoc
(setq evil-echo-state nil)
;; Do not move cursor back when exiting insert state
(setq evil-move-cursor-back nil)
;; Make `v$` exclude the final newline
(setq evil-v$-excludes-newline t)
;; Enable fine-grained undo behavior
(setq evil-want-fine-undo t)
;; Disable wrapping of search around buffer
(setq evil-search-wrap nil)
;; Allow C-h to delete in insert state
(setq evil-want-C-h-delete t)
;; Enable C-u to delete back to indentation in insert state
(setq evil-want-C-u-delete t)
;; Whether Y yanks to the end of the line
(setq evil-want-Y-yank-to-eol t)
;; Start `evil-mode'
(evil-mode 1)
:config
;; Occasionally, `evil' fails to respect `evil-search-module' when it is
;; defined inside the :custom block. This fix ensures the search module
;; is correctly set to `evil-search'.
(setq evil-search-module 'evil-search)
(evil-select-search-module 'evil-search-module 'evil-search))
(use-package evil-collection
:after evil
:init
;; It has to be defined before evil-collection
(setq evil-collection-setup-minibuffer t)
(evil-collection-init))
;; The goto-chg package is useful with Evil to jump directly to the most recent
;; edit location. This mirrors Vim's change navigation, allowing fast return to
;; where text was last modified without relying on the jump list or search.
;;
;; The goto-chg commands are bound to g; and g,
(use-package goto-chg
:commands (goto-last-change
goto-last-change-reverse))
You can also install the vim-tab-bar package to enhance the built-in Emacs tab-bar with a minimalist, Vim-inspired design that automatically adapts to the active Emacs theme. Beyond its Vim-inspired design, the vim-tab-bar package is valued by users who prioritize theme consistency, as it integrates the Emacs tab-bar with any Emacs theme, producing a visually coherent and polished interface:
;; Give Emacs tab-bar a style similar to Vim's
(use-package vim-tab-bar
:init
(vim-tab-bar-mode 1))

(The screenshot above showcases how vim-tab-bar modifies the built-in Emacs tab-bar.)
The evil-surround package simplifies handling surrounding characters, such as parentheses, brackets, quotes, etc. It provides key bindings to easily add, change, or delete these surrounding characters in pairs. For instance, you can surround the currently selected text with double quotes in visual state using S" or gS":
;; The evil-surround package simplifies handling surrounding characters, such as
;; parentheses, brackets, quotes, etc. It provides key bindings to easily add,
;; change, or delete these surrounding characters in pairs. For instance, you
;; can surround the currently selected text with double quotes in visual state
;; using S" or gS".
(use-package evil-surround
:after evil
:init
(setq evil-surround-pairs-alist
'((?\( . ("(" . ")"))
(?\[ . ("[" . "]"))
(?\{ . ("{" . "}"))
(?\) . ("(" . ")"))
(?\] . ("[" . "]"))
(?\} . ("{" . "}"))
(?< . ("<" . ">"))
(?> . ("<" . ">"))))
:config
(global-evil-surround-mode 1))
You can also add the following code to enable commenting and uncommenting by pressing gcc in normal mode and gc in visual mode (thanks you to the Reddit user u/mistakenuser for this contribution, which replaces the evil-commentary package):
;; The following code enables commenting and uncommenting by pressing gcc in
;; normal mode and gc in visual mode.
(with-eval-after-load "evil"
(evil-define-operator my-evil-comment-or-uncomment (beg end)
"Toggle comment for the region between BEG and END."
(interactive "<r>")
(comment-or-uncomment-region beg end))
(evil-define-key 'normal 'global (kbd "gc") 'my-evil-comment-or-uncomment))
The easysession package provides a comprehensive session management for Emacs. It is capable of persisting and restoring file-visiting buffers, indirect buffers (clones), buffer narrowing, Dired buffers, window configurations, the built-in tab-bar (including tabs, their buffers, and associated windows), as well as entire Emacs frames.
With easysession, your Emacs setup is restored automatically when you restart. All files, Dired buffers, and window layouts come back as they were, so you can continue working right where you left off. While editing, you can also switch to another session, switch back, rename sessions, or delete them, giving you full control over multiple work environments.
Easysession also supports extensions, enabling the restoration of Magit buffers and the scratch buffer. Custom extensions can also be created to extend its functionality.
To configure easysession, add the following to ~/.emacs.d/post-init.el:
;; The easysession Emacs package is a session manager for Emacs that can persist
;; and restore file editing buffers, indirect buffers/clones, Dired buffers,
;; windows/splits, the built-in tab-bar (including tabs, their buffers, and
;; windows), and Emacs frames. It offers a convenient and effortless way to
;; manage Emacs editing sessions and utilizes built-in Emacs functions to
;; persist and restore frames.
(use-package easysession
;; ':demand t' ensures the package is loaded immediately upon startup
:demand t
:config
;; Key mappings
(global-set-key (kbd "C-c sl") #'easysession-switch-to) ; Load session
(global-set-key (kbd "C-c ss") #'easysession-save) ; Save session
(global-set-key (kbd "C-c sL") #'easysession-switch-to-and-restore-geometry)
(global-set-key (kbd "C-c sr") #'easysession-rename)
(global-set-key (kbd "C-c sR") #'easysession-reset)
(global-set-key (kbd "C-c su") #'easysession-unload)
(global-set-key (kbd "C-c sd") #'easysession-delete)
;; Save every 10 minutes
(setq easysession-save-interval (* 10 60))
;; Save the current session when using `easysession-switch-to'
(setq easysession-switch-to-save-session t)
;; Do not exclude the current session when switching sessions
(setq easysession-switch-to-exclude-current nil)
;; Display the active session name in the mode-line lighter.
;; (setq easysession-save-mode-lighter-show-session-name t)
;; Optionally, the session name can be shown in the modeline info area:
;; (setq easysession-mode-line-misc-info t)
;; non-nil: Make `easysession-setup' load the session automatically.
;; (nil: session is not loaded automatically; the user can load it manually.)
(setq easysession-setup-load-session t)
;; The `easysession-setup' function adds hooks:
;; - To enable automatic session loading during `emacs-startup-hook', or
;; `server-after-make-frame-hook' when running in daemon mode.
;; - To save the session at regular intervals, and when Emacs exits.
(easysession-setup))
The markdown-mode package provides a major mode for Emacs 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).
To configure markdown-mode, add the following to ~/.emacs.d/post-init.el:
;; The markdown-mode package provides a major mode for Emacs 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).
(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)))
This configuration sets up markdown-mode with deferred loading to improve startup performance. The :commands and :mode keywords ensure that the mode is loaded only when needed-for example, when opening .md, .markdown, or README.md files. Files named README.md are specifically associated with gfm-mode, which is for GitHub Flavored Markdown syntax. The markdown-command variable is set to "multimarkdown" to specify the Markdown processor used for previews and exports. Additionally, a keybinding (C-c C-e) is defined in markdown-mode-map to invoke markdown-do, which can be customized to perform common Markdown-related actions.
Table of contents: To generate a table of contents when editing Markdown files, add the following to your ~/.emacs.d/post-init.el:
;; Automatically generate a table of contents when editing Markdown files
(use-package markdown-toc
:commands (markdown-toc-generate-toc
markdown-toc-generate-or-refresh-toc
markdown-toc-delete-toc
markdown-toc--toc-already-present-p)
:init
(setq markdown-toc-header-toc-title "**Table of Contents**"))
Once installed:
M-x markdown-toc-generate-tocM-x markdown-toc-generate-or-refresh-tocM-x markdown-toc-delete-tocThese commands work on any Markdown buffer and rely on properly formatted headers (e.g., #, ##) to build the table of contents.
The author also recommends reading the following article: Emacs: Automating Table of Contents Update for Markdown Documents (e.g., README.md).
NOTE: The following article provides a comprehensive guide on installing and enabling the supported folding modes: The Definitive Guide to Code Folding in Emacs.
The kirigami package provides a unified method to fold and unfold text in Emacs across a diverse set of Emacs modes.
Supported modes include: outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, gfm-mode, vdiff-mode, vdiff-3way-mode, hide-ifdef-mode, vimish-fold-mode, TeX-fold-mode (AUCTeX), fold-this-mode, origami-mode, yafolding-mode, folding-mode, ts-fold-mode, treesit-fold-mode, and hs-minor-mode (hideshow).
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 experience for opening and closing folds. The available commands include:
kirigami-open-fold: Open the fold at point.kirigami-open-fold-rec: Open the fold at point recursively.kirigami-close-fold: Close the fold at point.kirigami-open-folds: Open all folds in the buffer.kirigami-close-folds: Close all folds in the buffer.kirigami-toggle-fold: Toggle the fold at point.To configure kirigami, add the following to ~/.emacs.d/post-init.el:
(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
(("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 point
:init
(kirigami-global-mode 1))
;; 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))
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 experience for opening and closing folds.
In addition to unified interface for opening and closing folds, the kirigami package:
outline-mode, outline-minor-mode, markdown-mode, gfm-mode, and org-mode. It ensures that deep folds open reliably and permits closing folds even when the cursor is positioned within the content body. Additionally, it maintains window-start heading stability by automatically adjusting the scroll position to keep folded headings visible, preventing the context from disappearing when closing a fold that is partially scrolled off-screen.kirigami-pre-action-predicates and kirigami-post-action-functions, let external code run before and after every folding operation. The pre-action hook runs just before a fold is opened or closed and can allow or block the action. The post-action hook runs once the change is complete and can be used to update UI elements or keep external packages in sync with the new folding state.One of the modes that provide code folding is outline-minor-mode provides structured code folding in modes such as Emacs Lisp and Python, allowing users to collapse and expand sections based on headings or indentation levels. This feature enhances navigation and improves the management of large files with hierarchical structures.
Alternatively, hs-minor-mode offers basic code folding for blocks defined by curly braces, functions, or other language-specific delimiters. However, for more flexible folding that supports multiple nested levels, outline-minor-mode is generally the preferred choice, as it enables finer control over section visibility in deeply structured code.
For example, to enable outline-minor-mode:
;; The built-in outline-minor-mode provides structured code folding in modes
;; such as Emacs Lisp and Python, allowing users to collapse and expand sections
;; based on headings or indentation levels. This feature enhances navigation and
;; improves the management of large files with hierarchical structures.
(use-package outline
:ensure nil
:commands outline-minor-mode
:hook
(;; Use " ▼" instead of the default ellipsis "..." for folded text to make
;; folds more visually distinctive and readable.
(outline-minor-mode
.
(lambda()
(let* ((display-table (or buffer-display-table (make-display-table)))
(face-offset (* (face-id 'shadow) (ash 1 22)))
(value (vconcat (mapcar (lambda (c) (+ face-offset c)) " ▼"))))
(set-display-table-slot display-table 'selective-display value)
(setq buffer-display-table display-table))))))
;; Enable the mode
(add-hook 'emacs-lisp-mode-hook #'outline-minor-mode)
(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)
To enable hs-minor-mode, which is ideal for C-style languages and others that use braces {}:
(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 'sh-mode-hook #'hs-minor-mode)
(add-hook 'html-mode-hook #'hs-minor-mode)
For folding based on indentation levels, the outline-indent Emacs package provides a minor mode that enables folding according to the indentation structure:
;; The outline-indent Emacs package provides a minor mode that enables code
;; folding based on indentation levels.
;; In addition to code folding, outline-indent allows:
;; - Moving indented blocks up and down
;; - Indenting/unindenting to adjust indentation levels
;; - Inserting a new line with the same indentation level as the current line
;; - Move backward/forward to the indentation level of the current line
;; - and other features.
(use-package outline-indent
:commands outline-indent-minor-mode
:init
(setq 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)

It is also recommended to install treesit-fold, which 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. This allows for faster and more precise folding behavior that respects the grammar of the programming language, ensuring that fold boundaries are always syntactically correct even in complex or nested code structures.
;; 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. This allows for faster and more
;; precise folding behavior that respects the grammar of the programming
;; language, ensuring that fold boundaries are always syntactically correct even
;; in complex or nested code structures.
(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)
:init
(setq treesit-fold-line-count-show t)
(setq treesit-fold-line-count-format " ▼")
:config
(set-face-attribute 'treesit-fold-replacement-face nil
:foreground "#808080"
:box nil
:weight 'bold))
;; A few examples
(add-hook 'c-ts-mode-hook #'treesit-fold-mode)
(add-hook 'c++-ts-mode-hook #'treesit-fold-mode)
(add-hook 'php-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 'bash-ts-mode-hook #'treesit-fold-mode)
Apheleia is an Emacs package designed to run code formatters asynchronously without disrupting the cursor position. Code formatters like Shfmt, Black and Prettier ensure consistency and improve collaboration by automating formatting, but running them on save can introduce latency (e.g., Black takes around 200ms on an empty file) and unpredictably move the cursor when modifying nearby text.
Apheleia solves both problems across all languages, replacing language-specific packages like Blacken and prettier-js. It does this by invoking formatters in an after-save-hook, ensuring changes are applied only if the buffer remains unmodified.
To maintain cursor stability, Apheleia generates an RCS patch, applies it selectively, and employs a dynamic programming algorithm to reposition the cursor if necessary. If the formatting alters the vertical position of the cursor in the window, Apheleia adjusts the scroll position to preserve visual continuity across all displayed instances of the buffer. This allows enjoying automated code formatting without sacrificing editor responsiveness or usability.
To configure apheleia, add the following to ~/.emacs.d/post-init.el:
;; Apheleia is an Emacs package designed to run code formatters (e.g., Shfmt,
;; Black and Prettier) asynchronously without disrupting the cursor position.
(use-package apheleia
:commands (apheleia-mode
apheleia-global-mode)
:hook ((prog-mode . apheleia-mode)))
The dumb-jump package provides context-aware 'go to definition' functionality for 50+ programming languages without requiring a language server. It works by using simple heuristics and regular expression searches to locate the definitions of functions, variables, and symbols across project files.
Unlike more sophisticated language-aware tools (e.g., eglot or lsp-mode), dumb-jump' does not parse code semantically, which makes it lightweight and fast, but sometimes less precise. It integrates with popular navigation packages like xref', allowing users to jump to definitions or references.
To configure dumb-jump, add the following to ~/.emacs.d/post-init.el:
(use-package dumb-jump
:commands dumb-jump-xref-activate
:init
;; Register `dumb-jump' as an xref backend so it integrates with
;; `xref-find-definitions'. A priority of 80 ensures it is used only when no
;; more specific backend is available.
(with-eval-after-load 'xref
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate 80))
(setq dumb-jump-aggressive nil)
;; (setq dumb-jump-quiet t)
;; Number of seconds a rg/grep/find command can take before being warned to
;; use ag and config.
(setq dumb-jump-max-find-time 3)
;; Use `completing-read' so that selection of jump targets integrates with the
;; active completion framework (e.g., Vertico, Ivy, Helm, Icomplete),
;; providing a consistent minibuffer-based interface whenever multiple
;; definitions are found.
(setq dumb-jump-selector 'completing-read)
:config
;; If ripgrep is available, force `dumb-jump' to use it because it is
;; significantly faster and more accurate than the default searchers (grep,
;; ag, etc.).
(when (executable-find "rg")
(setq dumb-jump-force-searcher 'rg)
(setq dumb-jump-prefer-searcher 'rg)))
The yasnippet package provides a template system that enhances text editing by enabling users to define and use snippets, which are predefined templates of code or text. The user triggers snippet expansion by pressing the Tab key after typing an abbreviation, such as if. Upon pressing Tab, YASnippet replaces the abbreviation with the corresponding full template, allowing the user to fill in placeholders or fields within the expanded snippet.
The yasnippet-snippets package with a comprehensive collection of bundled templates for numerous programming and markup languages, including C, C++, C#, Perl, Python, Ruby, SQL, LaTeX, HTML, CSS...
(NOTE: Users of UltiSnips, a popular snippet engine for Vim, can export their snippets to YASnippet format using the tool ultyas)
;; The official collection of snippets for yasnippet.
(use-package yasnippet-snippets)
;; YASnippet is a template system designed that enhances text editing by
;; enabling users to define and use snippets. When a user types a short
;; abbreviation, YASnippet automatically expands it into a full template, which
;; can include placeholders, fields, and dynamic content.
(use-package yasnippet
:after yasnippet-snippets
:init
(setq yas-also-auto-indent-first-line t) ; Indent first line of snippet
(setq yas-also-indent-empty-lines t)
(setq yas-snippet-revival nil) ; Setting this to t causes issues with undo
(setq yas-wrap-around-region nil) ; Do not wrap region when expanding snippets
(setq yas-indent-line 'fixed) ; Do not auto-indent snippet content
;; (setq yas-triggers-in-field nil) ; Disable nested snippet expansion
;; (setq yas-prompt-functions '(yas-no-prompt)) ; No prompt for snippet choices
;; Suppress verbose messages
(setq yas-verbosity 0)
(yas-global-mode 1))
Trailing whitespace refers to any spaces or tabs that appear after the last non-whitespace character on a line. These characters have no semantic value and can lead to unnecessary diffs in version control, inconsistent formatting, or visual clutter. Removing them improves code clarity and consistency.
The stripspace Emacs package provides stripspace-local-mode, a minor mode that automatically removes trailing whitespace and blank lines at the end of the buffer when saving.
To enable stripspace and automatically delete trailing whitespace, add the following configuration to ~/.emacs.d/post-init.el:
;; The stripspace Emacs package provides stripspace-local-mode, a minor mode
;; that automatically removes trailing whitespace and blank lines at the end of
;; the buffer when saving.
(use-package stripspace
:commands stripspace-local-mode
;; Enable for prog-mode-hook, text-mode-hook, conf-mode-hook
:hook ((prog-mode . stripspace-local-mode)
(text-mode . stripspace-local-mode)
(conf-mode . stripspace-local-mode))
:init
;; The `stripspace-only-if-initially-clean' option:
;; - nil to always delete trailing whitespace.
;; - Non-nil to only delete whitespace when the buffer is clean initially.
;; (The initial cleanliness check is performed when `stripspace-local-mode'
;; is enabled.)
(setq stripspace-only-if-initially-clean nil)
;; Enabling `stripspace-restore-column' preserves the cursor's column position
;; even after stripping spaces. This is useful in scenarios where you add
;; extra spaces and then save the file. Although the spaces are removed in the
;; saved file, the cursor remains in the same position, ensuring a consistent
;; editing experience without affecting cursor placement.
(setq stripspace-restore-column t))
The diff-hl package highlights uncommitted changes in the window margin, enabling navigation between them. Also known as source control gutter indicators, it displays added, modified, and deleted lines in real time. In Git-controlled buffers, changes can be staged and unstaged directly, providing a clear view of version-control changes without running git diff. By default, the module does not start diff-hl-mode automatically.

To configure the diff-hl package, add the following to your ~/.emacs.d/post-init.el:
(use-package diff-hl
:commands (diff-hl-mode
global-diff-hl-mode)
:hook (prog-mode . diff-hl-mode)
:init
(setq diff-hl-flydiff-delay 0.4) ; Faster
(setq diff-hl-show-staged-changes nil) ; Realtime feedback
(setq diff-hl-update-async t) ; Do not block Emacs
(setq diff-hl-global-modes '(not pdf-view-mode image-mode)))
Org mode is a major mode designed for organizing notes, planning, task management, and authoring documents using plain text with a simple and expressive markup syntax. It supports hierarchical outlines, TODO lists, scheduling, deadlines, time tracking, and exporting to multiple formats including HTML, LaTeX, PDF, and Markdown.
To configure org-mode, add the following to ~/.emacs.d/post-init.el:
;; Org mode is a major mode designed for organizing notes, planning, task
;; management, and authoring documents using plain text with a simple and
;; expressive markup syntax. It supports hierarchical outlines, TODO lists,
;; scheduling, deadlines, time tracking, and exporting to multiple formats
;; including HTML, LaTeX, PDF, and Markdown.
(use-package org
:commands (org-mode org-version)
:mode
("\\.org\\'" . org-mode)
:init
(setq org-hide-leading-stars t)
(setq org-startup-indented t)
(setq org-adapt-indentation nil)
(setq org-edit-src-content-indentation 0)
;; (setq org-fontify-done-headline t)
;; (setq org-fontify-todo-headline t)
;; (setq org-fontify-whole-heading-line t)
;; (setq org-fontify-quote-and-verse-blocks t)
(setq org-startup-truncated t))
The org-appear package temporarily reveals normally hidden elements (such as emphasis markers, links, or entities) when the cursor enters them, and hides them again when the cursor leaves. To configure org-appear, add the following to ~/.emacs.d/post-init.el:
(use-package org-appear
:commands org-appear-mode
:hook (org-mode . org-appear-mode))
To set up Language Server Protocol (LSP) servers using Eglot, you can configure it, add the following to ~/.emacs.d/post-init.el:
;; Set up the Language Server Protocol (LSP) servers using Eglot.
(use-package eglot
:ensure nil
:commands (eglot-ensure
eglot-rename
eglot-format-buffer))
Here is an example of how to configure Eglot to enable or disable certain options for the pylsp server in Python development. (Note that a third-party tool, python-lsp-server, must be installed):
;; Configure Eglot to enable or disable certain options for the pylsp server
;; in Python development. (Note that a third-party tool,
;; https://github.com/python-lsp/python-lsp-server, must be installed),
(add-hook 'python-mode-hook #'eglot-ensure)
(add-hook 'python-ts-mode-hook #'eglot-ensure)
(setq-default eglot-workspace-configuration
`(:pylsp (:plugins
(;; Fix imports and syntax using `eglot-format-buffer`
:isort (:enabled t)
:autopep8 (:enabled t)
;; Syntax checkers (works with Flymake)
:pylint (:enabled t)
:pycodestyle (:enabled t)
:flake8 (:enabled t)
:pyflakes (:enabled t)
:pydocstyle (:enabled t)
:mccabe (:enabled t)
:yapf (:enabled :json-false)
:rope_autoimport (:enabled :json-false)))))
Related articles:
The buffer-terminator Emacs package automatically and safely kills buffers, ensuring a clean and efficient workspace while enhancing the performance of Emacs by reducing open buffers, which minimizes active modes, timers, processes...
Beyond performance, buffer-terminator provides other benefits. For instance, if you occasionally need to close annoying or unused buffers, buffer-terminator can handle this automatically, eliminating the need for manual intervention. (The default configuration is suitable for most users. However, the buffer-terminator package is highly customizable. You can define specific rules for retaining or terminating buffers by modifying the buffer-terminator-rules-alist with your preferred set of rules.)
To configure buffer-terminator, add the following to ~/.emacs.d/post-init.el:
(use-package buffer-terminator
:init
;; Enable/Disable verbose mode to log buffer cleanup events
(setq buffer-terminator-verbose nil)
;; Set the inactivity timeout (in seconds) after which buffers are considered
;; inactive (default is 30 minutes):
(setq buffer-terminator-inactivity-timeout (* 30 60)) ; 30 minutes
;; Define how frequently the cleanup process should run (default is every 10
;; minutes):
(setq buffer-terminator-interval (* 10 60)) ; 10 minutes
(buffer-terminator-mode 1))
(By default, buffer-terminator automatically determines which buffers are safe to terminate. However, if you need to define specific rules for keeping or terminating certain buffers, you can configure them using buffer-terminator-rules-alist.)
The treemacs package is a file and project explorer for Emacs that provides a visually structured tree layout similar to file browsers in modern IDEs. It integrates well with various Emacs packages such as projectile, lsp-mode, and magit, allowing users to navigate their project structure efficiently.

To configure treemacs, add the following to ~/.emacs.d/post-init.el:
;; A file and project explorer for Emacs that displays a structured tree
;; layout, similar to file browsers in modern IDEs. It functions as a sidebar
;; in the left window, providing a persistent view of files, projects, and
;; other elements.
(use-package treemacs
:commands (treemacs
treemacs-select-window
treemacs-delete-other-windows
treemacs-select-directory
treemacs-bookmark
treemacs-find-file
treemacs-find-tag)
:bind
(:map global-map
("M-0" . treemacs-select-window)
("C-x t 1" . treemacs-delete-other-windows)
("C-x t t" . treemacs)
("C-x t d" . treemacs-select-directory)
("C-x t B" . treemacs-bookmark)
("C-x t C-t" . treemacs-find-file)
("C-x t M-t" . treemacs-find-tag))
:init
(with-eval-after-load 'winum
(define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
:config
(setq treemacs-collapse-dirs (if treemacs-python-executable 3 0)
treemacs-deferred-git-apply-delay 0.5
treemacs-directory-name-transformer #'identity
treemacs-display-in-side-window t
treemacs-eldoc-display 'simple
treemacs-file-event-delay 2000
treemacs-file-extension-regex treemacs-last-period-regex-value
treemacs-file-follow-delay 0.2
treemacs-file-name-transformer #'identity
treemacs-follow-after-init t
treemacs-expand-after-init t
treemacs-find-workspace-method 'find-for-file-or-pick-first
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-header-scroll-indicators '(nil . "^^^^^^")
treemacs-hide-dot-git-directory t
treemacs-indentation 2
treemacs-indentation-string " "
treemacs-is-never-other-window nil
treemacs-max-git-entries 5000
treemacs-missing-project-action 'ask
treemacs-move-files-by-mouse-dragging t
treemacs-move-forward-on-expand nil
treemacs-no-png-images nil
treemacs-no-delete-other-windows t
treemacs-project-follow-cleanup nil
treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
treemacs-position 'left
treemacs-read-string-input 'from-child-frame
treemacs-recenter-distance 0.1
treemacs-recenter-after-file-follow nil
treemacs-recenter-after-tag-follow nil
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-litter-directories '("/node_modules" "/.venv" "/.cask")
treemacs-project-follow-into-home nil
treemacs-show-cursor nil
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-asc
treemacs-select-when-already-in-treemacs 'move-back
treemacs-space-between-root-nodes t
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 1.5
treemacs-text-scale nil
treemacs-user-mode-line-format nil
treemacs-user-header-line-format nil
treemacs-wide-toggle-width 70
treemacs-width 35
treemacs-width-increment 1
treemacs-width-is-initially-locked t
treemacs-workspace-switch-cleanup nil)
;; The default width and height of the icons is 22 pixels. If you are
;; using a Hi-DPI display, uncomment this to double the icon size.
;; (treemacs-resize-icons 44)
(treemacs-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
;;(when treemacs-python-executable
;; (treemacs-git-commit-diff-mode t))
(pcase (cons (not (null (executable-find "git")))
(not (null treemacs-python-executable)))
(`(t . t)
(treemacs-git-mode 'deferred))
(`(t . _)
(treemacs-git-mode 'simple)))
(treemacs-hide-gitignored-files-mode nil))
;; (use-package treemacs-evil
;; :after (treemacs evil))
;;
;; (use-package treemacs-icons-dired
;; :hook (dired-mode . treemacs-icons-dired-enable-once))
;;
;; (use-package treemacs-tab-bar ; treemacs-tab-bar if you use tab-bar-mode
;; :after (treemacs)
;; :config (treemacs-set-scope-type 'Tabs))
;;
;; (treemacs-start-on-boot)
Helpful is an alternative to the built-in Emacs help that provides much more contextual information.
To configure helpful, add the following to ~/.emacs.d/post-init.el:
;; Helpful is an alternative to the built-in Emacs help that provides much more
;; contextual information.
(use-package helpful
:commands (helpful-callable
helpful-variable
helpful-key
helpful-command
helpful-at-point
helpful-function)
:bind
([remap describe-command] . helpful-command)
([remap describe-function] . helpful-callable)
([remap describe-key] . helpful-key)
([remap describe-symbol] . helpful-symbol)
([remap describe-variable] . helpful-variable)
:init
(setq helpful-max-buffers 7))
The avy package is a navigation framework designed for jumping directly to any visible text on the screen with minimal keystrokes. The primary benefit of avy is a substantial increase in navigational efficiency, as it minimizes keystrokes compared to iterative methods like arrow keys or standard search.
It operates by generating a dynamic, temporary mapping: upon invocation, such as with the command avy-goto-char or avy-goto-char-2, the user inputs a target character, and avy highlights all visible instances on the screen with unique key sequences. Typing the short sequence corresponding to the desired location instantly moves the point directly there.
To configure avy, add the following to ~/.emacs.d/post-init.el:
(use-package avy
:commands (avy-goto-char
avy-goto-char-2
avy-next)
:init
(global-set-key (kbd "C-'") 'avy-goto-char-2))
The author recommends using avy-goto-char-2 (typically bound to C-'). Upon invocation, avy prompts the user to input a two-character sequence. Subsequently, all visible instances of this sequence are highlighted with unique, concise labels (e.g., single letters or numbers). The user then simply presses the key corresponding to the desired label, and avy instantly transports the cursor to that specific occurrence.
This bufferfile.el package provides helper functions to delete, rename, or copy buffer files:
bufferfile-rename: Renames the file visited by the current buffer, ensures that the destination directory exists, and updates the buffer name for all associated buffers, including clones/indirect buffers. It also ensures that buffer-local features referencing the file, such as Eglot or dired buffers, are correctly updated to reflect the new file name.bufferfile-delete: Delete the file associated with a buffer and kill all buffers visiting the file, including clones/indirect buffers.bufferfile-copy: Ensures that the destination directory exists and copies the file visited by the current buffer to a new file.The functions above also ensures that any modified buffers are saved prior to executing operations like renaming, deleting, or copying.
To configure bufferfile, add the following to ~/.emacs.d/post-init.el:
(use-package bufferfile
:commands (bufferfile-copy
bufferfile-rename
bufferfile-delete)
:init
;; If non-nil, display messages during file renaming operations
(setq bufferfile-verbose nil)
;; If non-nil, enable using version control (VC) when available
(setq bufferfile-use-vc nil)
;; Specifies the action taken after deleting a file and killing its buffer.
(setq bufferfile-delete-switch-to 'parent-directory))
The bufferfile package overcomes limitations in Emacs' built-in functions:
The bufferfile package resolves these issues by updating buffer names when a file is renamed and removing all related buffers, including indirect ones, when a file is deleted.
To enhance the Elisp development experience, add the following to ~/.emacs.d/post-init.el:
;; Enables automatic indentation of code while typing
(use-package aggressive-indent
:commands aggressive-indent-mode
:hook
(emacs-lisp-mode . aggressive-indent-mode))
;; Highlights function and variable definitions in Emacs Lisp mode
(use-package highlight-defined
:commands highlight-defined-mode
:hook
(emacs-lisp-mode . highlight-defined-mode))
Other optional packages that may be useful include:
;; Prevent parenthesis imbalance
(use-package paredit
:commands paredit-mode
:hook
(emacs-lisp-mode . paredit-mode)
:config
(define-key paredit-mode-map (kbd "RET") nil))
;; For paredit+Evil mode users: enhances paredit with Evil mode compatibility
;; --------------------------------------------------------------------------
;; (use-package enhanced-evil-paredit
;; :commands enhanced-evil-paredit-mode
;; :hook
;; (paredit-mode . enhanced-evil-paredit-mode))
;; Displays visible indicators for page breaks
(use-package page-break-lines
:commands (page-break-lines-mode
global-page-break-lines-mode)
:hook
(emacs-lisp-mode . page-break-lines-mode))
;; Provides functions to find references to functions, macros, variables,
;; special forms, and symbols in Emacs Lisp
(use-package elisp-refs
:commands (elisp-refs-function
elisp-refs-macro
elisp-refs-variable
elisp-refs-special
elisp-refs-symbol))
The inhibit-mouse package disables mouse input in Emacs.
This package is useful for users who want to disable the mouse to:
To configure inhibit-mouse, add the following to ~/.emacs.d/post-init.el:
;; This package is useful for users who want to disable the mouse to:
;; - Prevent accidental clicks or cursor movements that may unexpectedly change
;; the cursor position.
;; - Reinforce a keyboard-centric workflow by discouraging reliance on the mouse
;; for navigation.
(use-package inhibit-mouse
:init
(if (daemonp)
(add-hook 'server-after-make-frame-hook #'inhibit-mouse-mode)
(inhibit-mouse-mode 1)))
NOTE: inhibit-mouse-mode allows users to disable and re-enable mouse functionality, giving them the flexibility to use the mouse when needed.
Configure the tab-bar-show variable to 1 to display the tab bar exclusively when multiple tabs are open:
;; Configure the `tab-bar-show` variable to 1 to display the tab bar exclusively
;; when multiple tabs are open:
(setopt tab-bar-show 1)
The quick-sdcv.el package serves as a lightweight Emacs interface for the sdcv command-line interface, which is the console version of the StarDict dictionary application.
This package enables Emacs to function as an offline dictionary.
To enable quick-sdcv, add the following to your ~/.emacs.d/post-init.el:
(use-package quick-sdcv
:commands (quick-sdcv-search-at-point
quick-sdcv-search-input)
:init
;; When non-nil, a distinct buffer is created for each word searched.
(setq quick-sdcv-unique-buffers t)
;; Change the prefix character used before dictionary names, replacing the
;; default `-->`:
(setq quick-sdcv-dictionary-prefix-symbol "►")
;; Change the quick-sdcv dictionaries ellipsis from … to " ▼"
;; (In quick-sdcv buffers, `outline-minor-mode' is enabled by default, which
;; allows sections corresponding to individual dictionaries to be folded. The
;; ellipsis … indicates a folded section, making it easy to collapse all
;; dictionaries and expand only those of interest.)
(setq quick-sdcv-ellipsis " ▼")
;; Automatically fold all dictionary entries when performing a search.
;; You can then unfold the dictionaries you want to read.
(setq quick-sdcv-fold-on-search t))
Here are the main interactive functions:
M-x quick-sdcv-search-at-point: Searches the word around the cursor and displays the result in a buffer.M-x quick-sdcv-search-input: Searches the input word and displays the result in a buffer.Prerequisite:
sdcv package.)/usr/share/stardict/dic/, or configure the variable quick-sdcv-dictionary-data-dir in the Emacs configuration to specify an alternative dictionary path.To customize the default font, add the following expression to your ~/.emacs.d/post-init.el:
;; Set the default font to DejaVu Sans Mono with specific size and weight
(set-face-attribute 'default nil
:height 130 :weight 'normal :family "DejaVu Sans Mono")
':weight' value to control the font thickness/boldness. It must be one of the following symbols: 'ultra-heavy, 'heavy (a.k.a. 'black), 'ultra-bold (a.k.a. 'extra-bold), 'bold, 'semi-bold (a.k.a. 'demi-bold), 'medium, 'normal (a.k.a. 'regular, a.k.a. 'book), 'semi-light (a.k.a. 'demi-light), 'light, 'extra-light (a.k.a. 'ultra-light), or 'thin.:family value to specify a different font, according to your preference. You can replace it with, for example, "Iosevka Term", "Inconsolata", "JetBrains Mono", "Source Code Pro", or "Hack". (The authors preferred font family is "Iosevka Term", medium weight.)On Linux, you can display a comprehensive list of all installed font families by executing the following command:
fc-list : family | sed 's/,/\n/g' | sort -u
The persist-text-scale Emacs package provides persist-text-scale-mode, which ensures that all adjustments made with text-scale-increase and text-scale-decrease are persisted and restored across sessions. As a result, the text size in each buffer remains consistent, even after restarting Emacs.
This package also facilitates grouping buffers into categories, allowing buffers within the same category to share a consistent text scale. This ensures uniform font sizes when adjusting text scaling. By default:
This category-based behavior can be further customized by assigning a function to the persist-text-scale-buffer-category-function variable. The function determines how buffers are categorized by returning a category identifier (string) based on the buffer's context. Buffers within the same category will share the same text scale.
To configure the persist-text-scale package, add the following to your ~/.emacs.d/post-init.el:
(use-package persist-text-scale
:init
(setq text-scale-mode-step 1.07)
(persist-text-scale-mode 1))
NOTE: The vterm package requires external system dependencies, specifically cmake (>= 3.11), libtool-bin, and libvterm. Because it contains a C component, Emacs will prompt you to compile the module the first time you run it. Ensure your environment variables are correctly configured so Emacs can locate your C compiler and build tools.
The vterm package provides is an Emacs terminal emulator that provides a fully interactive shell experience within Emacs, supporting features such as color, cursor movement, and advanced terminal capabilities.
Unlike standard Emacs terminal modes, vterm utilizes the libvterm C library for high-performance emulation. This ensures accurate terminal behavior when running shell programs, text-based applications, and REPLs.
To configure the vterm package, add the following to your ~/.emacs.d/post-init.el:
;; `vterm' is an Emacs terminal emulator that provides a fully interactive shell
;; experience within Emacs, supporting features such as color, cursor movement,
;; and advanced terminal capabilities. Unlike standard Emacs terminal modes,
;; `vterm' utilizes the libvterm C library for high-performance emulation. This
;; ensures accurate terminal behavior when running shell programs, text-based
;; applications, and REPLs.
(use-package vterm
:if (bound-and-true-p module-file-suffix)
:commands (vterm
vterm-send-string
vterm-send-return
vterm-send-key
vterm-module-compile)
:preface
(when noninteractive
;; vterm unnecessarily triggers compilation of vterm-module.so upon loading.
;; This prevents that during byte-compilation (`use-package' eagerly loads
;; packages when compiling).
(advice-add #'vterm-module-compile :override #'ignore))
(defun my-vterm--setup ()
;; Hide the mode-line
(setq mode-line-format nil)
;; Inhibit early horizontal scrolling
(setq-local hscroll-margin 0)
;; Suppress prompts for terminating active processes when closing vterm
(setq-local confirm-kill-processes nil))
:init
(add-hook 'vterm-mode-hook #'my-vterm--setup)
(setq vterm-timer-delay 0.05) ; Faster vterm
(setq vterm-kill-buffer-on-exit t)
(setq vterm-max-scrollback 5000))
The vterm terminal emulator can be started with M-x vterm.
The ispell package serves as the underlying interface in Emacs for communicating with external spell checking programs. Building upon this, the flyspell package is a built-in minor mode that provides on-the-fly spell checking. It highlights misspelled words as you type and offers interactive corrections.
This article presents a configuration that sets up the Emacs spell checker: Emacs Spell Checker: Configuring Flyspell, Ispell, and Aspell to Minimize False Positives in Source Code and Prose.
While writing code, variables such as filepath or buffername in comments or docstrings are often flagged as errors, generating visual noise in your programming buffers. This article contains a specific configuration to prevent these false positives.
The Emacs server allows external programs such as emacsclient to connect to a single running instance of Emacs. This makes it possible to open files in the existing session rather than starting a new Emacs process each time.
To start the Emacs server after initialization, add the following form to your ~/.emacs.d/post-init.el:
;; The Emacs server allows external programs such as `emacsclient' to connect to
;; a single running instance of Emacs. This makes it possible to open files in
;; the existing session rather than starting a new Emacs process each time.
;;
;; Once the server is running, the `emacsclient' command can be used in the
;; terminal to open files in the active Emacs session. For example, running the
;; following command opens the file in the existing Emacs frame without blocking
;; the terminal process.
;; emacsclient -n filename.txt
;;
(use-package server
:ensure nil
:if (not (daemonp))
:preface
(defun my-server-start ()
"Start the Emacs server if no server process is currently active."
(unless (server-running-p)
(server-start)))
:init
;; Defer starting the server until after Emacs has finished initializing
(add-hook 'emacs-startup-hook #'my-server-start))
This configuration safely checks that Emacs is not running as a daemon and ensures that no existing server process is active, preventing conflicts.
Once the server is running, the emacsclient command can be used in the terminal to open files in the active Emacs session. For example, running emacsclient -n filename.txt opens the file in the existing Emacs frame without blocking the terminal process.
NOTE: The author advises against loading custom.el. Users are instead encouraged to define their configuration programmatically in files such as post-init.el. Maintaining configuration programmatically offers several advantages: it ensures reproducibility and facilitates version control. This makes it easier to understand, audit, and evolve the configuration over time.
In Emacs, customization variables modified via the UI (e.g., M-x customize) are typically stored in a separate file, commonly named custom.el. To ensure these settings are loaded during Emacs initialization, it is necessary to explicitly load this file if it exists. To accomplish this, add the following form to your ~/.emacs.d/post-init.el:
;; In Emacs, customization variables modified via the UI (e.g., M-x customize)
;; are typically stored in a separate file, commonly named 'custom.el'. To
;; ensure these settings are loaded during Emacs initialization, it is necessary
;; to explicitly load this file if it exists.
(load custom-file 'noerror 'no-message)
Read the following article from the same author: Essential Emacs Packages for Efficient Software Development and Text Editing
You can also add the following to ~/.emacs.d/post-init.el:
;;; Enable automatic insertion and management of matching pairs of characters
;;; (e.g., (), {}, "") globally using `electric-pair-mode'.
(use-package elec-pair
:ensure nil
:init
(electric-pair-mode 1))
;; Set the fringes to match the pixel height of a character. This ensures the
;; fringe is wide enough, scaling dynamically with the current font size.
(fringe-mode (frame-char-width))
;; When Delete Selection mode is enabled, typed text replaces the selection
;; if the selection is active.
(delete-selection-mode 1)
;; Display the current line and column numbers in the mode line
(setq line-number-mode t)
(setq column-number-mode t)
(setq mode-line-position-column-line-format '("%l:%C"))
;; Display of line numbers in the buffer:
(setq-default display-line-numbers-type 'relative)
(dolist (hook '(prog-mode-hook text-mode-hook conf-mode-hook))
(add-hook hook #'display-line-numbers-mode))
;; Set the maximum level of syntax highlighting for Tree-sitter modes
(setq treesit-font-lock-level 4)
(use-package which-key
:ensure nil
:init
(setq which-key-idle-delay 1.5)
(setq which-key-idle-secondary-delay 0.25)
(setq which-key-add-column-padding 1)
(setq which-key-max-description-length 40)
(which-key-mode 1))
(unless (and (eq window-system 'mac)
(bound-and-true-p mac-carbon-version-string))
;; Enables `pixel-scroll-precision-mode' on all operating systems and Emacs
;; versions, except for emacs-mac.
;;
;; Enabling `pixel-scroll-precision-mode' is unnecessary with emacs-mac, as
;; this version of Emacs natively supports smooth scrolling.
;; https://bitbucket.org/mituharu/emacs-mac/commits/65c6c96f27afa446df6f9d8eff63f9cc012cc738
(setq pixel-scroll-precision-use-momentum nil) ; Precise/smoother scrolling
(pixel-scroll-precision-mode 1))
;; Display the time in the modeline
(display-time-mode 1)
;; Paren match highlighting
(show-paren-mode 1)
;; Track changes in the window configuration, allowing undoing actions such as
;; closing windows.
(setq winner-boring-buffers '("*Completions*"
"*Minibuf-0*"
"*Minibuf-1*"
"*Minibuf-2*"
"*Minibuf-3*"
"*Minibuf-4*"
"*Compile-Log*"
"*inferior-lisp*"
"*Fuzzy Completions*"
"*Apropos*"
"*Help*"
"*cvs*"
"*Buffer List*"
"*Ibuffer*"
"*esh command on file*"))
(winner-mode 1)
(setq uniquify-buffer-name-style 'reverse)
(setq uniquify-separator "•")
(setq uniquify-after-kill-buffer-p t)
;; Window dividers separate windows visually. Window dividers are bars that can
;; be dragged with the mouse, thus allowing you to easily resize adjacent
;; windows.
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Window-Dividers.html
(window-divider-mode 1)
;; Constrain vertical cursor movement to lines within the buffer
(setq dired-movement-style 'bounded-files)
;; Dired buffers: Automatically hide file details (permissions, size,
;; modification date, etc.) and all the files in the `dired-omit-files' regular
;; expression for a cleaner display.
(add-hook 'dired-mode-hook #'dired-hide-details-mode)
;; Hide files from dired
(setq dired-omit-files (concat "\\`[.]\\'"
"\\|\\(?:\\.js\\)?\\.meta\\'"
"\\|\\.\\(?:elc\\|a\\|o\\|pyc\\|pyo\\|swp\\|class\\)\\'"
"\\|^\\.DS_Store\\'"
"\\|^\\.\\(?:svn\\|git\\)\\'"
"\\|^\\.ccls-cache\\'"
"\\|^__pycache__\\'"
"\\|^\\.project\\(?:ile\\)?\\'"
"\\|^flycheck_.*"
"\\|^flymake_.*"))
(add-hook 'dired-mode-hook #'dired-omit-mode)
;; dired: Group directories first
(with-eval-after-load 'dired
(let ((args "--group-directories-first -ahlv"))
(when (or (eq system-type 'darwin) (eq system-type 'berkeley-unix))
(if-let* ((gls (executable-find "gls")))
(setq insert-directory-program gls)
(setq args nil)))
(when args
(setq dired-listing-switches args))))
;; Enables visual indication of minibuffer recursion depth after initialization.
(minibuffer-depth-indicate-mode 1)
;; Configure Emacs to ask for confirmation before exiting
(setq confirm-kill-emacs 'y-or-n-p)
;; Enabled backups save your changes to a file intermittently
(setq make-backup-files t)
(setq vc-make-backup-files t)
(setq kept-old-versions 10)
(setq kept-new-versions 10)
;; When tooltip-mode is enabled, certain UI elements (e.g., help text,
;; mouse-hover hints) will appear as native system tooltips (pop-up windows),
;; rather than as echo area messages. This is useful in graphical Emacs sessions
;; where tooltips can appear near the cursor.
(setq tooltip-hide-delay 20) ; Time in seconds before a tooltip disappears (default: 10)
(setq tooltip-delay 0.4) ; Delay before showing a tooltip after mouse hover (default: 0.7)
(setq tooltip-short-delay 0.08) ; Delay before showing a short tooltip (Default: 0.1)
(tooltip-mode 1)
;; Keep unmodified buffers A/B/C at session end
(setq ediff-keep-variants t)
;; Automatically apply verified, safe file-local variables. This eliminates
;; confirmation prompts when loading files, while ensuring that unauthorized or
;; risky configurations are silently ignored.
(setq enable-local-variables :safe)
It is also recommended to read the following articles:
The following additional file types may be enabled to extend language support beyond the core set.
These modes are optional and can be added selectively to ~/.emacs.d/post-init.el, depending on the languages and formats commonly encountered in a given workflow.
;; Support for Git files (.gitconfig, .gitignore, .gitattributes...)
(use-package git-modes
:commands (gitattributes-mode
gitconfig-mode
gitignore-mode)
:mode (("/\\.gitignore\\'" . gitignore-mode)
("/info/exclude\\'" . gitignore-mode)
("/git/ignore\\'" . gitignore-mode)
("/\\.gitconfig\\'" . gitconfig-mode)
("/\\.git/config\\'" . gitconfig-mode)
("/modules/.*/config\\'" . gitconfig-mode)
("/git/config\\'" . gitconfig-mode)
("/\\.gitmodules\\'" . gitconfig-mode)
("/etc/gitconfig\\'" . gitconfig-mode)
("/\\.gitattributes\\'" . gitattributes-mode)
("/info/attributes\\'" . gitattributes-mode)
("/git/attributes\\'" . gitattributes-mode)))
;; Configure built-in sgml-mode to automatically enable
;; `sgml-electric-tag-pair-mode' in `html-mode' and `mhtml-mode', providing
;; automatic insertion of matching closing tags.
(use-package sgml-mode
:ensure nil
:commands (sgml-mode sgml-electric-tag-pair-mode)
:hook ((html-mode mhtml-mode) . sgml-electric-tag-pair-mode))
;; Support for YAML files.
;;
;; NOTE: Prefer the tree-sitter-based yaml-ts-mode over yaml-mode when
;; available, as it provides more accurate syntax parsing and enhanced editing
;; features.
(use-package yaml-mode
:commands yaml-mode
:mode (("\\.yaml\\'" . yaml-mode)
("\\.yml\\'" . yaml-mode)))
;; Support for Dockerfile files.
;;
;; NOTE: Prefer the tree-sitter-based dockerfile-ts-mode over dockerfile-mode
;; when available, as it provides more accurate syntax parsing and enhanced
;; editing features.
(use-package dockerfile-mode
:commands dockerfile-mode
:mode ("Dockerfile\\'" . dockerfile-mode))
;; Support for Gnuplot files
(use-package gnuplot
:commands gnuplot-mode
:mode ("\\.gp\\'" . gnuplot-mode))
;; Support for *.lua files.
;;
;; Prefer the tree-sitter-based lua-ts-mode over lua-mode when available, as it
;; provides more accurate syntax parsing and enhanced editing features.
(use-package lua-mode
:commands lua-mode
:mode ("\\.lua\\'" . lua-mode))
;; Jinja2 template support for files commonly used in configuration management
;; systems and web frameworks. This mode enables syntax highlighting and basic
;; editing facilities for templates written using the Jinja2 templating
;; language.
(use-package jinja2-mode
:commands jinja2-mode
:mode ("\\.j2\\'" . jinja2-mode))
;; CSV file support with automatic column alignment. This configuration enables
;; csv-align-mode whenever a CSV file is opened, improving readability by
;; keeping columns visually aligned according to a configurable maximum width
;; and a set of recognized field separators.
(use-package csv-mode
:commands (csv-mode
csv-align-mode
csv-guess-set-separator)
:mode ("\\.csv\\'" . csv-mode)
:hook ((csv-mode . csv-align-mode)
(csv-mode . csv-guess-set-separator))
:init
(setq csv-align-max-width 100)
(setq csv-separators '("," ";" " " "|" "\t")))
;; Support for Go
;;
;; NOTE: Prefer the tree-sitter-based go-ts-mode over go-mode
;; when available, as it provides more accurate syntax parsing and enhanced
;; editing features.
(use-package go-mode
:commands go-mode
:mode ("\\.go\\'" . go-mode))
;; Support for Rust
(use-package rust-mode
:commands rust-mode
:mode ("\\.rs\\'" . rust-mode)
:init
(setq rust-indent-offset 2))
;; Major mode for editing crontab files
(use-package crontab-mode
:commands crontab-mode
:mode ("/crontab\\(\\.X*[[:alnum:]]+\\)?\\'" . crontab-mode))
;; Major mode for editing Nginx configuration files
(use-package nginx-mode
:commands nginx-mode
:mode (("nginx\\.conf\\'" . nginx-mode)
("/nginx/.+\\.conf\\'" . nginx-mode)))
;; Major mode for HashiCorp Configuration Language (HCL) files
(use-package hcl-mode
:commands hcl-mode
:mode ("\\.hcl\\'" . hcl-mode))
;; Major mode for Nix expression language files
(use-package nix-mode
:commands nix-mode
:mode ("\\.nix\\'" . nix-mode))
;; Major mode for editing Fish shell scripts
(use-package fish-mode
:commands fish-mode
:mode ("\\.fish\\'" . fish-mode))
;; Vim configuration file support. This mode provides syntax highlighting and
;; editing support for various Vim configuration files, including vimrc, gvimrc,
;; local overrides, and project-specific configuration files.
(use-package vimrc-mode
:commands vimrc-mode
:mode ("\\.vim\\(rc\\)?\\'" . vimrc-mode))
;; Support for Jenkinsfile files
(use-package jenkinsfile-mode
:commands jenkinsfile-mode
:mode ("Jenkinsfile\\'" . jenkinsfile-mode))
;; Support for Haskell
;; (use-package haskell-mode
;; :commands haskell-mode
;; :mode ("\\.hs\\'" . haskell-mode))
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:
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.
To configure the buffer-guardian package, add the following to your ~/.emacs.d/post-init.el:
(use-package buffer-guardian
:init
;; When non-nil, include remote files in the auto-save process
(setq buffer-guardian-inhibit-saving-remote-files t)
;; When non-nil, buffers visiting nonexistent files are not saved
(setq buffer-guardian-inhibit-saving-nonexistent-files nil)
;; Save the buffer even if the window change results in the same buffer
(setq buffer-guardian-save-on-same-buffer-window-change t)
;; Non-nil to enable verbose mode to log when a buffer is automatically saved
(setq buffer-guardian-verbose nil)
;; Save all buffers after N seconds of user idle time. (Disabled by default)
;; (setq 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))
(buffer-guardian-mode 1))
NOTE: Despite its name, the evil-matchit package is fully compatible with vanilla Emacs and does not require evil-mode.
The evil-matchit package is an extensible package that allows jumping between matching syntactic pairs. It provides out-of-the-box support for a wide array of syntaxes, ranging from Python indentation blocks and Bash shell scripting constructs to Git merge conflicts. Although designed to integrate cleanly with evil-mode, the package operates entirely independently and offers native commands for standard usage. Additionally, it features an accessible Elisp API, enabling developers to quickly write custom matching rules and integrate third-party jump functions for any unsupported languages.
To configure the evil-matchit package, add the following to your ~/.emacs.d/post-init.el:
;; The `evil-matchit' package is a extensible package that allows jumping
;; between matching syntactic pairs. It provides out-of-the-box support for a
;; wide array of syntaxes, ranging from Python indentation blocks and Bash shell
;; scripting constructs to Git merge conflicts.
;;
;; Although designed to integrate cleanly with `evil-mode', the package operates
;; entirely independently and offers native commands for standard usage.
;; Additionally, it features an accessible Elisp API, enabling developers to
;; quickly write custom matching rules and integrate third-party jump functions
;; for any unsupported languages.
(use-package evil-matchit
:commands turn-on-evil-matchit-mode
:hook (prog-mode . turn-on-evil-matchit-mode)
:init
;; Setting this to t disables the advanced Python matching algorithm.
;; Set to nil if you prefer the advanced block-matching behavior in Python.
(setq evilmi-always-simple-jump t)
;; Uncomment the following if you are an `evil-mode' user:
;; (with-eval-after-load 'evil
;; (require 'evil-matchit-evil-setup))
:config
;; Because evil-matchit maps its rules to traditional major modes by default,
;; we must manually associate the new tree-sitter modes with their respective
;; matching rules until the package incorporates them natively.
(when (fboundp 'evilmi-load-plugin-rules)
(evilmi-load-plugin-rules '(cmake-ts-mode) '(cmake))
(evilmi-load-plugin-rules '(c-ts-mode c++-ts-mode) '(c simple))
(evilmi-load-plugin-rules '(bash-ts-mode) '(simple sh))
(evilmi-load-plugin-rules '(css-ts-mode) '(simple))
(evilmi-load-plugin-rules '(php-ts-mode) '(simple template html))
(evilmi-load-plugin-rules '(lua-ts-mode) '(simple script))
(evilmi-load-plugin-rules '(python-ts-mode) '(simple python))
(evilmi-load-plugin-rules '(yaml-ts-mode) '(simple yaml))))
NOTE: Using straight.el or Elpaca is optional. Emacs already has a built-in package manager.
NOTE: When using the :hook keyword with use-package, replace after-init and emacs-startup with elpaca-after-init. Similarly, when using add-hook, replace after-init-hook, emacs-startup-hook with elpaca-after-init-hook to ensure they execute only after Elpaca has activated all queued packages.
Elpaca is a modern, asynchronous package manager for Emacs designed to be a drop-in replacement for package.el and straight.el, with enhanced performance and flexibility. Unlike traditional Emacs package managers, Elpaca installs packages asynchronously, allowing Emacs to remain responsive during installation and updates.
Add to ~/.emacs.d/pre-early-init.el:
;; By default, minimal-emacs-package-initialize-and-refresh is set to t, which
;; makes minimal-emacs.d call the built-in package manager. Since Elpaca will
;; replace the package manager, there is no need to call it.
(setq minimal-emacs-package-initialize-and-refresh nil)
And add the Elpaca bootstrap code to ~/.emacs.d/pre-init.el:
;; Elpaca bootstrap
(defvar elpaca-installer-version 0.12)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-sources-directory (expand-file-name "sources/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil :depth 1 :inherit ignore
:files (:defaults "elpaca-test.el" (:exclude "extensions"))
:build (:not elpaca-activate)))
(let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(when (<= emacs-major-version 28) (require 'subr-x))
(condition-case-unless-debug err
(if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (apply #'call-process `("git" nil ,buffer t "clone"
,@(when-let* ((depth (plist-get order :depth)))
(list (format "--depth=%d" depth) "--no-single-branch"))
,(plist-get order :repo) ,repo))))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
;; Enable 'elpaca-no-symlink-mode' on Windows, as symlink creation
;; often fails without Administrator privileges or Developer Mode.
(when (eq system-type 'windows-nt)
(elpaca-no-symlink-mode 1))
;; Install use-package support
(elpaca elpaca-use-package
;; Enable use-package :ensure support for Elpaca.
(elpaca-use-package-mode))
Note: For straight.el to function correctly, replace :ensure nil with :straight nil across all use-package declarations.
Note: It is highly recommended to use elpaca instead of straight.el because elpaca operates asynchronously. Unlike Straight, which blocks the Emacs UI during package installation and updates, Elpaca processes operations in the background.
The straight.el package is a declarative package manager for Emacs that aims to replace traditional systems like package.el by providing more precise control over package installation and management. Unlike package.el, which relies on downloading pre-built packages from ELPA archives, straight.el clones packages directly from their source repositories (typically Git), enabling reproducible and fully source-controlled package configurations.
Add the straight.el bootstrap code to ~/.emacs.d/pre-init.el:
;; Straight bootstrap
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name
"straight/repos/straight.el/bootstrap.el"
(or (bound-and-true-p straight-base-dir)
user-emacs-directory)))
(bootstrap-version 7))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(setq straight-use-package-by-default t)
;; Limit Git clone depth to a single commit when using straight.el. This
;; performs shallow clones, reducing download size the cost of full
;; repository history.
;; (setq straight-vc-git-default-clone-depth 1)
setq instead of setopt?The minimal-emacs.d configuration prioritizes an optimized, fast startup. Using setopt introduces overhead due to its type checking and function execution. For the vast majority of variables, this overhead is unnecessary during the initial startup phase. Read: Emacs startup: Why setq beats setopt, customize-set-variable, and use-package :custom?
During the development of your init files, the author strongly recommends adding the following line at the very beginning of your ~/.emacs.d/pre-early-init.el file:
(setq debug-on-error t)
Enabling debug-on-error at this stage allows you to catch errors that might otherwise cause Emacs to fail silently or behave unpredictably.
It is recommended to read: Configuring Emacs scrolling for better performance and usability
By default, minimal-emacs.d sets scroll-conservatively to 20:
(setq scroll-conservatively 20) ; Default minimal-emacs.d value
This makes Emacs recenters the window when the cursor moves past scroll-conservatively lines beyond the window edge.
You can override this in your post-init.el file. Setting it to 0 forces Emacs to recenter the point aggressively, typically positioning it in the middle of the window (NOT RECOMMENDED):
(setq scroll-conservatively 0) ; NOT RECOMMENDED
Although this offers more surrounding context, it results in frequent and pronounced screen movement, which can disrupt navigation. A value of 0 is generally discouraged unless this behavior is explicitly desired.
A value of 101 minimizes screen movement and maintains point visibility with minimal adjustment:
(setq scroll-conservatively 101)
The main drawback of 101 is that Emacs will avoid recentering almost entirely, only adjusting the window just enough to keep point visible at the very top or very bottom of the screen. Point can stick to the top or bottom edge of the window, giving you very little context above or below, which can make editing harder if you want surrounding lines visible.
Related articles:
site-run-file and inhibit-default-initEmacs performs a multi-stage initialization sequence that may include system-level configuration before and after user configuration. For a minimal and fully deterministic setup, these stages can be disabled.
Emacs can load two optional system-wide files:
site-start.el: Executed before the user configuration. Controlled by the variable site-run-file.default.el: Executed after the user configuration. Controlled by the variable inhibit-default-init.Both files are typically maintained by operating systems or system administrators to provide global defaults, package path adjustments, or distribution-specific behavior.
site-start.el (Pre-Initialization Stage)By default, Emacs evaluates site-start.el early in the startup process. While useful in managed environments, this introduces two disadvantages for a minimal configuration:
load-path, or introduce behavior that differs across machines.To ensure a clean and reproducible startup, disable this stage in ~/.emacs.d/pre-early-init.el:
(setq site-run-file nil)
This guarantees that no system-level configuration executes before the user configuration.
Note: On conventional GNU/Linux distributions such as Ubuntu, Fedora, or Arch Linux, site-start.el is often optional and may only introduce distribution defaults. However, on functional systems such as NixOS or Guix System, it may be required to populate essential load-path entries. Disabling it in such environments can prevent Emacs from locating required libraries.
default.el (Post-Initialization Stage)After loading the user configuration, Emacs may evaluate default.el. This file can override user-defined settings or introduce additional global behavior.
To prevent any system configuration from executing after the user initialization, add the following to pre-early-init.el:
(setq inhibit-default-init t)
Disabling both site-run-file and default.el removes system-level interference, reduces startup variability, and establishes a fully controlled initialization environment suitable for minimal and reproducible configurations.
As your configuration grows, your post-init.el might become difficult to navigate. A standard practice in Elisp development is to split a large configuration into smaller, modular files organized by topic (e.g., UI, programming languages, Org mode).
Here is how you can organize your custom Elisp files:
Step 1: Create a directory to store your modular configuration files, for example, ~/.emacs.d/lisp/.
Step 2: Create your individual configuration files inside the ~/.emacs.d/lisp/ directory. For example, create a file named ``~/.emacs.d/lisp/my-ui-config.el`.
Step 3: At the end of my-ui-config.el, add the provide function so Emacs knows the feature has been loaded:
;;; my-ui-config.el --- UI customizations -*- no-byte-compile: t; lexical-binding: t; -*-
;; Your UI configuration goes here...
(setq display-line-numbers-type 'relative)
(provide 'my-ui-config)
Step 4: Add this new directory to your Emacs load-path. You can do this by adding the following line to the top of your ~/.emacs.d/post-init.el (or pre-init.el):
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
Step 5: Finally, load this file in your ~/.emacs.d/post-init.el using require:
(require 'my-ui-config)
This keeps your post-init.el clean and makes it much easier to maintain your setup.
By default, minimal-emacs.d is configured to prioritize packages from GNU ELPA and NonGNU ELPA repositories over MELPA, ensuring greater stability.
If you prefer to obtain the latest packages from MELPA to access new features and improvements, you can adjust the priority so that Emacs use-package retrieves the newest versions from MELPA before consulting the stable GNU and NonGNU repositories. While MELPA packages are generally regarded as less stable, actual breakages are uncommon; over the past year, only a single package (package-lint) out of 146 packages in the author's configuration experienced a brief disruption, which was quickly resolved.
Benefit:
Drawback:
To ensure that Emacs always installs or updates to the newest versions of all packages, add the following configuration to ~/.emacs.d/post-early-init.el:
;; Obtain the latest packages from MELPA to access new features and
;; improvements. While MELPA packages are generally regarded as less stable,
;; actual breakages are uncommon; over the past year, only a single package
;; (package-lint) out of 146 packages in the minimal-emacs.d author's
;; configuration experienced a brief disruption, which was quickly resolved.
(setq package-archive-priorities '(("melpa" . 90)
("gnu" . 70)
("nongnu" . 60)
("melpa-stable" . 50)))
This setup prioritizes MELPA over the stable GNU and NonGNU repositories. When multiple archives provide the same package, Emacs will choose the version from the archive with the highest priority. As a result, you will consistently receive the latest available versions from MELPA while still having access to stable GNU and NonGNU packages when MELPA does not provide them.
In the event of a package breakage, you can direct Emacs to install a package from a specific repository. For instance, to ensure that evil and evil-collection are installed from melpa-stable, add the following configuration to ~/.emacs.d/post-early-init.el:
(setq package-pinned-packages
'((evil . "melpa-stable")
(evil-collection . "melpa-stable")))
Here is a comprehensive package-pinned-packages configuration to guarantee that essential packages, such as consult or corfu, are retrieved from a stable repository, while all remaining packages are obtained from MELPA according to the `package-archive-priorities' priorities above:
(setq package-pinned-packages
'((annalist . "melpa-stable")
(ansible-doc . "melpa-stable")
(apheleia . "melpa-stable")
(basic-mode . "melpa-stable")
(consult-dir . "melpa-stable")
(corfu-prescient . "melpa-stable")
(dtrt-indent . "melpa-stable")
(dumb-jump . "melpa-stable")
(elisp-refs . "melpa-stable")
(evil-collection . "melpa-stable")
(f . "melpa-stable")
(flymake-quickdef . "melpa-stable")
(groovy-mode . "melpa-stable")
(highlight-defined . "melpa-stable")
(markdown-toc . "melpa-stable")
(org-appear . "melpa-stable")
(package-lint-flymake . "melpa-stable")
(parent-mode . "melpa-stable")
(php-mode . "melpa-stable")
(prescient . "melpa-stable")
(s . "melpa-stable")
(tocus . "melpa-stable")
(treesit-auto . "melpa-stable")
(vertico-prescient . "melpa-stable")
(visual-fill-column . "melpa-stable")
(yasnippet-snippets . "melpa-stable")
(aggressive-indent . "gnu")
(cape . "gnu")
(compat . "gnu")
(consult . "gnu")
(corfu . "gnu")
(csv-mode . "gnu")
(dash . "gnu")
(diff-hl . "gnu")
(diminish . "gnu")
(easy-escape . "gnu")
(embark . "gnu")
(embark-consult . "gnu")
(expand-region . "gnu")
(gcmh . "gnu")
(indent-bars . "gnu")
(marginalia . "gnu")
(modus-themes . "gnu")
(orderless . "gnu")
(org . "gnu")
(rainbow-mode . "gnu")
(transient . "gnu")
(vertico . "gnu")
(yasnippet . "gnu")
(ztree . "gnu")
(eat . "nongnu")
(edit-indirect . "nongnu")
(evil-visualstar . "nongnu")
(exec-path-from-shell . "nongnu")
(git-modes . "nongnu")
(golden-ratio . "nongnu")
(goto-chg . "nongnu")
(gptel . "nongnu")
(lua-mode . "nongnu")
(magit . "nongnu")
(markdown-mode . "nongnu")
(package-lint . "nongnu")
(page-break-lines . "nongnu")
(paredit . "nongnu")
(popup . "nongnu")
(rainbow-delimiters . "nongnu")
(undo-fu . "nongnu")
(undo-fu-session . "nongnu")
(wgrep . "nongnu")
(with-editor . "nongnu")
(ws-butler . "nongnu")
(yaml-mode . "nongnu")))
Note: The minimal-emacs.d author does not recommend using MELPA Stable. Use MELPA instead, which is enabled by default in the minimal-emacs.d configuration.
By default, minimal-emacs.d uses MELPA instead of MELPA Stable because MELPA Stable offers outdated packages that lack essential features. If you prefer to use MELPA Stable, you may follow the instructions below.
Here are the key differences between MELPA (the default repository used in minimal-emacs.d) and MELPA Stable:
If you prefer MELPA Stable over MELPA, you can add MELPA Stable and prioritize it. To ensure packages are fetched from MELPA Stable first, add the following configuration to ~/.emacs.d/post-early-init.el:
;; This change increases MELPA Stable priority to 70, above MELPA,
;; ensuring that MELPA is preferred for package installations
;; over MELPA Stable.
;; (Note: The minimal-emacs.d author does not assign higher priority to MELPA
;; Stable than to MELPA.)
;;
;; (setq package-archive-priorities '(("gnu" . 90)
;; ("nongnu" . 80)
;; ("melpa-stable" . 70)
;; ("melpa" . 60)))
Add the following line to the end of your post-init.el file:
(minimal-emacs-load-user-init "local.el")
This allows local.el to load, enabling custom configurations specific to the machine.
(Ensure that local.el is in the same directory as post-init.el.)
Alternatively, the target file can be loaded from a subdirectory relative to the location of post-init.el:
(minimal-emacs-load-user-init "sub-directory/local.el")
In certain Emacs configurations, specific files may be recompiled repeatedly during startup.
Compiling /snap/emacs/current/usr/share/emacs/lisp/org/org-loaddefs.el.gz...
Compiling /snap/emacs/current/usr/share/emacs/etc/themes/modus-vivendi-theme.el...
This behavior arises because Emacs performs native compilation on specific Elisp files, and in many scenarios, it is desirable to prevent compilation of files that fail during the process.
Emacs can be configured to bypass native compilation for files whose paths match a list of regular expression patterns by setting native-comp-jit-compilation-deny-list. For example:
(let ((deny-list '("\\(?:[/\\\\]\\.dir-locals\\.el\\(?:\\.gz\\)?$\\)"
"\\(?:[/\\\\]modus-vivendi-theme\\.el\\(?:\\.gz\\)?$\\)"
"\\(?:[/\\\\][^/\\\\]+-loaddefs\\.el\\(?:\\.gz\\)?$\\)"
"\\(?:[/\\\\][^/\\\\]+-autoloads\\.el\\(?:\\.gz\\)?$\\)")))
(setq native-comp-jit-compilation-deny-list deny-list)
;; Deprecated
(with-no-warnings
(setq native-comp-deferred-compilation-deny-list deny-list)
(setq comp-deferred-compilation-deny-list deny-list)))
This deny list instructs Emacs to bypass native compilation for files matching the specified patterns, preventing unnecessary or error-prone recompilation while permitting all other files to be compiled normally.
To load customizations saved by Emacs (M-x customize), add the following code snippet to the post-init.el file. This ensures that the custom file, typically set to a separate file for user preferences, is loaded without errors or messages during startup:
(when custom-file
(load custom-file 'noerror 'nomessage))
However, rather than relying on customizations loaded with the code above, the author recommends configuring Emacs through init files (just as you are doing by reading this README.md and customizing packages using use-package with the :custom keyword).
Add the following to ~/.emacs.d/pre-early-init.el to ensure that minimal-emacs.d restores the specified amount after startup:
(setq minimal-emacs-gc-cons-threshold (* 64 1024 1024))
By default, Emacs loads .dir-locals.el from the current directory or its parents and applies project-specific settings such as indentation, compilation commands, or custom minor modes. While useful in many cases, this behavior can introduce unintended overrides, inconsistencies, or even security risks when working with untrusted projects.
If you want to prevent Emacs from applying these directory-local settings, you can disable .dir-locals.el by setting enable-dir-local-variables to nil:
(setq enable-dir-local-variables nil)
Add the following to the top of the ~/.emacs.d/pre-early-init.el file to make minimal-emacs.d use the MINIMAL_EMACS_USER_DIRECTORY environment variable to change ~/.emacs.d to another directory:
;; Place this at the very beginning of pre-early-init.el
(let ((previous-minimal-emacs-user-directory (expand-file-name
minimal-emacs-user-directory))
(env-dir (getenv "MINIMAL_EMACS_USER_DIRECTORY")))
(setq minimal-emacs-user-directory (if env-dir
(expand-file-name env-dir)
(expand-file-name user-emacs-directory)))
(unless (string= minimal-emacs-user-directory
previous-minimal-emacs-user-directory)
;; Load pre-early-init.el from the new directory
(minimal-emacs-load-user-init "pre-early-init.el")))
During the execution of early-init.el (and pre-early-init.el and post-early-init.el), Emacs has not yet loaded the graphical user interface (GUI). This file is used for configurations that need to be applied before the GUI is initialized, such as settings that affect the early stages of the Emacs startup process.
Thus, post-early-init.el and pre-init.el serve different purposes and are not the same.
The menu bar is disabled by default in minimal-emacs.d to provide a minimal, distraction-free environment, which many experienced users prefer.
The menu bar can be re-enabled by adding the following configuration to ~/.emacs.d/pre-early-init.el:
(setq minimal-emacs-ui-features '(menu-bar))
Other UI features can also be enabled by adding the following to ~/.emacs.d/pre-early-init.el:
(setq minimal-emacs-ui-features '(context-menu tool-bar menu-bar dialogs tooltips))
The author began working on it after realizing that no existing starter kit offered a truly minimal setup with the flexibility for users to choose exactly what to include in their configuration.
To ensure the minimal-emacs.d configuration loads post-early-init.el, pre-init.el, and post-init.el from a different directory, such as ~/.config/minimal-emacs.d/, modify the minimal-emacs-user-directory variable by adding the following to your ~/.emacs.d/pre-early-init.el file:
(setq minimal-emacs-user-directory "~/.config/minimal-emacs.d/")
This will ensure that the minimal-emacs.d configuration loads post-early-init.el, pre-init.el, and post-init.el from ~/.config/minimal-emacs.d/.
Keep in mind that if you change the minimal-emacs-user-directory, minimal-emacs.d will attempt to load the rest of the configuration from that directory (e.g., ~/.config/minimal-emacs/post-early-init.el, ~/.config/minimal-emacs/pre-init.el and ~/.config/minimal-emacs/post-init.el, etc.).
NOTE: Running package initialization and installation during the early-init phase is NOT RECOMMENDED because this stage occurs before the GUI system, windowing, and comprehensive error-handling buffers are fully initialized. When package-install or package-refresh-contents triggers a failure-such as a TLS handshake error or a lost network connection-Emacs cannot yet render a graphical window to display the backtrace or warning. This results in a "silent" hang or a crash that provides no visual feedback to the user, forcing a pivot to a terminal to inspect standard output. Furthermore, many packages expect a fully functional frame and loaded user environment to configure themselves correctly; forcing them to load during early-init bypasses the intentional separation designed to let you set up UI-independent variables before the package system and GUI logic complicate the startup sequence.
To install and load packages during the early-init phase, add the following to post-early-init.el:
;; THIS IS NOT RECOMMENDED
;;
;; Running package initialization and installation during the early-init phase
;; is NOT RECOMMENDED because this stage occurs before the GUI system,
;; windowing, and comprehensive error-handling buffers are fully initialized.
;; When package-install or `package-refresh-contents` triggers a failure-such as
;; a TLS handshake error or a lost network connection-Emacs cannot yet render a
;; graphical window to display the backtrace or warning. This results in a
;; "silent" hang or a crash that provides no visual feedback to the user,
;; forcing a pivot to a terminal to inspect standard output. Furthermore, many
;; packages expect a fully functional frame and loaded user environment to
;; configure themselves correctly; forcing them to load during early-init
;; bypasses the intentional separation designed to let you set up UI-independent
;; variables before the package system and GUI logic complicate the startup
;; sequence.
;;
;; File: `post-early-init.el'
(setq minimal-emacs-package-initialize-and-refresh nil)
;; If you want to ignore the warning:
;; "Warning (package): Unnecessary call to package-initialize in init file."
;; Uncomment the following setq:
;; (setq warning-suppress-types '((package)))
;; Initialize packages in the early-init phase instead of init
(when (bound-and-true-p minimal-emacs-package-initialize-and-refresh)
;; Initialize and refresh package contents again if needed
(package-initialize)
(unless (package-installed-p 'use-package)
(unless (seq-empty-p package-archive-contents)
(package-refresh-contents))
(package-install 'use-package))
(require 'use-package))
;; TODO: Add your use-package packages here
If you want to maintain a strictly version-controlled, declarative configuration, you should prevent the Emacs customization interface from automatically appending custom-set-variables blocks to your files.
;; Prevent Emacs from writing custom settings to any file
(with-eval-after-load 'cus-edit
(advice-add 'custom-save-all :override #'ignore))
Optimizing Emacs startup - Guide to deferred package loading with use-package: As an Emacs user, your configuration can easily grow from a few lightweight adjustments to a massive, hundred-package IDE. Without careful management, Emacs startup time can degrade from sub-second execution to several seconds, or minutes, in the worst cases. Eager package loading is one common source of startup overhead. This guide explains how Emacs loads libraries, how use-package configures package loading, and how deferred loading can reduce startup time.
Emacs: Preventing Org-Mode formatting bleed when copy-pasting: Copying text from an Org buffer often results in unwanted colors, backgrounds, or text weights bleeding into the destination buffer. By default, vanilla Emacs preserves explicit text formatting (face properties) when copying and pasting to support rich-text environments. While standard syntax highlighting (font-lock-face) is automatically stripped, modes like org-mode rely heavily on the face property for their visual styling.
Securing and reducing prompts for Emacs .dir-locals.el and local variables: Emacs automatically applies project-specific configurations through file-local and directory-local (.dir-locals.el) variables when opening a file or directory. While this feature ensures consistent settings across environments, it can cause security risks and persistent prompt fatigue when editing source code. Malicious .dir-locals.el files or file-local variables containing eval forms can execute arbitrary Lisp code if Emacs is configured to evaluate them, or if the user approves the relevant prompt by mistake. This article outlines configurations for securing file-local and directory-local variables while reducing prompts.
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. Compiling 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. 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...
Measuring Emacs Startup Time More Accurately Than the Built-in emacs-init-time Function: As an Emacs configuration grows, startup time can gradually increase. Measuring that increase accurately makes it easier to identify regressions. However, the built-in emacs-init-time function does not measure the entire startup sequence.
The minimal-emacs.d base provides a sensible foundation for your personal configuration. It addresses common pain points in vanilla Emacs to provide a responsive and clean environment from the start, without forcing a specific workflow.
--histogram diff algorithm and automatically follow file renames in logs.dired-dwim-target for easier file operations between panes. Auto-updates Dired buffers and cleans up deleted directories silently.~/.emacs files.pre-early-init.el, post-early-init.el, pre-init.el, and post-init.el) to hook into different stages of the startup process.~/.authinfo.gpg) and directs the GPG agent to use the minibuffer for passphrase entry.The minimal-emacs.d project has been written by James Cherti and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version.
Copyright (C) 2024-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 (in the .LICENSE file).
Contribution from the minimal-emacs.d community:
Other Emacs packages by the same author:
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.Hacker News (1)
Emacs Lisp
100.0%