diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33a3756..dbf93c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,9 @@ # name: CI -on: [push, pull_request] +on: + - workflow_dispatch + - pull_request jobs: test: diff --git a/README.md b/README.md index 53611a3..762753b 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,17 @@ The original repository contains a lot of useful info, so do check it out. The *minimal-emacs.d* project is: - **Minimal yet effective:** A solid starting point. - **Better defaults:** Improved settings for usability, UI, garbage collection, and built-in packages. -- **0 packages loaded / No forced modes:** Unlike other frameworks or starter kits, *minimal-emacs.d* does not impose modes or require packages. **You have full control** over which global or minor modes to enable and which packages to load with `require`. +- **0 packages loaded / No forced modes:** Unlike other frameworks or starter kits, *minimal-emacs.d* does not impose modes or require packages. **You have full control** over which global or minor modes to enable and which packages to load. - **Customizable foundation:** Designed to be extended, not replaced. This README.md offers extensive recommendations for customizing your *minimal-emacs.d* configuration. (Reminder: [Never modify init.el and early-init.el. Modify these instead...](#customizations-never-modify-initel-and-early-initel-modify-these-instead)) 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. +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. The user retains full control over which packages to install and which modes to enable. + +Emacs comes with many well-designed defaults, but it also retains some less-than-ideal settings, often due to historical constraints or legacy compatibility. The purpose of *minimal-emacs.d* is to offer refined defaults that improve both usability and performance, replacing long-standing Emacs settings that no longer serve modern workflows well. + ![](https://www.jamescherti.com/misc/screenshot-minimal-emacs-2.png) *(The theme shown in the screenshot above is ef-melissa-light, which is part of the ef-themes collection available on MELPA.)* ![](https://www.jamescherti.com/misc/screenshot-minimal-emacs-1.png) @@ -61,6 +65,7 @@ In addition to *minimal-emacs.d*, startup speed is influenced by your computer's - [Debug on error](#debug-on-error) - [Customizations: UI (pre-early-init.el)](#customizations-ui-pre-early-initel) - [How to enable the menu-bar, the tool-bar, dialogs, the contextual menu, and tooltips?](#how-to-enable-the-menu-bar-the-tool-bar-dialogs-the-contextual-menu-and-tooltips) + - [Reducing clutter in `~/.emacs.d` by redirecting files to `~/.emacs.d/var/`](#reducing-clutter-in-emacsd-by-redirecting-files-to-emacsdvar) - [Customizations: Packages (post-init.el)](#customizations-packages-post-initel) - [Optimization: Native Compilation](#optimization-native-compilation) - [How to activate recentf, savehist, saveplace, and auto-revert?](#how-to-activate-recentf-savehist-saveplace-and-auto-revert) @@ -75,24 +80,28 @@ In addition to *minimal-emacs.d*, startup speed is influenced by your computer's - [Enhancing undo/redo](#enhancing-undoredo) - [Configuring Vim keybindings using Evil?](#configuring-vim-keybindings-using-evil) - [Configuring LSP Servers with Eglot (built-in)](#configuring-lsp-servers-with-eglot-built-in) - - [Session Management](#session-management) + - [Persisting and Restoring all buffers, windows/split, tab-bar, frames...](#persisting-and-restoring-all-buffers-windowssplit-tab-bar-frames) - [Configuring org-mode](#configuring-org-mode) - [Configuring markdown-mode (e.g., README.md syntax)](#configuring-markdown-mode-eg-readmemd-syntax) - [Tree-sitter Integration (Better Syntax Highlighting)](#tree-sitter-integration-better-syntax-highlighting) + - [Auto upgrade Emacs packages](#auto-upgrade-emacs-packages) + - [Safely terminating unused buffers](#safely-terminating-unused-buffers) - [Treemacs, a tree layout file explorer (Sidebar file explorer)](#treemacs-a-tree-layout-file-explorer-sidebar-file-explorer) - [Inhibit the mouse](#inhibit-the-mouse) - [Spell checker](#spell-checker) + - [Efficient jumps for enhanced productivity](#efficient-jumps-for-enhanced-productivity) - [Asynchronous code formatting without cursor disruption](#asynchronous-code-formatting-without-cursor-disruption) + - [Efficient template expansion with snippets](#efficient-template-expansion-with-snippets) - [A better Emacs *help* buffer](#a-better-emacs-help-buffer) - [Enhancing the Elisp development experience](#enhancing-the-elisp-development-experience) - [Showing the tab-bar](#showing-the-tab-bar) - - [Preventing Emacs from saving custom.el](#preventing-emacs-from-saving-customel) - [Changing the Default Font](#changing-the-default-font) + - [Persist Text Scale](#persist-text-scale) + - [Loading the custom.el file](#loading-the-customel-file) - [Which other customizations can be interesting to add?](#which-other-customizations-can-be-interesting-to-add) - [Customizations: pre-early-init.el](#customizations-pre-early-initel) - - [Reducing clutter in `~/.emacs.d` by redirecting files to `~/.emacs.d/var/`](#reducing-clutter-in-emacsd-by-redirecting-files-to-emacsdvar) - [Configuring straight.el](#configuring-straightel) - - [Configuring elpaca (package manager)](#configuring-elpaca-package-manager) + - [Configuring Elpaca (package manager)](#configuring-elpaca-package-manager) - [Frequently asked questions](#frequently-asked-questions) - [Customizing Scroll Recentering](#customizing-scroll-recentering) - [How to display Emacs startup duration?](#how-to-display-emacs-startup-duration) @@ -197,6 +206,22 @@ To customize your Emacs setup to include various user interface elements, you ca These settings control the visibility of dialogs, context menus, toolbars, menu bars, and tooltips. +### Reducing clutter in `~/.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`: +``` emacs-lisp +;;; 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. + ## Customizations: Packages (post-init.el) 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. @@ -211,7 +236,7 @@ Native compilation enhances Emacs performance by converting Elisp code into nati ``` (A non-nil result indicates that native compilation is active.) -2. Ensure all libraries are byte-compiled and native-compiled using [compile-angel.el](https://github.com/jamescherti/compile-angel.el). To install compile-angel, add the following code at the very beginning of your `~/.emacs.d/post-init.el` file, before all other packages: +2. Ensure all libraries are byte-compiled and native-compiled using [compile-angel.el](https://github.com/jamescherti/compile-angel.el). To install compile-angel, add the following code to the `~/.emacs.d/post-init.el` file: ```emacs-lisp ;; Native compilation enhances Emacs performance by converting Elisp code into ;; native machine code, resulting in faster execution and improved @@ -221,7 +246,6 @@ Native compilation enhances Emacs performance by converting Elisp code into nati ;; of your `~/.emacs.d/post-init.el` file, before all other packages. (use-package compile-angel :ensure t - :demand t :custom ;; Set `compile-angel-verbose` to nil to suppress output from compile-angel. ;; Drawback: The minibuffer will not display compile-angel's actions. @@ -244,8 +268,10 @@ Native compilation enhances Emacs performance by converting Elisp code into nati ;; 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 before they are loaded. - (compile-angel-on-load-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)) ``` ### How to activate recentf, savehist, saveplace, and auto-revert? @@ -255,28 +281,71 @@ The recentf, savehist, saveplace, and auto-revert built-in packages are already ;; 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. -(add-hook 'after-init-hook #'global-auto-revert-mode) +(use-package autorevert + :ensure nil + :commands (auto-revert-mode global-auto-revert-mode) + :hook + (after-init . global-auto-revert-mode) + :custom + (auto-revert-interval 3) + (auto-revert-remote-files nil) + (auto-revert-use-notify t) + (auto-revert-avoid-polling nil) + (auto-revert-verbose t)) -;; recentf is an Emacs package that maintains a list of recently +;; Recentf is an Emacs package that maintains a list of recently ;; accessed files, making it easier to reopen files you have worked on ;; recently. -(add-hook 'after-init-hook #'(lambda() - (let ((inhibit-message t)) - (recentf-mode 1)))) +(use-package recentf + :ensure nil + :commands (recentf-mode recentf-cleanup) + :hook + (after-init . recentf-mode) -(with-eval-after-load "recentf" - (add-hook 'kill-emacs-hook #'recentf-cleanup)) + :custom + (recentf-auto-cleanup (if (daemonp) 300 'never)) + (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$")) + + :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. -(add-hook 'after-init-hook #'savehist-mode) +(use-package savehist + :ensure nil + :commands (savehist-mode savehist-save) + :hook + (after-init . savehist-mode) + :custom + (savehist-autosave-interval 600) + (savehist-additional-variables + '(kill-ring ; clipboard + register-alist ; macros + mark-ring global-mark-ring ; marks + search-ring regexp-search-ring))) ;; 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. -(add-hook 'after-init-hook #'save-place-mode) +(use-package saveplace + :ensure nil + :commands (save-place-mode save-place-local-mode) + :hook + (after-init . save-place-mode) + :custom + (save-place-limit 400)) ``` ### Activating autosave @@ -320,7 +389,7 @@ This is different from `auto-save-mode`: `auto-save-mode` periodically saves all [Corfu](https://github.com/minad/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. +[Cape](https://github.com/minad/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. ![](https://github.com/minad/corfu/blob/screenshots/popupinfo-dark.png?raw=true) @@ -674,7 +743,7 @@ To enable **stripspace** and automatically delete trailing whitespace, add the f ### Enhancing undo/redo -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 [undo-fu](https://codeberg.org/ideasman42/emacs-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](https://codeberg.org/ideasman42/emacs-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: @@ -852,7 +921,7 @@ Here is an example of how to configure Eglot to enable or disable certain option :rope_autoimport (:enabled :json-false))))) ``` -### Session Management +### Persisting and Restoring all buffers, windows/split, tab-bar, frames... The [easysession](https://github.com/jamescherti/easysession.el) 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. @@ -936,9 +1005,6 @@ To configure **markdown-mode**, add the following to `~/.emacs.d/post-init.el`: :mode (("\\.markdown\\'" . markdown-mode) ("\\.md\\'" . markdown-mode) ("README\\.md\\'" . gfm-mode)) - :init - (setq markdown-command "multimarkdown") - :bind (:map markdown-mode-map ("C-c C-e" . markdown-do))) @@ -946,6 +1012,29 @@ To configure **markdown-mode**, add the following to `~/.emacs.d/post-init.el`: 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`: +```elisp +;; Automatically generate a table of contents when editing Markdown files +(use-package markdown-toc + :ensure t + :commands (markdown-toc-generate-toc + markdown-toc-generate-or-refresh-toc + markdown-toc-delete-toc + markdown-toc--toc-already-present-p) + :custom + (markdown-toc-header-toc-title "**Table of Contents**")) +``` + +Once installed: + +- To **insert a table of contents** for the first time, run: `M-x markdown-toc-generate-toc` +- To **update an existing table of contents**, run: `M-x markdown-toc-generate-or-refresh-toc` +- To **remove an existing table of contents**, run: `M-x markdown-toc-delete-toc` + +These 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)](https://www.jamescherti.com/emacs-markdown-table-of-contents-update-before-save/). + ### Tree-sitter Integration (Better Syntax Highlighting) Tree-sitter is an incremental parsing system introduced in Emacs 29 that provides precise, high-performance syntax analysis and highlighting by constructing concrete syntax trees from source code. It supports a broad set of programming languages, including Bash, C, C++, C#, CMake, CSS, Dockerfile, Go, Java, JavaScript, JSON, Python, Rust, TOML, TypeScript, YAML, Elisp, Lua, Markdown, and many others. Unlike traditional font-lock, which relies on regular expressions, Tree-sitter uses formal grammar definitions to build real-time parse trees, enabling accurate syntax highlighting, structural navigation, code folding, and foundational support for advanced editing features like refactoring. @@ -969,6 +1058,79 @@ To enable Tree-sitter, add the following to your `~/.emacs.d/post-init.el`: (global-treesit-auto-mode)) ``` +### Auto upgrade Emacs packages + +The `auto-package-update` package that automates the process of updating installed packages managed by *package.el*. Instead of requiring users to manually invoke `package-list-packages` and update each package, `auto-package-update` can check for available updates at regular intervals, perform updates in the background, and optionally hide the results buffer or prompt before applying changes. + +To configure **auto-package-update**, add the following to `~/.emacs.d/post-init.el`: + +```elisp +;; This automates the process of updating installed packages +(use-package auto-package-update + :ensure t + :custom + ;; Set the number of days between automatic updates. + ;; Here, packages will only be updated if at least 7 days have passed + ;; since the last successful update. + (auto-package-update-interval 7) + + ;; Suppress display of the *auto-package-update results* buffer after updates. + ;; This keeps the user interface clean and avoids unnecessary interruptions. + (auto-package-update-hide-results t) + + ;; Automatically delete old package versions after updates to reduce disk + ;; usage and keep the package directory clean. This prevents the accumulation + ;; of outdated files in Emacs’s package directory, which consume + ;; unnecessary disk space over time. + (auto-package-update-delete-old-versions t) + + ;; Uncomment the following line to enable a confirmation prompt + ;; before applying updates. This can be useful if you want manual control. + ;; (auto-package-update-prompt-before-update t) + + :config + ;; Run package updates automatically at startup, but only if the configured + ;; interval has elapsed. + (auto-package-update-maybe) + + ;; Schedule a background update attempt daily at 10:00 AM. + ;; This uses Emacs' internal timer system. If Emacs is running at that time, + ;; the update will be triggered. Otherwise, the update is skipped for that + ;; day. Note that this scheduled update is independent of + ;; `auto-package-update-maybe` and can be used as a complementary or + ;; alternative mechanism. + (auto-package-update-at-time "10:00")) +``` + +### Safely terminating unused buffers + +The [buffer-terminator](https://github.com/jamescherti/buffer-terminator.el) 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`: + +```emacs-lisp +(use-package buffer-terminator + :ensure t + :custom + ;; Enable/Disable verbose mode to log buffer cleanup events + (buffer-terminator-verbose nil) + + ;; Set the inactivity timeout (in seconds) after which buffers are considered + ;; inactive (default is 30 minutes): + (buffer-terminator-inactivity-timeout (* 30 60)) ; 30 minutes + + ;; Define how frequently the cleanup process should run (default is every 10 + ;; minutes): + (buffer-terminator-interval (* 10 60)) ; 10 minutes + + :config + (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`.) + ### Treemacs, a tree layout file explorer (Sidebar file explorer) The [treemacs](https://github.com/Alexander-Miller/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. @@ -1125,6 +1287,8 @@ NOTE: `inhibit-mouse-mode` allows users to disable and re-enable mouse functiona The `flyspell` package is a built-in Emacs minor mode that provides on-the-fly spell checking. It highlights misspelled words as you type, offering interactive corrections. In text modes, it checks the entire buffer, while in programming modes, it typically checks only comments and strings. It integrates with external spell checkers like `aspell`, `hunspell`, or `ispell` to provide suggestions and corrections. +NOTE: `flyspell-mode` can become slow when using Aspell, especially with large buffers or aggressive suggestion settings like `--sug-mode=ultra`. This slowdown occurs because Flyspell checks words dynamically as you type or navigate text, requiring frequent communication between Emacs and the external Aspell process. Each check involves sending words to Aspell and receiving results, which introduces overhead from process invocation and inter-process communication. + To configure **flyspell**, add the following to `~/.emacs.d/post-init.el`: ``` emacs-lisp (use-package ispell @@ -1134,11 +1298,19 @@ To configure **flyspell**, add the following to `~/.emacs.d/post-init.el`: ;; Set the ispell program name to aspell (ispell-program-name "aspell") + ;; Define the "en_US" spell-check dictionary locally, telling Emacs to use + ;; UTF-8 encoding, match words using alphabetic characters, allow apostrophes + ;; inside words, treat non-alphabetic characters as word boundaries, and pass + ;; -d en_US to the underlying spell-check program. + (ispell-local-dictionary-alist + '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8))) + ;; Configures Aspell's suggestion mode to "ultra", which provides more ;; aggressive and detailed suggestions for misspelled words. The language ;; is set to "en_US" for US English, which can be replaced with your desired ;; language code (e.g., "en_GB" for British English, "de_DE" for German). - (ispell-extra-args '("--sug-mode=ultra" "--lang=en_US"))) + (ispell-extra-args '(; "--sug-mode=ultra" + "--lang=en_US"))) ;; The flyspell package is a built-in Emacs minor mode that provides ;; on-the-fly spell checking. It highlights misspelled words as you type, @@ -1147,12 +1319,12 @@ To configure **flyspell**, add the following to `~/.emacs.d/post-init.el`: :ensure nil :commands flyspell-mode :hook - ((prog-mode . flyspell-prog-mode) + (; (prog-mode . flyspell-prog-mode) (text-mode . (lambda() (if (or (derived-mode-p 'yaml-mode) (derived-mode-p 'yaml-ts-mode) (derived-mode-p 'ansible-mode)) - (flyspell-prog-mode) + (flyspell-prog-mode 1) (flyspell-mode 1))))) :config ;; Remove strings from Flyspell @@ -1164,6 +1336,25 @@ To configure **flyspell**, add the following to `~/.emacs.d/post-init.el`: flyspell-prog-text-faces))) ``` +### Efficient jumps for enhanced productivity + +The [avy](https://github.com/abo-abo/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`: +```elisp +(use-package avy + :ensure t + :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. + ### Asynchronous code formatting without cursor disruption [Apheleia](https://github.com/radian-software/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. @@ -1183,9 +1374,50 @@ To configure **apheleia**, add the following to `~/.emacs.d/post-init.el`: :hook ((prog-mode . apheleia-mode))) ``` +### Efficient template expansion with snippets + +The [yasnippet](https://github.com/joaotavora/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](https://github.com/AndreaCrotti/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](https://github.com/jamescherti/ultyas)) + + +```elisp +;; The official collection of snippets for yasnippet. +(use-package yasnippet-snippets + :ensure t + :after yasnippet) + +;; 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 + :ensure t + :commands (yas-minor-mode + yas-global-mode) + + :hook + (after-init . yas-global-mode) + + :custom + (yas-also-auto-indent-first-line t) ; Indent first line of snippet + (yas-also-indent-empty-lines t) + (yas-snippet-revival nil) ; Setting this to t causes issues with undo + (yas-wrap-around-region nil) ; Do not wrap region when expanding snippets + ;; (yas-triggers-in-field nil) ; Disable nested snippet expansion + ;; (yas-indent-line 'fixed) ; Do not auto-indent snippet content + ;; (yas-prompt-functions '(yas-no-prompt)) ; No prompt for snippet choices + + :init + ;; Suppress verbose messages + (setq yas-verbosity 0)) +``` + ### A better Emacs *help* buffer -Helpful is an alternative to the built-in Emacs help that provides much more contextual information. +[Helpful](https://github.com/Wilfred/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`: ```emacs-lisp @@ -1275,14 +1507,6 @@ Configure the `tab-bar-show` variable to 1 to display the tab bar exclusively wh (setopt tab-bar-show 1) ``` -### Preventing Emacs from saving custom.el - -To prevent Emacs from saving customization information to a custom file, set `custom-file` to `null-device` by adding to the following to `~/.emacs.d/post-init.el`: -``` emacs-lisp -;; Prevent Emacs from saving customization information to a custom file -(setq custom-file null-device) -``` - ### Changing the Default Font To customize the default font, add the following expression to your `~/.emacs.d/post-init.el`: @@ -1302,6 +1526,43 @@ On Linux, you can display a comprehensive list of all installed font families by fc-list : family | sed 's/,/\n/g' | sort -u ``` +### Persist Text Scale + +The [persist-text-scale](https://github.com/jamescherti/persist-text-scale.el) 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: +- Each file-visiting buffer has its own independent text scale. +- Special buffers, identified by their buffer names, each retain their own text scale setting. +- All Dired buffers maintain the same font size, treating Dired as a unified "file explorer" where the text scale remains consistent across different buffers. + +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`: +```elisp +(use-package persist-text-scale + :commands (persist-text-scale-mode + persist-text-scale-restore) + + :hook (after-init . persist-text-scale-mode) + + :custom + (text-scale-mode-step 1.07)) +``` + +### Loading the custom.el file + +**NOTE:** The author advises against loading `custom.el`. To disable it, set `custom-file` to the null device using `(setq custom-file null-device)`. 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`: + +```elisp +;; 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) +``` + ### Which other customizations can be interesting to add? 1. Read the following article from the same author: [Essential Emacs Packages for Efficient Software Development and Text Editing](https://www.jamescherti.com/essential-emacs-packages/) @@ -1359,9 +1620,6 @@ fc-list : family | sed 's/,/\n/g' | sort -u ;; closing windows. (add-hook 'after-init-hook #'winner-mode) -;; Replace selected text with typed text -;; (delete-selection-mode 1) - (use-package uniquify :ensure nil :custom @@ -1375,6 +1633,9 @@ fc-list : family | sed 's/,/\n/g' | sort -u ;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Window-Dividers.html (add-hook 'after-init-hook #'window-divider-mode) +;; 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. @@ -1393,6 +1654,16 @@ fc-list : family | sed 's/,/\n/g' | sort -u "\\|^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. (add-hook 'after-init-hook #'minibuffer-depth-indicate-mode) @@ -1404,6 +1675,24 @@ fc-list : family | sed 's/,/\n/g' | sort -u (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) + +;; Configure the built-in Emacs server to start after initialization, +;; allowing the use of the emacsclient command to open files in the +;; current session. +(use-package server + :ensure nil + :commands server-start + :hook + (after-init . server-start)) ``` It is also recommended to read the following articles: @@ -1413,23 +1702,6 @@ It is also recommended to read the following articles: ## Customizations: pre-early-init.el -### Reducing clutter in `~/.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`: -``` emacs-lisp -;; Reducing clutter in ~/.emacs.d by redirecting files to ~/.emacs.d/var/ -;; IMPORTANT: This part should be in the pre-early-init.el file -(setq minimal-emacs-var-dir (expand-file-name "var/" minimal-emacs-user-directory)) -(setq package-user-dir (expand-file-name "elpa" minimal-emacs-var-dir)) -(setq user-emacs-directory minimal-emacs-var-dir) -``` - -**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. - ### Configuring straight.el 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. @@ -1454,7 +1726,9 @@ The `straight.el` package is a declarative package manager for Emacs that aims t (load bootstrap-file nil 'nomessage)) ``` -### Configuring elpaca (package manager) +### Configuring Elpaca (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. @@ -1466,7 +1740,7 @@ Add to `~/.emacs.d/pre-early-init.el`: (setq minimal-emacs-package-initialize-and-refresh nil) ``` -And [add the elpaca bootstrap code](https://github.com/progfolio/elpaca?tab=readme-ov-file#installer) to `~/.emacs.d/pre-init.el`: +And [add the Elpaca bootstrap code](https://github.com/progfolio/elpaca?tab=readme-ov-file#installer) to `~/.emacs.d/pre-init.el`: ```elisp ;; Elpaca bootstrap (defvar elpaca-installer-version 0.11) @@ -1708,9 +1982,11 @@ A drawback of using the early-init phase instead of init is that if a package fa ### Comments from users - [JamesBrickley (Shout out to this starter-kit: Minimal-Emacs )](https://www.reddit.com/r/emacs/comments/1epz7qn/shout_out_to_this_starterkit_minimalemacs/) appreciates that *minimal-emacs.d* provides an optimized *early-init.el* and *init.el* for fast startup times and sensible default settings. He highlights that the project includes all the essential configurations needed for a well-tuned Emacs setup, eliminating the need to sift through conflicting advice on topics like garbage collection optimization. While he has encountered similar settings before, he also discovered new optimizations he had not seen elsewhere. +- [Brandon Schneider (skarekrow)](https://github.com/skarekrow): "...the minimal-emacs project is incredible. I love how documented it is as a beginner to learn from. Thank you for all the effort you've put into that and the other packages you maintain. It's a huge boon to new users." - [Leading_Ad6415 commented on Reddit](https://www.reddit.com/r/emacs/comments/1feaf37/comment/lmw7ijd/) that after switching to *minimal-emacs.d*, their configuration execution time decreased from 3 seconds to just 1 second by simply replacing their `init.el` and `early-init.el` files with those from the project. - [Another user commented on Reddit](https://www.reddit.com/r/emacs/comments/1feaf37/comment/lrsfd64/), highlighting how a minimal-emacs.d significantly enhanced their Emacs performance. They reported substantial startup time reductions on both their main machine (from ~2.25 to ~0.95 seconds) and an older laptop (from ~2.95 to ~1.27 seconds) while also experiencing a generally snappier performance within Emacs. The user expressed gratitude for the project, calling it fantastic. - [Cyneox commented on Reddit](https://www.reddit.com/r/emacs/comments/1gh687a/comment/lwdv18t/), expressing gratitude for the resource and sharing their experience. They mentioned it was their fourth attempt to set up a vanilla configuration and highlighted that they had been using the repository as a foundation for their customizations over the past few days. They appreciated the absence of unexplained behavior and the clear instructions on where to place files. The user reported successful testing on both Linux and macOS, noting that everything functioned smoothly, including in the terminal. +- [Mlepnos1984](https://www.reddit.com/r/emacs/comments/1lz181i/comment/n2yjj17/): "I give you an A+ on documentation, the readme is great!" - [rrajath](https://www.reddit.com/r/emacs/comments/1ihn2tv/comment/mb0ja8k/) has been using the minimal-emacs.d config for the past several months and loves it. His previous setup used to take around 4 seconds to load, but with minimal-emacs.d, it now loads in just 1 second. - [LionyxML](https://www.reddit.com/r/emacs/comments/1ihn2tv/comment/mb35t9y/) considers that *minimal-emacs.d* contains one of the best README files he has ever read. The author of *minimal-emacs.d* found his comment encouraging. Reading this README.md is highly recommended for anyone looking to start customizing their *minimal-emacs.d* configuration. - [cyneox](https://www.reddit.com/r/emacs/comments/1ihn2tv/comment/mdnzgqx/): "Still using it and loving it! Thanks for the regular updates." diff --git a/early-init.el b/early-init.el index 867bf29..d959cd6 100644 --- a/early-init.el +++ b/early-init.el @@ -4,7 +4,7 @@ ;; URL: https://github.com/jamescherti/minimal-emacs.d ;; Package-Requires: ((emacs "29.1")) ;; Keywords: maint -;; Version: 1.2.1 +;; Version: 1.3.0 ;; SPDX-License-Identifier: GPL-3.0-or-later ;;; Commentary: @@ -58,16 +58,6 @@ Ignored if `minimal-emacs-optimize-startup-gc' is nil.") (defvar minimal-emacs-gc-cons-threshold-restore-delay nil "Number of seconds to wait before restoring `gc-cons-threshold'.") -(defvar minimal-emacs-inhibit-redisplay-during-startup nil - "Suppress redisplay during startup to improve performance. -This prevents visual updates while Emacs initializes. The tradeoff is that you -won't see the progress or activities during the startup process.") - -(defvar minimal-emacs-inhibit-message-during-startup nil - "Suppress startup messages for a cleaner experience. -This slightly enhances performance. The tradeoff is that you won't be informed -of the progress or any relevant activities during startup.") - (defvar minimal-emacs-optimize-file-name-handler-alist t "Enable optimization of `file-name-handler-alist'. When non-nil, this variable activates optimizations to reduce file name handler @@ -93,11 +83,19 @@ native compilation features: If nil, these variables are left at their default values and are not modified during setup.") -(defvar minimal-emacs-user-directory user-emacs-directory - "The default value of the `user-emacs-directory' variable.") +(defvar minimal-emacs-inhibit-redisplay-during-startup nil + "Suppress redisplay during startup to improve performance. +This prevents visual updates while Emacs initializes. The tradeoff is that you +won't see the progress or activities during the startup process.") -(defvar minimal-emacs-dired-group-directories-first nil - "If non-nil, group directories first in Dired listings.") +(defvar minimal-emacs-inhibit-message-during-startup nil + "Suppress startup messages for a cleaner experience. +This slightly enhances performance. The tradeoff is that you won't be informed +of the progress or any relevant activities during startup.") + +(defvar minimal-emacs-user-directory user-emacs-directory + "Directory beneath minimal-emacs.d files are placed. +Note that this should end with a directory separator.") ;;; Load pre-early-init.el @@ -150,6 +148,7 @@ pre-early-init.el, and post-early-init.el.") (setq custom-theme-directory (expand-file-name "themes/" minimal-emacs-user-directory)) + (setq custom-file (expand-file-name "custom.el" minimal-emacs-user-directory)) ;;; Garbage collection @@ -190,8 +189,7 @@ pre-early-init.el, and post-early-init.el.") (setq features (delq 'native-compile features))) (setq native-comp-warning-on-missing-source minimal-emacs-debug - native-comp-async-report-warnings-errors (or minimal-emacs-debug 'silent) - native-comp-verbose (if minimal-emacs-debug 1 0)) + native-comp-async-report-warnings-errors (or minimal-emacs-debug 'silent)) (setq jka-compr-verbose minimal-emacs-debug) (setq byte-compile-warnings minimal-emacs-debug @@ -218,6 +216,14 @@ pre-early-init.el, and post-early-init.el.") (when minimal-emacs-debug (setq message-log-max 16384)) +;; In PGTK, this timeout introduces latency. Reducing it from the default 0.1 +;; improves responsiveness of childframes and related packages. +(when (boundp 'pgtk-wait-for-event-timeout) + (setq pgtk-wait-for-event-timeout 0.001)) + +;; Disable warnings from the legacy advice API. They aren't useful. +(setq ad-redefinition-action 'accept) + ;;; Performance: Miscellaneous options ;; Font compacting can be very resource-intensive, especially when rendering @@ -429,6 +435,7 @@ this stage of initialization." (push '(tool-bar-lines . 0) default-frame-alist) (setq tool-bar-mode nil)) +(setq default-frame-scroll-bars 'right) (push '(vertical-scroll-bars) default-frame-alist) (push '(horizontal-scroll-bars) default-frame-alist) (setq scroll-bar-mode nil) diff --git a/init.el b/init.el index 6cf44ee..c113e56 100644 --- a/init.el +++ b/init.el @@ -4,7 +4,7 @@ ;; URL: https://github.com/jamescherti/minimal-emacs.d ;; Package-Requires: ((emacs "29.1")) ;; Keywords: maint -;; Version: 1.2.1 +;; Version: 1.3.0 ;; SPDX-License-Identifier: GPL-3.0-or-later ;;; Commentary: @@ -38,11 +38,6 @@ (setq use-short-answers t) (advice-add 'yes-or-no-p :override #'y-or-n-p)) -;;; Features, warnings, and errors - -;; Disable warnings from the legacy advice API. They aren't useful. -(setq ad-redefinition-action 'accept) - ;;; Undo/redo (setq undo-limit (* 13 160000) @@ -85,11 +80,6 @@ (setq visible-bell nil) (setq ring-bell-function #'ignore) -;; In PGTK, this timeout introduces latency. Reducing it from the default 0.1 -;; improves responsiveness of childframes and related packages. -(when (boundp 'pgtk-wait-for-event-timeout) - (setq pgtk-wait-for-event-timeout 0.001)) - ;;; Show-paren (setq show-paren-delay 0.1 @@ -97,12 +87,6 @@ show-paren-when-point-inside-paren t show-paren-when-point-in-periphery t) -;;; Compilation - -(setq compilation-always-kill t - compilation-ask-about-save nil - compilation-scroll-output 'first-error) - ;;; Misc (setq custom-buffer-done-kill t) @@ -157,8 +141,17 @@ (setq uniquify-buffer-name-style 'forward) -(setq comint-prompt-read-only t) -(setq comint-buffer-maximum-size 2048) +;;; comint (general command interpreter in a window) + +(setq ansi-color-for-comint-mode t + comint-prompt-read-only t + comint-buffer-maximum-size 4096) + +;;; Compilation + +(setq compilation-ask-about-save nil + compilation-always-kill t + compilation-scroll-output 'first-error) ;; Skip confirmation prompts when creating a new file or buffer (setq confirm-nonexistent-file-or-buffer nil) @@ -224,7 +217,7 @@ ;; `recentf' is an that maintains a list of recently accessed files. (setq recentf-max-saved-items 300) ; default is 20 (setq recentf-max-menu-items 15) -(setq recentf-auto-cleanup (if (daemonp) 300 'never)) +(setq recentf-auto-cleanup 'mode) ;; Update recentf-exclude (setq recentf-exclude (list "^/\\(?:ssh\\|su\\|sudo\\)?:")) @@ -310,7 +303,8 @@ ;; The blinking cursor is distracting and interferes with cursor settings in ;; some minor modes that try to change it buffer-locally (e.g., Treemacs). -(blink-cursor-mode -1) +(when (bound-and-true-p blink-cursor-mode) + (blink-cursor-mode -1)) ;; Don't blink the paren matching the one at point, it's too distracting. (setq blink-matching-paren nil) @@ -335,7 +329,7 @@ (setq-default left-fringe-width 8) (setq-default right-fringe-width 8) -;; Do not show an arrow at the top/bottomin the fringe and empty lines +;; Disable visual indicators in the fringe for buffer boundaries and empty lines (setq-default indicate-buffer-boundaries nil) (setq-default indicate-empty-lines nil) @@ -408,7 +402,10 @@ dired-filter-verbose nil dired-recursive-deletes 'top dired-recursive-copies 'always - dired-create-destination-dirs 'ask) + dired-vc-rename-file t + dired-create-destination-dirs 'ask + ;; Suppress Dired buffer kill prompt for deleted dirs + dired-clean-confirm-killing-deleted-buffers nil) ;; This is a higher-level predicate that wraps `dired-directory-changed-p' ;; with additional logic. This `dired-buffer-stale-p' predicate handles remote @@ -417,26 +414,9 @@ (setq auto-revert-remote-files nil) (setq dired-auto-revert-buffer 'dired-buffer-stale-p) -(setq dired-vc-rename-file t) - -;; Disable the prompt about killing the Dired buffer for a deleted directory. -(setq dired-clean-confirm-killing-deleted-buffers nil) - ;; dired-omit-mode -(setq dired-omit-verbose nil) -(setq dired-omit-files (concat "\\`[.]\\'")) - -;; Group directories first -(when minimal-emacs-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))))) +(setq dired-omit-verbose nil + dired-omit-files (concat "\\`[.]\\'")) (setq ls-lisp-verbosity nil) (setq ls-lisp-dirs-first t) @@ -561,7 +541,7 @@ (dolist (cmd '(list-timers narrow-to-region upcase-region downcase-region list-threads erase-buffer scroll-left - dired-find-alternate-file)) + dired-find-alternate-file set-goal-column)) (put cmd 'disabled nil)) ;;; Load post init