diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fbeac8c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: jamescherti diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..33a3756 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +--- +# +# This file 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 2, or (at your option) +# any later version. +# +# This file 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 GNU Emacs. If not, see . +# + +name: CI +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + emacs-version: + - 29.1 + - 29.4 + - 30.1 + python-version: + - 3.11 + steps: + # Cask + - uses: actions/checkout@v2 + - uses: purcell/setup-emacs@master + with: + version: ${{ matrix.emacs-version }} + + - name: Compile + run: | + LISP_ERR_ON_WARN="(setq byte-compile-error-on-warn t) (setq byte-compile-warnings '(not obsolete free-vars))" + emacs -batch -L . --eval "(progn $LISP_ERR_ON_WARN )" -f batch-byte-compile early-init.el + emacs -batch -L . --eval "(progn $LISP_ERR_ON_WARN (load \"early-init.el\" nil t t) )" -f batch-byte-compile init.el diff --git a/.gitignore b/.gitignore index f6d83c6..793116d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,12 @@ -/auto-save-list/ -/custom.el -/easysession -/eln-cache/ -/elpa/ -/elpaca/ -/etc/ -/history -/ielm-history.eld -/lisp/ -/projectile-bookmarks.eld -/recentf -/saveplace -/tramp -/straight/ -/themes/ -/transient/ -/tree-sitter/ -/undo-fu-session/ -/var/ -/bookmarks -/bookmarks -/ido.last -/autosave/ -/url/ +* +!README.md +!early-init.el +!init.el +!.images/ +!.github/ +!.LICENSE + +!custom.el +!pre-early-init.el +!post-init.el +!.config.d/ diff --git a/README.md b/README.md index 928bda5..5d6d983 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,1364 @@ Author of [minimal-emacs.d](https://github.com/jamescherti/minimal-emacs.d) sugg As such, post-init.el file only contains the minimal setup code, and at the end loads the files in the `.config.d` folder, in alphanumeric order. In those plug-in files the real magic happens. All the files are heavily documented, with mostly self-explanatory names, so I think it will be redundant to list them here. See the files in `config.d` directory for more. -## Repository setup +- [*minimal-emacs.d* - A Customizable Emacs `init.el` and `early-init.el` that Provides Better Defaults and Faster Startup](#minimal-emacsd---a-customizable-emacs-initel-and-early-initel-that-provides-better-defaults-and-faster-startup) + - [Install minimal-emacs.d](#install-minimal-emacsd) + - [Install minimal-emacs.d into `~/.emacs.d`](#install-minimal-emacsd-into-emacsd) + - [Alternative: Install minimal-emacs.d into `~/.minimal-emacs.d`](#alternative-install-minimal-emacsd-into-minimal-emacsd) + - [Update minimal-emacs.d](#update-minimal-emacsd) + - [Customizations: Never modify init.el and early-init.el. Modify these instead...](#customizations-never-modify-initel-and-early-initel-modify-these-instead) + - [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) + - [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) + - [Activating autosave](#activating-autosave) + - [auto-save-mode (Prevent data loss in case of crashes)](#auto-save-mode-prevent-data-loss-in-case-of-crashes) + - [auto-save-visited-mode (Save file buffers after a few seconds of inactivity)](#auto-save-visited-mode-save-file-buffers-after-a-few-seconds-of-inactivity) + - [Code completion with corfu](#code-completion-with-corfu) + - [Configuring Vertico, Consult, and Embark](#configuring-vertico-consult-and-embark) + - [Code folding](#code-folding) + - [Changing the default theme](#changing-the-default-theme) + - [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) + - [Configuring org-mode](#configuring-org-mode) + - [Inhibit the mouse](#inhibit-the-mouse) + - [Spell checker](#spell-checker) + - [Asynchronous code formatting without cursor disruption](#asynchronous-code-formatting-without-cursor-disruption) + - [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) + - [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) + - [Frequently asked questions](#frequently-asked-questions) + - [How to display Emacs startup duration?](#how-to-display-emacs-startup-duration) + - [How to use MELPA stable?](#how-to-use-melpa-stable) + - [How to load a local lisp file for machine-specific configurations?](#how-to-load-a-local-lisp-file-for-machine-specific-configurations) + - [How to load Emacs customizations?](#how-to-load-emacs-customizations) + - [How to increase gc-cons-threshold?](#how-to-increase-gc-cons-threshold) + - [How to change the outline-mode or outline-minor-mode Ellipsis (...) to (▼)?](#how-to-change-the-outline-mode-or-outline-minor-mode-ellipsis--to-) + - [How to prevent Emacs from loading .dir-locals.el files?](#how-to-prevent-emacs-from-loading-dir-localsel-files) + - [How to make minimal-emacs.d use an environment variable to change ~/.emacs.d to another directory?](#how-to-make-minimal-emacsd-use-an-environment-variable-to-change-emacsd-to-another-directory) + - [Are post-early-init.el and pre-init.el the same file in terms of the logic?](#are-post-early-initel-and-pre-initel-the-same-file-in-terms-of-the-logic) + - [Why is the menu bar disabled by default?](#why-is-the-menu-bar-disabled-by-default) + - [Why did the author develop minimal-emacs.d?](#why-did-the-author-develop-minimal-emacsd) + - [How to keep minimal-emacs.d pre-\*.el and post-\*.el files in a separate directory?](#how-to-keep-minimal-emacsd-pre-el-and-post-el-files-in-a-separate-directory) + - [How to make *minimal-emacs.d* install packages in the early-init phase instead of the init phase?](#how-to-make-minimal-emacsd-install-packages-in-the-early-init-phase-instead-of-the-init-phase) + - [Comments from users](#comments-from-users) + - [Minimal-emacs.d configurations from users](#minimal-emacsd-configurations-from-users) + - [Features](#features) + - [Author and license](#author-and-license) + - [Links](#links) -This section contains a bit of information on how to setup this git repository after you cloned it (in case you want to modify it, ignore otherwise). + -You will need to add a new origin, usually called just 'upstream', that points to the original [minimal-emacs.d](https://github.com/jamescherti/minimal-emacs.d) repository. Then, to pull in any new updates, you will have to pull them form there with a --rebase flag. This will ensure that any modifications to the original repository will be applied on top. +## Install minimal-emacs.d + +- **Important:** Ensure that the `~/.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](https://www.gnu.org/software/emacs/manual/html_node/emacs/Find-Init.html#Find-Init)). **Simply delete the *~/.emacs* and *~/.emacs.el* files avoid this issue.** +- **Debug:** If a package or any other functionality is not working as expected, start Emacs with `emacs --debug-init` to enable debug mode and obtain the backtrace. +- **Prerequisite:** git + +### Install minimal-emacs.d into `~/.emacs.d` + +Execute the following command install this repository into `~/.emacs.d`: +``` +git clone https://github.com/jamescherti/minimal-emacs.d ~/.emacs.d +``` + +### Alternative: Install minimal-emacs.d into `~/.minimal-emacs.d` + +To 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: + +1. Clone the repository into `~/.minimal-emacs.d/` using: + ``` + git clone https://github.com/jamescherti/minimal-emacs.d ~/.minimal-emacs.d + ``` + +2. Start Emacs with the new configuration directory: + ``` + emacs --init-directory ~/.minimal-emacs.d/ + ``` + +## Update 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 +``` + + +## Customizations: Never modify init.el and early-init.el. Modify these instead... +**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: +```elisp +;;; 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.)* + +**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. + +## Debug on error + +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: + +```elisp +(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. + +## Customizations: UI (pre-early-init.el) + +### How to enable the menu-bar, the tool-bar, dialogs, the contextual menu, and tooltips? + +**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``: + +``` emacs-lisp +(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. + +## Customizations: Packages (post-init.el) + +### Optimization: Native Compilation + +Native compilation enhances Emacs performance by converting Elisp code into native machine code, resulting in faster execution and improved responsiveness. + +1. To check if native compilation is enabled, evaluate: + ```elisp + (native-comp-available-p) + ``` + (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: +```emacs-lisp +;; 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 + :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. + (compile-angel-verbose t) + + :config + ;; 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 + ;; `use-package', you'll need to explicitly add `(require 'use-package)` at + ;; the top of your init file. + (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 before they are loaded. + (compile-angel-on-load-mode)) +``` + +### How to activate recentf, savehist, saveplace, and auto-revert? + +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`: +``` emacs-lisp +;; 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) + +;; 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)))) +(add-hook 'kill-emacs-hook #'recentf-cleanup) + +;; 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) + +;; 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) +``` + +### Activating autosave + +#### auto-save-mode (Prevent data loss in case of crashes) + +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`: + +```emacs-lisp +;; Enable `auto-save-mode' to prevent data loss. Use `recover-file' or +;; `recover-session' to restore unsaved changes. +(setq auto-save-default t) + +(setq auto-save-interval 300) +(setq auto-save-timeout 30) +``` + +#### auto-save-visited-mode (Save file buffers after a few seconds of inactivity) + +When `auto-save-visited-mode` is enabled, Emacs will auto-save file-visiting buffers after a certain amount of idle time if the user forgets to save it with `save-buffer` or `C-x s` for example. + +This is different from `auto-save-mode`: `auto-save-mode` periodically saves all modified buffers, creating backup files, including those not associated with a file, while `auto-save-visited-mode` only saves file-visiting buffers after a period of idle time, directly saving to the file itself without creating backup files. + +``` emacs-lisp +(setq auto-save-visited-interval 5) ; Save after 5 seconds if inactivity +(auto-save-visited-mode 1) +``` + +### Code completion with corfu + +[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. + +![](https://github.com/minad/corfu/blob/screenshots/popupinfo-dark.png?raw=true) + +To configure `corfu` and `cape`, add the following to `~/.emacs.d/post-init.el`: +``` emacs-lisp +(use-package corfu + :ensure t + :defer t + :commands (corfu-mode global-corfu-mode) + + :hook ((prog-mode . corfu-mode) + (shell-mode . corfu-mode) + (eshell-mode . corfu-mode)) + + :custom + ;; Hide commands in M-x which do not apply to the current mode. + (read-extended-command-predicate #'command-completion-default-include-p) + ;; Disable Ispell completion function. As an alternative try `cape-dict'. + (text-mode-ispell-word-completion nil) + (tab-always-indent 'complete) + + ;; Enable Corfu + :config + (global-corfu-mode)) + +(use-package cape + :ensure t + :defer t + :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)) +``` + +### Configuring Vertico, Consult, and Embark + +[Vertico](https://github.com/minad/vertico), [Consult](https://github.com/minad/consult), and [Embark](https://github.com/oantolin/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 and powerful environment for managing completions and interactions. + +![](https://github.com/minad/consult/blob/screenshots/consult-grep.gif?raw=true) + +Add the following to `~/.emacs.d/post-init.el` to set up Vertico, Consult, and Embark: +``` emacs-lisp +(use-package vertico + ;; (Note: It is recommended to also enable the savehist package.) + :ensure t + :defer t + :commands vertico-mode + :hook (after-init . vertico-mode)) + +(use-package orderless + ;; 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. + :ensure t + :custom + (completion-styles '(orderless basic)) + (completion-category-defaults nil) + (completion-category-overrides '((file (styles partial-completion))))) + +(use-package marginalia + ;; 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. + :ensure t + :defer t + :commands (marginalia-mode marginalia-cycle) + :hook (after-init . marginalia-mode)) + +(use-package embark + ;; Embark is an Emacs package that acts like a context menu, allowing + ;; users to perform context-sensitive actions on selected items + ;; directly from the completion interface. + :ensure t + :defer t + :commands (embark-act + embark-dwim + embark-export + embark-collect + embark-bindings + embark-prefix-help-command) + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + (setq prefix-help-command #'embark-prefix-help-command) + + :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 + :ensure t + :hook + (embark-collect-mode . consult-preview-at-point-mode)) + +(use-package consult + :ensure t + :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) + ("C-x b" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ("C-x t b" . consult-buffer-other-tab) + ("C-x r b" . consult-bookmark) + ("C-x p b" . consult-project-buffer) + ;; Custom M-# bindings for fast register access + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) + ("C-M-#" . consult-register) + ;; Other custom bindings + ("M-y" . consult-yank-pop) + ;; M-g bindings in `goto-map' + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) + ("M-g g" . consult-goto-line) + ("M-g M-g" . consult-goto-line) + ("M-g o" . consult-outline) + ("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) + ("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) + ("M-s e" . consult-isearch-history) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ;; Minibuffer history + :map minibuffer-local-map + ("M-s" . consult-history) + ("M-r" . consult-history)) + + ;; Enable automatic preview at point in the *Completions* buffer. + :hook (completion-list-mode . consult-preview-at-point-mode) + + :init + ;; Optionally configure the register formatting. This improves the register + (setq register-preview-delay 0.5 + register-preview-function #'consult-register-format) + + ;; Optionally tweak the register preview window. + (advice-add #'register-preview :override #'consult-register-window) + + ;; Use Consult to select xref locations with preview + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + + :config + (consult-customize + consult-theme :preview-key '(:debounce 0.2 any) + consult-ripgrep consult-git-grep consult-grep + 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)) + (setq consult-narrow-key "<")) +``` + +### Code folding + +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. + +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` in Emacs Lisp: + +``` emacs-lisp +(add-hook 'emacs-lisp-mode-hook #'outline-minor-mode) +``` + +For folding based on indentation levels, the **[outline-indent](https://github.com/jamescherti/outline-indent.el)** Emacs package provides a minor mode that enables folding according to the indentation structure: +```elisp +(use-package outline-indent + :ensure t + :defer t + :commands outline-indent-minor-mode + + :custom + (outline-indent-ellipsis " ▼ ") + + :init + ;; The minor mode can also be automatically activated for a certain modes. + (add-hook 'python-mode-hook #'outline-indent-minor-mode) + (add-hook 'python-ts-mode-hook #'outline-indent-minor-mode) + + (add-hook 'yaml-mode-hook #'outline-indent-minor-mode) + (add-hook 'yaml-ts-mode-hook #'outline-indent-minor-mode)) +``` + +In addition to code folding, *outline-indent* also 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 more. + +![](https://raw.githubusercontent.com/jamescherti/outline-indent.el/main/.screenshot2.png) + +### Changing the default theme + +For instance, to switch to a another theme than the default one, add the following to the `~/.emacs.d/post-init.el` file: + +```emacs-lisp +(mapc #'disable-theme custom-enabled-themes) ; Disable all active themes +(load-theme 'modus-operandi t) ; Load the built-in theme +``` + +(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-operandi` +- `modus-operandi-deuteranopia` +- `modus-operandi-tinted` +- `modus-operandi-tritanopia` +- `modus-vivendi` +- `modus-vivendi-deuteranopia` +- `modus-vivendi-tinted` +- `modus-vivendi-tritanopia` +- `tsdh-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.) + +### 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 default undo system in Emacs has two main issues that undo-fu fixes: + +1. **Redo requires two steps**: To redo an action after undoing, you need to press a key twice, which can be annoying and inefficient. +2. **Accidental over-redo**: When redoing, it's easy to go too far back, past the point where you started the undo, which makes it hard to return to the exact state you wanted to restore. + +To install and configure these packages, add the following to `~/.emacs.d/post-init.el`: +```emacs-lisp +;; The undo-fu package is a lightweight wrapper around Emacs' built-in undo +;; system, providing more convenient undo/redo functionality. +(use-package undo-fu + :defer t + :commands (undo-fu-only-undo + undo-fu-only-redo + undo-fu-only-redo-all + undo-fu-disable-checkpoint) + :config + (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 + :defer t + :commands undo-fu-session-global-mode + :hook (after-init . undo-fu-session-global-mode)) +``` + +### Configuring Vim keybindings using Evil? + +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](https://github.com/emacs-evil/evil): + +``` emacs-lisp +;; Required by evil-collection +(eval-when-compile + ;; It has to be defined before evil + (setq evil-want-integration t) + (setq evil-want-keybinding nil)) + +;; Uncomment the following if you are using undo-fu +;; (setq evil-undo-system 'undo-fu) + +;; Vim emulation +(use-package evil + :ensure t + :defer t + :commands (evil-mode evil-define-key) + :hook (after-init . evil-mode)) + +(eval-when-compile + ;; It has to be defined before evil-colllection + (setq evil-collection-setup-minibuffer t)) + +(use-package evil-collection + :after evil + :ensure t + :config + (evil-collection-init)) +``` + +You can also use the [vim-tab-bar](https://github.com/jamescherti/vim-tab-bar.el) Emacs package to `~/.emacs.d/post-init.el` to give the built-in Emacs tab-bar a style similar to Vim's tabbed browsing interface: +``` emacs-lisp +;; Give Emacs tab-bar a style similar to Vim's +(use-package vim-tab-bar + :ensure t + :commands vim-tab-bar-mode + :hook (after-init . vim-tab-bar-mode)) +``` + +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"`: +``` emacs-lisp +(use-package evil-surround + :after evil + :ensure t + :defer t + :commands global-evil-surround-mode + :custom + (evil-surround-pairs-alist + '((?\( . ("(" . ")")) + (?\[ . ("[" . "]")) + (?\{ . ("{" . "}")) + + (?\) . ("(" . ")")) + (?\] . ("[" . "]")) + (?\} . ("{" . "}")) + + (?< . ("<" . ">")) + (?> . ("<" . ">")))) + :hook (after-init . global-evil-surround-mode)) +``` + +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): +``` emacs-lisp +(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 "") + (comment-or-uncomment-region beg end)) + (evil-define-key 'normal 'global (kbd "gc") 'my-evil-comment-or-uncomment)) +``` + +### Configuring LSP Servers with Eglot (built-in) + +To set up Language Server Protocol (LSP) servers using Eglot, you can configure it in your Emacs setup as follows. This configuration ensures minimal disruption from Eglot's progress reporting and optimizes performance by disabling unnecessary logging. + +To configure `eglot`, add the following to `~/.emacs.d/post-init.el`: +``` emacs-lisp +(use-package eglot + :ensure nil + :defer t + :commands (eglot + 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](https://github.com/python-lsp/python-lsp-server), must be installed): + +``` emacs-lisp +(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))))) + +(add-hook 'python-mode-hook #'eglot) +(add-hook 'python-ts-mode-hook #'eglot) +``` + +### Session Management + +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. + +To configure **easysession**, add the following to `~/.emacs.d/post-init.el`: +``` emacs-lisp +(use-package easysession + :ensure t + :defer t + :commands (easysession-switch-to + easysession-save-as + easysession-save-mode + easysession-load-including-geometry) + + :custom + (easysession-mode-line-misc-info t) ; Display the session in the modeline + (easysession-save-interval (* 10 60)) ; Save every 10 minutes + + :init + ;; Key mappings: + ;; C-c l for switching sessions + ;; and C-c s for saving the current session + (global-set-key (kbd "C-c l") 'easysession-switch-to) + (global-set-key (kbd "C-c s") 'easysession-save-as) + + ;; The depth 102 and 103 have been added to to `add-hook' to ensure that the + ;; session is loaded after all other packages. (Using 103/102 is particularly + ;; useful for those using minimal-emacs.d, where some optimizations restore + ;; `file-name-handler-alist` at depth 101 during `emacs-startup-hook`.) + (add-hook 'emacs-startup-hook #'easysession-load-including-geometry 102) + (add-hook 'emacs-startup-hook #'easysession-save-mode 103)) +``` + +### Configuring org-mode + +To configure **org-mode**, add the following to `~/.emacs.d/post-init.el`: +```elisp +(use-package org + :ensure t + :defer t + :commands (org-mode org-version) + :mode + ("\\.org\\'" . org-mode) + :custom + (org-hide-leading-stars t) + (org-startup-indented t) + (org-adapt-indentation nil) + (org-edit-src-content-indentation 0) + (org-startup-truncated t) + (org-fontify-done-headline t) + (org-fontify-todo-headline t) + (org-fontify-whole-heading-line t) + (org-fontify-quote-and-verse-blocks t)) +``` + +### Inhibit the mouse + +The [inhibit-mouse](https://github.com/jamescherti/inhibit-mouse.el) package disables mouse input in Emacs. + +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. + +To configure **inhibit-mouse**, add the following to `~/.emacs.d/post-init.el`: +```emacs-lisp +(use-package inhibit-mouse + :ensure t + :config + (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. + +### Spell checker + +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. + +To configure **flyspell**, add the following to `~/.emacs.d/post-init.el`: +``` emacs-lisp +(use-package ispell + :ensure nil + :defer t + :commands (ispell ispell-minor-mode) + :custom + ;; Set the ispell program name to aspell + (ispell-program-name "aspell") + + ;; 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"))) + +(use-package flyspell + :ensure nil + :defer t + :commands flyspell-mode + :hook + ((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-mode 1))))) + :config + ;; Remove strings from Flyspell + (setq flyspell-prog-text-faces (delq 'font-lock-string-face + flyspell-prog-text-faces)) + + ;; Remove doc from Flyspell + (setq flyspell-prog-text-faces (delq 'font-lock-doc-face + flyspell-prog-text-faces))) +``` + +### 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. + +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`: +```elisp +(use-package apheleia + :ensure t + :defer t + :commands (apheleia-mode + apheleia-global-mode) + :hook ((prog-mode . apheleia-mode))) +``` + +### A better Emacs *help* buffer + +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 +(use-package helpful + :defer t + :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) + :custom + (helpful-max-buffers 7)) +``` + +### Enhancing the Elisp development experience + +To enhance the Elisp development experience, add the following to `~/.emacs.d/post-init.el`: +```emacs-lisp +;; Enables automatic indentation of code while typing +(use-package aggressive-indent + :ensure t + :defer t + :commands aggressive-indent-mode + :hook + (emacs-lisp-mode . aggressive-indent-mode)) + +;; Highlights function and variable definitions in Emacs Lisp mode +(use-package highlight-defined + :ensure t + :defer t + :commands highlight-defined-mode + :hook + (emacs-lisp-mode . highlight-defined-mode)) +``` + +Other optional packages that may be useful include: +```emacs-lisp +;; Prevent parenthesis imbalance +(use-package paredit + :ensure t + :defer t + :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 +;; :ensure t +;; :defer t +;; :commands enhanced-evil-paredit-mode +;; :hook +;; (paredit-mode . enhanced-evil-paredit-mode)) + +;; Displays visible indicators for page breaks +(use-package page-break-lines + :ensure t + :defer t + :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 + :ensure t + :defer t + :commands (elisp-refs-function + elisp-refs-macro + elisp-refs-variable + elisp-refs-special + elisp-refs-symbol)) +``` + +### Showing the tab-bar + +Configure the `tab-bar-show` variable to 1 to display the tab bar exclusively when multiple tabs are open: +```elisp +(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 +(setq custom-file null-device) +``` + +### 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/) + +2. You can also add the following to `~/.emacs.d/post-init.el`: +```emacs-lisp +;; Allow Emacs to upgrade built-in packages, such as Org mode +(setq package-install-upgrade-built-in t) + +;; 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: +;; (display-line-numbers-mode 1) + +(use-package which-key + :ensure nil ; builtin + :defer t + :commands which-key-mode + :hook (after-init . which-key-mode) + :custom + (which-key-idle-delay 1.5) + (which-key-idle-secondary-delay 0.25) + (which-key-add-column-padding 1) + (which-key-max-description-length 40)) + +(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 +(add-hook 'after-init-hook #'display-time-mode) + +;; Paren match highlighting +(add-hook 'after-init-hook #'show-paren-mode) + +;; Track changes in the window configuration, allowing undoing actions such as +;; 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 + (uniquify-buffer-name-style 'reverse) + (uniquify-separator "•") + (uniquify-after-kill-buffer-p t) + (uniquify-ignore-buffers-re "^\\*")) + +;; 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 +(add-hook 'after-init-hook #'window-divider-mode) + +;; 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) + +;; 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) +``` + +It is also recommended to read the following articles: +- [Automating Table of Contents Update for Markdown Documents (e.g., README.md)](https://www.jamescherti.com/emacs-markdown-table-of-contents-update-before-save/) +- [Maintaining proper indentation in indentation-sensitive programming languages](https://www.jamescherti.com/elisp-code-and-emacs-packages-for-maintaining-proper-indentation-in-indentation-sensitive-languages-such-as-python-or-yaml/) + + +## 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? + +[Add the straight.el bootstrap code](https://github.com/radian-software/straight.el?tab=readme-ov-file#getting-started) to `~/.emacs.d/pre-init.el`: +``` emacs-lisp +;; 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)) +``` + +### Configuring elpaca (package manager) + +Add to `~/.emacs.d/pre-early-init.el`: +```elisp +;; 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](https://github.com/progfolio/elpaca?tab=readme-ov-file#installer) to `~/.emacs.d/pre-init.el`: +```elisp +(defvar elpaca-installer-version 0.10) +(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) +(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) +(defvar elpaca-repos-directory (expand-file-name "repos/" 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-package))) +(let* ((repo (expand-file-name "elpaca/" elpaca-repos-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) + (load "./elpaca-autoloads"))) +(add-hook 'after-init-hook #'elpaca-process-queues) +(elpaca `(,@elpaca-order)) + +;; Optional: Install use-package support +;; If you enable elpaca-use-package, some use-package definitions, such as +;; Vertico's, may need modifications. See the following discussion for details: +;; https://github.com/jamescherti/minimal-emacs.d/issues/54 +;; +;; (elpaca elpaca-use-package +;; (elpaca-use-package-mode)) +``` + +## Frequently asked questions + +### How to display Emacs startup duration? + +To measure and display the time taken for Emacs to start, you can use the following Emacs Lisp function. This function will report both the startup duration and the number of garbage collections that occurred during initialization. + +Add the following to your `~/.emacs.d/pre-early-init.el` file: +```emacs-lisp +(defun display-startup-time () + "Display the startup time and number of garbage collections." + (message "Emacs init loaded in %.2f seconds (Full emacs-startup: %.2fs) with %d garbage collections." + (float-time (time-subtract after-init-time before-init-time)) + (time-to-seconds (time-since before-init-time)) + gcs-done)) + +(add-hook 'emacs-startup-hook #'display-startup-time 100) +``` + +(Alternatively, you may use the built-in `M-x emacs-init-time` command to obtain the startup duration. However, `emacs-init-time` does not account for the portion of the startup process that occurs after `after-init-time`.) + +### How to use MELPA stable? + +**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](https://melpa.org/) instead of [MELPA Stable](https://stable.melpa.org/) 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**: +- **MELPA** (preferred) is a rolling release repository for Emacs packages, where packages are continuously updated with the latest commits from their respective development branches, delivering the most current features and bug fixes. While MELPA features the latest changes, most Emacs package developers have learned to maintain a relatively stable master branch, which contributes to MELPA’s overall stability. Furthermore, MELPA includes a broader selection of packages. +- In contrast, **MELPA Stable** is a repository that hosts versioned, tagged releases of packages. However, **MELPA Stable does not guarantee more reliability than MELPA, as its tagged versions may still suffer from issues** like uncoordinated dependencies or incomplete testing, and updates are less frequent, often based on developer discretion rather than every new commit. + +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`: + +```elisp +;; Add melpa-stable to `package-archives' +(push '("melpa-stable" . "https://stable.melpa.org/packages/") package-archives) + +;; This change increases MELPA Stable priority to 70, above MELPA, +;; ensuring that MELPA is preferred for package installations +;; over MELPA Stable. +(customize-set-variable 'package-archive-priorities '(("gnu" . 99) + ("nongnu" . 80) + ("melpa-stable" . 70) + ("melpa" . 0))) +``` + +### How to load a local lisp file for machine-specific configurations? + +Add the following line to the end of your `post-init.el` file: +```lisp +(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`.) + +### How to load Emacs customizations? + +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: + +```elisp +(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). + +### How to increase gc-cons-threshold? + +Add the following to `~/.emacs.d/pre-early-init.el` to ensure that *minimal-emacs.d* restores the specified amount after startup: +```emacs-lisp +(setq minimal-emacs-gc-cons-threshold (* 64 1024 1024)) +``` + +### How to change the outline-mode or outline-minor-mode Ellipsis (...) to (▼)? + +If you want to to change the outline-mode or outline-minor-mode Ellipsis (...) to (▼), use the code snippet in this article: [Changing the Ellipsis (“…”) in outline-mode and outline-minor-mode](https://www.jamescherti.com/emacs-customize-ellipsis-outline-minor-mode/). + +### How to prevent Emacs from loading .dir-locals.el files? + +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`: + +``` emacs-lisp +(setq enable-dir-local-variables nil) +``` + +### How to make minimal-emacs.d use an environment variable to change ~/.emacs.d to another directory? + +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: +```emacs-lisp +;; 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"))) +``` + +### Are post-early-init.el and pre-init.el the same file in terms of the logic? + +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. + +### Why is the menu bar disabled by default? + +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`: +```elisp +(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`: +```elisp +(setq minimal-emacs-ui-features '(context-menu tool-bar menu-bar dialogs tooltips)) +``` + +### Why did the author develop minimal-emacs.d? + +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. + +### How to keep minimal-emacs.d pre-\*.el and post-\*.el files in a separate directory? + +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: +```elisp +(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.). + +### How to make *minimal-emacs.d* install packages in the early-init phase instead of the init phase? + +To install and load packages during the early-init phase, add the following to `post-early-init.el`: + +```elisp +(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 +(progn + (package-initialize) + (unless package-archive-contents + (package-refresh-contents)) + (unless (package-installed-p 'use-package) + (package-install 'use-package)) + (eval-when-compile + (require 'use-package))) + +;; TODO: Add your use-package code here +``` + +A drawback of using the early-init phase instead of init is that if a package fails (e.g, due to a network issue), no output will be displayed in the Emacs GUI. You will need to open a terminal to view Emacs's stdout for error messages. + +### 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. +- [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. +- [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." + +### Minimal-emacs.d configurations from users + +- [Victor Dorneanu's minimal-emacs.d configuration](https://github.com/dorneanu/dotfiles/blob/master/minimal-emacs/config.org) + +- [Flowfx emacs.d](https://codeberg.org/flowfx/emacs.d) + +- [John B. Sigman: A literate Emacs configuration with some parts inspired by minimal-emacs.d](https://www.johnsigman.com/projects/emacs_config/) + +- [Mark Norton's minimal-emacs.d configuration](https://github.com/Remillard/minimal-emacs.d/tree/develop) + +Please share your configuration. It could serve as inspiration for other users. + +## Features + +1. **Performance Improvements:** + - Increases the amount read from processes in a single chunk. + - Prefers loading newer compiled files. + - Reduces startup screen and message noise, including removing the "For information about GNU Emacs..." message. + - Configures Emacs to start with a scratch buffer in `fundamental-mode` to shave seconds off startup time. + - Delays garbage collection during startup to improve performance and resets it to a more reasonable value once Emacs has started. + - Customizes `file-name-handler-alist` for improved startup time and package load time (Special thanks to the Doom Emacs developers for the `file-name-handler-alist` optimizations; This function have been inspired by their project and will contribute to improving vanilla Emacs configurations.) + - Reduces rendering workload by not rendering cursors or regions in non-focused windows. + - Disables warnings from the legacy advice API and suppresses warnings about aliased variables. + - Avoids unnecessary excessive UI updates. + - Disables font compacting to avoid high memory usage. + - Defer tool bar setup + - Unset command line options irrelevant to the current OS + +2. **Native Compilation and Byte Compilation:** + - Configures native compilation and byte compilation settings + - Suppresses compiler warnings and errors during native compilation. + +4. **UI Element Management:** + - Disables the startup screen and messages, including menu bar, tool bar, and scroll bars. + - Configures Emacs to avoid resizing frames and minimizes unnecessary UI updates. + +5. **Package Management:** + - Configures package archives and priorities for MELPA, ELPA, and other repositories. + +6. **Customizable Initialization Files:** + - Supports additional configuration files (`pre-init.el`, `post-init.el`, `pre-early-init.el`, and `post-early-init.el`) to allow further customization at different stages of the startup process. + +7. **File Management:** + - Manages auto-save and backup files, including backup options and version control settings. + +8. **User Experience Enhancements:** + - Configures user interface settings such as cursor behavior, scrolling, and response to prompts. + - Disables beeping and blinking to avoid distractions. + +9. **Buffer and Window Configuration:** + - Sets default fringe widths and buffer boundaries. + - Configures smooth scrolling and cursor behavior for a more seamless editing experience. + +10. **Miscellaneous** + - Verifies that the Emacs configuration has loaded successfully and issues a warning if there are any issues. + - Configure and optimize settings for Eglot, recentf, savehist, auto-save, and others without enabling the modes themselves. This modifies the behavior and preferences to improve performance and usability. + - Configure Ediff to use a single frame and split windows horizontally + +## Author and license + +The *minimal-emacs.d* project has been written by [James Cherti](https://www.jamescherti.com/) and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version. + +Copyright (C) 2024-2025 [James Cherti](https://www.jamescherti.com) + +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). + +## Links + +- [minimal-emacs.d @GitHub](https://github.com/jamescherti/minimal-emacs.d) + +Other Emacs packages by the same author: +- [compile-angel.el](https://github.com/jamescherti/compile-angel.el): **Speed up Emacs!** This package guarantees that all .el files are both byte-compiled and native-compiled, which significantly speeds up Emacs. +- [outline-indent.el](https://github.com/jamescherti/outline-indent.el): An Emacs package that provides a minor mode that enables code folding and outlining based on indentation levels for various indentation-based text files, such as YAML, Python, and other indented text files. +- [easysession.el](https://github.com/jamescherti/easysession.el): Easysession is lightweight Emacs session manager that can persist and restore file editing buffers, indirect buffers/clones, Dired buffers, the tab-bar, and the Emacs frames (with or without the Emacs frames size, width, and height). +- [vim-tab-bar.el](https://github.com/jamescherti/vim-tab-bar.el): Make the Emacs tab-bar Look Like Vim's Tab Bar. +- [elispcomp](https://github.com/jamescherti/elispcomp): A command line tool that allows compiling Elisp code directly from the terminal or from a shell script. It facilitates the generation of optimized .elc (byte-compiled) and .eln (native-compiled) files. +- [tomorrow-night-deepblue-theme.el](https://github.com/jamescherti/tomorrow-night-deepblue-theme.el): The Tomorrow Night Deepblue Emacs theme is a beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette that is pleasing to the eyes. It features a deep blue background color that creates a calming atmosphere. The theme is also a great choice for those who miss the blue themes that were trendy a few years ago. +- [Ultyas](https://github.com/jamescherti/ultyas/): A command-line tool designed to simplify the process of converting code snippets from UltiSnips to YASnippet format. +- [dir-config.el](https://github.com/jamescherti/dir-config.el): Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings. +- [flymake-bashate.el](https://github.com/jamescherti/flymake-bashate.el): A package that provides a Flymake backend for the bashate Bash script style checker. +- [flymake-ansible-lint.el](https://github.com/jamescherti/flymake-ansible-lint.el): An Emacs package that offers a Flymake backend for ansible-lint. +- [inhibit-mouse.el](https://github.com/jamescherti/inhibit-mouse.el): A package that disables mouse input in Emacs, offering a simpler and faster alternative to the disable-mouse package. +- [quick-sdcv.el](https://github.com/jamescherti/quick-sdcv.el): This package enables Emacs to function as an offline dictionary by using the sdcv command-line tool directly within Emacs. +- [enhanced-evil-paredit.el](https://github.com/jamescherti/enhanced-evil-paredit.el): An Emacs package that prevents parenthesis imbalance when using *evil-mode* with *paredit*. It intercepts *evil-mode* commands such as delete, change, and paste, blocking their execution if they would break the parenthetical structure. diff --git a/custom.el b/custom.el new file mode 100644 index 0000000..e8a0fcb --- /dev/null +++ b/custom.el @@ -0,0 +1,12 @@ +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(auth-source-save-behavior nil)) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + ) diff --git a/early-init.el b/early-init.el index b9f656f..fd949cc 100644 --- a/early-init.el +++ b/early-init.el @@ -1,10 +1,10 @@ -;;; early-init.el --- Early Init -*- no-byte-compile: t; lexical-binding: t; -*- +;;; early-init.el --- Early Init -*- lexical-binding: t; -*- ;; Author: James Cherti ;; URL: https://github.com/jamescherti/minimal-emacs.d ;; Package-Requires: ((emacs "29.1")) ;; Keywords: maint -;; Version: 1.1.2 +;; Version: 1.2.0 ;; SPDX-License-Identifier: GPL-3.0-or-later ;;; Commentary: @@ -17,54 +17,60 @@ ;;; Variables (defvar minimal-emacs-ui-features '() - "List of user interface features to disable in minimal Emacs setup. - + "List of user interface features to enable in minimal Emacs setup. This variable holds a list Emacs UI features that can be enabled: -- `context-menu`: Enables the context menu in graphical environments. -- `tool-bar`: Enables the tool bar in graphical environments. -- `menu-bar`: Enables the menu bar in graphical environments. -- `dialogs`: Enables both file dialogs and dialog boxes. -- `tooltips`: Enables tooltips. - -Each feature in the list corresponds to a specific UI component that can be -turned on.") +- context-menu (Enables the context menu in graphical environments.) +- tool-bar (Enables the tool bar in graphical environments.) +- menu-bar (Enables the menu bar in graphical environments.) +- dialogs (Enables both file dialogs and dialog boxes.) +- tooltips (Enables tooltips.)") (defvar minimal-emacs-frame-title-format "%b – Emacs" "Template for displaying the title bar of visible and iconified frame.") -(defvar minimal-emacs-debug nil +(defvar minimal-emacs-debug (bound-and-true-p init-file-debug) "Non-nil to enable debug.") -(defvar minimal-emacs-gc-cons-threshold (* 16 1024 1024) - "The value of `gc-cons-threshold' after Emacs startup.") +(defvar minimal-emacs-gc-cons-threshold (* 32 1024 1024) + "Value to set `gc-cons-threshold' to after Emacs startup. +Ignored if `minimal-emacs-optimize-startup-gc' is nil.") (defvar minimal-emacs-optimize-startup-gc t "If non-nil, increase `gc-cons-threshold' during startup to reduce pauses. After Emacs finishes loading, `gc-cons-threshold' is restored to the value stored in `minimal-emacs--restore-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 +lookups during Emacs startup.") + +(defvar minimal-emacs-disable-mode-line-during-startup t + "Disable the mode line during startup. +This reduces visual clutter and slightly enhances startup performance. The +tradeoff is that the mode line is hidden during the startup phase.") + (defvar minimal-emacs-package-initialize-and-refresh t "Whether to automatically initialize and refresh packages. When set to non-nil, Emacs will automatically call `package-initialize' and `package-refresh-contents' to set up and update the package system.") -(defvar minimal-emacs-inhibit-redisplay-during-startup nil - "Suppress redisplay during startup to improve performance. -This prevents visual updates while Emacs initializes, leading to a cleaner and -faster startup. 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. -By disabling startup messages, this slightly enhances performance and provides a -cleaner startup. The tradeoff is that you won't be informed of the progress or -any relevant activities during startup.") - -(defvar minimal-emacs-disable-mode-line-during-startup nil - "Disable the mode line during startup to improve performance. -This reduces visual clutter and slightly enhances startup performance. The -tradeoff is that the mode line is hidden during the startup phase, giving a more -minimalistic appearance during startup.") +(defvar minimal-emacs-setup-native-compilation t + "If non-nil, enable and configure native compilation. +When enabled, this variable sets the following: +- `native-comp-deferred-compilation' +- `native-comp-jit-compilation' +- `package-native-compile'") (defvar minimal-emacs-user-directory user-emacs-directory "The default value of the `user-emacs-directory' variable.") @@ -73,14 +79,48 @@ minimalistic appearance during startup.") ;; Prefer loading newer compiled files (setq load-prefer-newer t) +(setq debug-on-error minimal-emacs-debug) + +(defvar minimal-emacs--success nil) +(defun minimal-emacs--check-success () + "Verify that the Emacs configuration has loaded successfully." + (unless minimal-emacs--success + (cond + ((or (file-exists-p (expand-file-name "~/.emacs.el")) + (file-exists-p (expand-file-name "~/.emacs"))) + (error "Emacs ignored loading 'init.el'. Please ensure that files such as ~/.emacs or ~/.emacs.el do not exist, as they may be preventing Emacs from loading the 'init.el' file")) + + (t + (error "Configuration error. Debug by starting Emacs with: emacs --debug-init"))))) +(add-hook 'emacs-startup-hook #'minimal-emacs--check-success 102) + +(defvar minimal-emacs-load-compiled-init-files nil + "If non-nil, attempt to load byte-compiled .elc for init files. +This will enable minimal-emacs to load byte-compiled or possibly native-compiled +init files for the following initialization files: pre-init.el, post-init.el, +pre-early-init.el, and post-early-init.el.") + +(defun minimal-emacs--remove-el-file-suffix (filename) + "Remove the Elisp file suffix from FILENAME and return it (.el, .el.gz...)." + (let ((suffixes (mapcar (lambda (ext) (concat ".el" ext)) + load-file-rep-suffixes))) + (catch 'done + (dolist (suffix suffixes filename) + (when (string-suffix-p suffix filename) + (setq filename (substring filename 0 (- (length suffix)))) + (throw 'done t)))) + filename)) (defun minimal-emacs-load-user-init (filename) "Execute a file of Lisp code named FILENAME." - (let ((user-init-file - (expand-file-name filename - minimal-emacs-user-directory))) - (when (file-exists-p user-init-file) - (load user-init-file nil t t)))) + (let ((init-file (expand-file-name filename + minimal-emacs-user-directory))) + (if (not minimal-emacs-load-compiled-init-files) + (load init-file :no-error :no-message :nosuffix) + ;; Remove the file suffix (.el, .el.gz, etc.) to let the `load' function + ;; select between .el and .elc files. + (setq init-file (minimal-emacs--remove-el-file-suffix init-file)) + (load init-file :no-error :no-message)))) (minimal-emacs-load-user-init "pre-early-init.el") @@ -92,172 +132,216 @@ minimalistic appearance during startup.") ;; Garbage collection significantly affects startup times. This setting delays ;; garbage collection during startup but will be reset later. +(setq garbage-collection-messages minimal-emacs-debug) + +(defun minimal-emacs--restore-gc-cons-threshold () + "Restore `minimal-emacs-gc-cons-threshold'." + (setq gc-cons-threshold minimal-emacs-gc-cons-threshold)) + (when minimal-emacs-optimize-startup-gc (setq gc-cons-threshold most-positive-fixnum) - - (defun minimal-emacs--restore-gc-cons-threshold () - "Restore `minimal-emacs-gc-cons-threshold'." - (setq gc-cons-threshold minimal-emacs-gc-cons-threshold)) (add-hook 'emacs-startup-hook #'minimal-emacs--restore-gc-cons-threshold 105)) -;;; Misc - -(set-language-environment "UTF-8") - -;; Set-language-environment sets default-input-method, which is unwanted. -(setq default-input-method nil) - -;;; Performance - -;; Font compacting can be very resource-intensive, especially when rendering -;; icon fonts on Windows. This will increase memory usage. -(setq inhibit-compacting-font-caches t) - -(unless (daemonp) - (let ((old-value (default-toplevel-value 'file-name-handler-alist))) - ;; Determine the state of bundled libraries using calc-loaddefs.el. - ;; If compressed, retain the gzip handler in `file-name-handler-alist`. - ;; If compiled or neither, omit the gzip handler during startup for - ;; improved startup and package load time. - (set-default-toplevel-value 'file-name-handler-alist - (if (eval-when-compile - (locate-file-internal "calc-loaddefs.el" - load-path)) - nil - (list (rassq 'jka-compr-handler old-value)))) - ;; Ensure the new value persists through any current let-binding. - (put 'file-name-handler-alist 'initial-value old-value) - ;; Emacs processes command-line files very early in startup. These files may - ;; include special paths like TRAMP paths, so restore - ;; `file-name-handler-alist' for this stage of initialization. - (define-advice command-line-1 (:around (fn args-left) respect-file-handlers) - (let ((file-name-handler-alist - (if args-left old-value file-name-handler-alist))) - (funcall fn args-left))) - - ;; Restore the old value to reset it as needed. - (defun minimal-emacs--restore-file-name-handler-alist () - "Restore `file-name-handler-alist'." - (set-default-toplevel-value - 'file-name-handler-alist - ;; Merge instead of overwrite to preserve any changes made since startup. - (delete-dups (append file-name-handler-alist old-value)))) - - (add-hook - 'emacs-startup-hook #'minimal-emacs--restore-file-name-handler-alist 101)) - - (unless noninteractive - (unless minimal-emacs-debug - (when minimal-emacs-inhibit-redisplay-during-startup - ;; Suppress redisplay and redraw during startup to avoid delays and - ;; prevent flashing an unstyled Emacs frame. - (defun minimal-emacs--reset-inhibit-redisplay () - (setq-default inhibit-redisplay nil) - (remove-hook 'post-command-hook #'minimal-emacs--reset-inhibit-redisplay)) - - (setq-default inhibit-redisplay t) - (add-hook 'post-command-hook #'minimal-emacs--reset-inhibit-redisplay -100)) - - (when minimal-emacs-inhibit-message-during-startup - (defun minimal-emacs--reset-inhibit-message () - (setq-default inhibit-message nil) - (remove-hook 'post-command-hook #'minimal-emacs--reset-inhibit-message)) - - (setq-default inhibit-message t) - (add-hook 'post-command-hook #'minimal-emacs--reset-inhibit-message -100)) - - (put 'mode-line-format - 'initial-value (default-toplevel-value 'mode-line-format)) - (when minimal-emacs-disable-mode-line-during-startup - (setq-default mode-line-format nil) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (setq mode-line-format nil)))) - - (defun minimal-emacs--startup-load-user-init-file (fn &rest args) - "Advice for startup--load-user-init-file to reset mode-line-format." - (unwind-protect - ;; Start up as normal - (apply fn args) - ;; If we don't undo inhibit-{message, redisplay} and there's an - ;; error, we'll see nothing but a blank Emacs frame. - (when minimal-emacs-inhibit-message-during-startup - (setq-default inhibit-message nil)) - (when minimal-emacs-inhibit-redisplay-during-startup - (setq-default inhibit-redisplay nil)) - ;; Restore the mode-line - (when minimal-emacs-disable-mode-line-during-startup - (unless (default-toplevel-value 'mode-line-format) - (setq-default mode-line-format (get 'mode-line-format - 'initial-value)))))) - - (advice-add 'startup--load-user-init-file :around - #'minimal-emacs--startup-load-user-init-file)) - - ;; Without this, Emacs will try to resize itself to a specific column size - (setq frame-inhibit-implied-resize t) - - ;; A second, case-insensitive pass over `auto-mode-alist' is time wasted. - ;; No second pass of case-insensitive search over auto-mode-alist. - (setq auto-mode-case-fold nil) - - ;; Reduce *Message* noise at startup. An empty scratch buffer (or the - ;; dashboard) is more than enough, and faster to display. - (setq inhibit-startup-screen t - inhibit-startup-echo-area-message user-login-name) - (setq initial-buffer-choice nil - inhibit-startup-buffer-menu t - inhibit-x-resources t) - - ;; Disable bidirectional text scanning for a modest performance boost. - (setq-default bidi-display-reordering 'left-to-right - bidi-paragraph-direction 'left-to-right) - - ;; Give up some bidirectional functionality for slightly faster re-display. - (setq bidi-inhibit-bpa t) - - ;; Remove "For information about GNU Emacs..." message at startup - (advice-add #'display-startup-echo-area-message :override #'ignore) - - ;; Suppress the vanilla startup screen completely. We've disabled it with - ;; `inhibit-startup-screen', but it would still initialize anyway. - (advice-add #'display-startup-screen :override #'ignore) - - ;; Shave seconds off startup time by starting the scratch buffer in - ;; `fundamental-mode' - (setq initial-major-mode 'fundamental-mode - initial-scratch-message nil) - - (unless minimal-emacs-debug - ;; Unset command line options irrelevant to the current OS. These options - ;; are still processed by `command-line-1` but have no effect. - (unless (eq system-type 'darwin) - (setq command-line-ns-option-alist nil)) - (unless (memq initial-window-system '(x pgtk)) - (setq command-line-x-option-alist nil))))) - ;;; Native compilation and Byte compilation (if (and (featurep 'native-compile) (fboundp 'native-comp-available-p) (native-comp-available-p)) ;; Activate `native-compile' - (setq native-comp-jit-compilation t - native-comp-deferred-compilation t ; Obsolete since Emacs 29.1 - package-native-compile t) + (setq native-comp-deferred-compilation minimal-emacs-setup-native-compilation + native-comp-jit-compilation minimal-emacs-setup-native-compilation + package-native-compile minimal-emacs-setup-native-compilation) ;; Deactivate the `native-compile' feature if it is not available (setq features (delq 'native-compile features))) -;; Suppress compiler warnings and don't inundate users with their popups. -(setq native-comp-async-report-warnings-errors - (or minimal-emacs-debug 'silent)) -(setq native-comp-warning-on-missing-source minimal-emacs-debug) +(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)) -(setq debug-on-error minimal-emacs-debug - jka-compr-verbose minimal-emacs-debug) +(setq jka-compr-verbose minimal-emacs-debug) +(setq byte-compile-warnings minimal-emacs-debug + byte-compile-verbose minimal-emacs-debug) -(setq byte-compile-warnings minimal-emacs-debug) -(setq byte-compile-verbose minimal-emacs-debug) +;;; Miscellaneous + +(set-language-environment "UTF-8") + +;; Set-language-environment sets default-input-method, which is unwanted. +(setq default-input-method nil) + +;; Increase how much is read from processes in a single chunk +(setq read-process-output-max (* 2 1024 1024)) ; 1024kb + +(setq process-adaptive-read-buffering nil) + +;; Don't ping things that look like domain names. +(setq ffap-machine-p-known 'reject) + +(setq warning-minimum-level (if minimal-emacs-debug :warning :error)) +(setq warning-suppress-types '((lexical-binding))) + +;;; Performance: Miscellaneous options + +;; Font compacting can be very resource-intensive, especially when rendering +;; icon fonts on Windows. This will increase memory usage. +(setq inhibit-compacting-font-caches t) + +(when (and (not (daemonp)) (not noninteractive)) + ;; Without this, Emacs will try to resize itself to a specific column size + (setq frame-inhibit-implied-resize t) + + ;; A second, case-insensitive pass over `auto-mode-alist' is time wasted. + ;; No second pass of case-insensitive search over auto-mode-alist. + (setq auto-mode-case-fold nil) + + ;; Reduce *Message* noise at startup. An empty scratch buffer (or the + ;; dashboard) is more than enough, and faster to display. + (setq inhibit-startup-screen t + inhibit-startup-echo-area-message user-login-name) + (setq initial-buffer-choice nil + inhibit-startup-buffer-menu t + inhibit-x-resources t) + + ;; Disable bidirectional text scanning for a modest performance boost. + (setq-default bidi-display-reordering 'left-to-right + bidi-paragraph-direction 'left-to-right) + + ;; Give up some bidirectional functionality for slightly faster re-display. + (setq bidi-inhibit-bpa t) + + ;; Remove "For information about GNU Emacs..." message at startup + (advice-add 'display-startup-echo-area-message :override #'ignore) + + ;; Suppress the vanilla startup screen completely. We've disabled it with + ;; `inhibit-startup-screen', but it would still initialize anyway. + (advice-add 'display-startup-screen :override #'ignore) + + ;; Shave seconds off startup time by starting the scratch buffer in + ;; `fundamental-mode' + (setq initial-major-mode 'fundamental-mode + initial-scratch-message nil) + + (unless minimal-emacs-debug + ;; Unset command line options irrelevant to the current OS. These options + ;; are still processed by `command-line-1` but have no effect. + (unless (eq system-type 'darwin) + (setq command-line-ns-option-alist nil)) + (unless (memq initial-window-system '(x pgtk)) + (setq command-line-x-option-alist nil)))) + +;;; Performance: File-name-handler-alist + +(defvar minimal-emacs--old-file-name-handler-alist (default-toplevel-value + 'file-name-handler-alist)) + +(defun minimal-emacs--respect-file-handlers (fn args-left) + "Respect file handlers. +FN is the function and ARGS-LEFT is the same argument as `command-line-1'. +Emacs processes command-line files very early in startup. These files may +include special paths like TRAMP paths, so restore `file-name-handler-alist' for +this stage of initialization." + (let ((file-name-handler-alist (if args-left + minimal-emacs--old-file-name-handler-alist + file-name-handler-alist))) + (funcall fn args-left))) + +(defun minimal-emacs--restore-file-name-handler-alist () + "Restore `file-name-handler-alist'." + (set-default-toplevel-value + 'file-name-handler-alist + ;; Merge instead of overwrite to preserve any changes made since startup. + (delete-dups (append file-name-handler-alist + minimal-emacs--old-file-name-handler-alist)))) + +(when (and minimal-emacs-optimize-file-name-handler-alist + (not (daemonp)) + (not minimal-emacs-debug)) + ;; Determine the state of bundled libraries using calc-loaddefs.el. If + ;; compressed, retain the gzip handler in `file-name-handler-alist`. If + ;; compiled or neither, omit the gzip handler during startup for improved + ;; startup and package load time. + (set-default-toplevel-value + 'file-name-handler-alist + (if (locate-file-internal "calc-loaddefs.el" load-path) + nil + (list (rassq 'jka-compr-handler + minimal-emacs--old-file-name-handler-alist)))) + + ;; Ensure the new value persists through any current let-binding. + (put 'file-name-handler-alist 'initial-value + minimal-emacs--old-file-name-handler-alist) + + ;; Emacs processes command-line files very early in startup. These files may + ;; include special paths TRAMP. Restore `file-name-handler-alist'. + (advice-add 'command-line-1 :around #'minimal-emacs--respect-file-handlers) + + (add-hook 'emacs-startup-hook #'minimal-emacs--restore-file-name-handler-alist + 101)) + +;;; Performance: Inhibit redisplay + +(defun minimal-emacs--reset-inhibit-redisplay () + "Reset inhibit redisplay." + (setq-default inhibit-redisplay nil) + (remove-hook 'post-command-hook #'minimal-emacs--reset-inhibit-redisplay)) + +(when (and minimal-emacs-inhibit-redisplay-during-startup + (not (daemonp)) + (not noninteractive) + (not minimal-emacs-debug)) + ;; Suppress redisplay and redraw during startup to avoid delays and + ;; prevent flashing an unstyled Emacs frame. + (setq-default inhibit-redisplay t) + (add-hook 'post-command-hook #'minimal-emacs--reset-inhibit-redisplay -100)) + +;;; Performance: Inhibit message + +(defun minimal-emacs--reset-inhibit-message () + "Reset inhibit message." + (setq-default inhibit-message nil) + (remove-hook 'post-command-hook #'minimal-emacs--reset-inhibit-message)) + +(when (and minimal-emacs-inhibit-message-during-startup + (not (daemonp)) + (not noninteractive) + (not minimal-emacs-debug)) + (setq-default inhibit-message t) + (add-hook 'post-command-hook #'minimal-emacs--reset-inhibit-message -100)) + +;;; Performance: Disable mode-line during startup + +(when (and minimal-emacs-disable-mode-line-during-startup + (not (daemonp)) + (not noninteractive) + (not minimal-emacs-debug)) + (put 'mode-line-format + 'initial-value (default-toplevel-value 'mode-line-format)) + (setq-default mode-line-format nil) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (setq mode-line-format nil)))) + +;;; Restore values + +(defun minimal-emacs--startup-load-user-init-file (fn &rest args) + "Advice to reset `mode-line-format'. FN and ARGS are the function and args." + (unwind-protect + ;; Start up as normal + (apply fn args) + ;; If we don't undo inhibit-{message, redisplay} and there's an error, we'll + ;; see nothing but a blank Emacs frame. + (when minimal-emacs-inhibit-message-during-startup + (setq-default inhibit-message nil)) + (when minimal-emacs-inhibit-redisplay-during-startup + (setq-default inhibit-redisplay nil)) + ;; Restore the mode-line + (when minimal-emacs-disable-mode-line-during-startup + (unless (default-toplevel-value 'mode-line-format) + (setq-default mode-line-format (get 'mode-line-format + 'initial-value)))))) + +(advice-add 'startup--load-user-init-file :around + #'minimal-emacs--startup-load-user-init-file) ;;; UI elements @@ -277,17 +361,23 @@ minimalistic appearance during startup.") (unless (memq window-system '(mac ns)) (setq menu-bar-mode nil))) -(unless (daemonp) - (unless noninteractive - (when (fboundp 'tool-bar-setup) - ;; Temporarily override the tool-bar-setup function to prevent it from - ;; running during the initial stages of startup - (advice-add #'tool-bar-setup :override #'ignore) - (define-advice startup--load-user-init-file - (:after (&rest _) minimal-emacs-setup-toolbar) - (advice-remove #'tool-bar-setup #'ignore) - (when tool-bar-mode - (tool-bar-setup)))))) +(defun minimal-emacs--setup-toolbar (&rest _) + "Setup the toolbar." + (when (fboundp 'tool-bar-setup) + (advice-remove 'tool-bar-setup #'ignore) + (when (bound-and-true-p tool-bar-mode) + (funcall 'tool-bar-setup)))) + +(when (and (not (daemonp)) + (not noninteractive)) + (when (fboundp 'tool-bar-setup) + ;; Temporarily override the tool-bar-setup function to prevent it from + ;; running during the initial stages of startup + (advice-add 'tool-bar-setup :override #'ignore) + + (advice-add 'startup--load-user-init-file :after + #'minimal-emacs--setup-toolbar))) + (unless (memq 'tool-bar minimal-emacs-ui-features) (push '(tool-bar-lines . 0) default-frame-alist) (setq tool-bar-mode nil)) @@ -306,12 +396,17 @@ minimalistic appearance during startup.") (setq use-file-dialog nil) (setq use-dialog-box nil)) +;;; Security +(setq gnutls-verify-error t) ; Prompts user if there are certificate issues +(setq tls-checktrust t) ; Ensure SSL/TLS connections undergo trust verification +(setq gnutls-min-prime-bits 3072) ; Stronger GnuTLS encryption + ;;; package.el (setq use-package-compute-statistics minimal-emacs-debug) ;; Setting use-package-expand-minimally to (t) results in a more compact output ;; that emphasizes performance over clarity. -(setq use-package-expand-minimally (not noninteractive)) +(setq use-package-expand-minimally (not minimal-emacs-debug)) (setq use-package-minimum-reported-time (if minimal-emacs-debug 0 0.1)) (setq use-package-verbose minimal-emacs-debug) @@ -319,17 +414,19 @@ minimalistic appearance during startup.") (setq use-package-always-ensure t) (setq use-package-enable-imenu-support t) (setq package-archives '(("melpa" . "https://melpa.org/packages/") - ("melpa-stable" . "https://stable.melpa.org/packages/") ("gnu" . "https://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/"))) (customize-set-variable 'package-archive-priorities '(("gnu" . 99) ("nongnu" . 80) - ("stable" . 70) - ("melpa" . 0))) + ("melpa" . 70))) ;;; Load post-early-init.el (minimal-emacs-load-user-init "post-early-init.el") (provide 'early-init) +;; Local variables: +;; byte-compile-warnings: (not obsolete free-vars) +;; End: + ;;; early-init.el ends here diff --git a/init.el b/init.el index 7101c0b..38fd499 100644 --- a/init.el +++ b/init.el @@ -1,10 +1,10 @@ -;;; init.el --- Init -*- no-byte-compile: t; lexical-binding: t; -*- +;;; init.el --- Init -*- lexical-binding: t; -*- ;; Author: James Cherti ;; URL: https://github.com/jamescherti/minimal-emacs.d ;; Package-Requires: ((emacs "29.1")) ;; Keywords: maint -;; Version: 1.1.2 +;; Version: 1.2.0 ;; SPDX-License-Identifier: GPL-3.0-or-later ;;; Commentary: @@ -15,41 +15,47 @@ ;;; Code: ;;; Load pre-init.el -(minimal-emacs-load-user-init "pre-init.el") +(if (fboundp 'minimal-emacs-load-user-init) + (minimal-emacs-load-user-init "pre-init.el") + (error "The early-init.el file failed to loaded")) ;;; Before package -;; Increase how much is read from processes in a single chunk -(setq read-process-output-max (* 1024 1024)) ; 1024kb +;; Ask the user whether to terminate asynchronous compilations on exit. +;; This prevents native compilation from leaving temporary files in /tmp. +(setq native-comp-async-query-on-exit t) -;; Don't ping things that look like domain names. -(setq ffap-machine-p-known 'reject) +;; Allow for shorter responses: "y" for yes and "n" for no. +(setq read-answer-short t) +(if (boundp 'use-short-answers) + (setq use-short-answers t) + (advice-add 'yes-or-no-p :override #'y-or-n-p)) + +;;; Undo/redo + +(setq undo-limit (* 13 160000) + undo-strong-limit (* 13 240000) + undo-outer-limit (* 13 24000000)) ;;; package.el (when (bound-and-true-p minimal-emacs-package-initialize-and-refresh) ;; Initialize and refresh package contents again if needed (package-initialize) - (unless package-archive-contents - (package-refresh-contents)) - ;; Install use-package if necessary (unless (package-installed-p 'use-package) + (unless (seq-empty-p package-archive-contents) + (package-refresh-contents)) (package-install 'use-package)) - ;; Ensure use-package is available at compile time - (eval-when-compile - (require 'use-package))) - -;; Ensure the 'use-package' package is installed and loaded + ;; Ensure use-package is available + (require 'use-package)) ;;; Features, warnings, and errors ;; Disable warnings from the legacy advice API. They aren't useful. (setq ad-redefinition-action 'accept) -(setq warning-suppress-types '((lexical-binding))) - ;;; Minibuffer ;; Allow nested minibuffers @@ -65,11 +71,6 @@ ;; By default, Emacs "updates" its ui more often than it needs to (setq idle-update-delay 1.0) -;; Allow for shorter responses: "y" for yes and "n" for no. -(setq read-answer-short t) -(if (boundp 'use-short-answers) - (setq use-short-answers t) - (advice-add #'yes-or-no-p :override #'y-or-n-p)) (defalias #'view-hello-file #'ignore) ; Never show the hello file ;; No beeping or blinking @@ -89,14 +90,14 @@ compilation-ask-about-save nil compilation-scroll-output 'first-error) +;; Recenter to the middle of the window for `compile-goto-error', which is also +;; used by `wgrep' and `embark-export'. +(setq next-error-recenter '(4)) + ;;; Misc (setq whitespace-line-column nil) ; whitespace-mode -;; I reduced the default value of 9 to simplify the font-lock keyword, -;; aiming to improve performance. -(setq rainbow-delimiters-max-face-count 5) - ;; Can be activated with `display-line-numbers-mode' (setq-default display-line-numbers-width 3) (setq-default display-line-numbers-widen t) @@ -104,13 +105,8 @@ (setq truncate-string-ellipsis "…") ;; Improve Emacs' responsiveness by delaying syntax highlighting during input -;; but may reduce visual feedback. (setq redisplay-skip-fontification-on-input t) -;; Collects and displays all available documentation immediately, even if -;; multiple sources provide it. It concatenates the results. -(setq eldoc-documentation-strategy 'eldoc-documentation-compose-eagerly) - ;; Disable truncation of printed s-expressions in the message buffer (setq eval-expression-print-length nil eval-expression-print-level nil) @@ -122,13 +118,12 @@ ;; Delete by moving to trash in interactive mode (setq delete-by-moving-to-trash (not noninteractive)) +(setq remote-file-name-inhibit-delete-by-moving-to-trash t) -;; Disable the warning "X and Y are the same file". Ignoring this warning is -;; acceptable since it will redirect you to the existing buffer regardless. +;; Ignoring this is acceptable since it will redirect to the buffer regardless. (setq find-file-suppress-same-file-warnings t) -;; Resolve symlinks when opening files, so that any operations are conducted -;; from the file's true directory (like `find-file'). +;; Resolve symlinks so that operations are conducted from the file's directory (setq find-file-visit-truename t vc-follow-symlinks t) @@ -148,8 +143,7 @@ ;;; Backup files -;; Avoid generating backups or lockfiles to prevent creating world-readable -;; copies of files. +;; Avoid backups or lockfiles to prevent creating world-readable copies of files (setq create-lockfiles nil) (setq make-backup-files nil) @@ -173,11 +167,11 @@ ;; Enable auto-save to safeguard against crashes or data loss. The ;; `recover-file' or `recover-session' functions can be used to restore ;; auto-saved data. -(setq auto-save-default t) +(setq auto-save-default nil) +(setq auto-save-no-message t) ;; Do not auto-disable auto-save after deleting large chunks of -;; text. The purpose of auto-save is to provide a failsafe, and -;; disabling it contradicts this objective. +;; text. (setq auto-save-include-big-deletions t) (setq auto-save-list-file-prefix @@ -188,22 +182,23 @@ ;; Auto save options (setq kill-buffer-delete-auto-save-files t) +;; Remove duplicates from the kill ring to reduce clutter +(setq kill-do-not-save-duplicates t) + ;;; Auto revert -;; 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. +;; Auto-revert in Emacs is a feature that automatically updates the contents of +;; a buffer to reflect changes made to the underlying file. (setq revert-without-query (list ".") ; Do not prompt auto-revert-stop-on-user-input nil auto-revert-verbose t) ;; Revert other buffers (e.g, Dired) (setq global-auto-revert-non-file-buffers t) +(setq global-auto-revert-ignore-modes '(Buffer-menu-mode)) ; Resolve issue #29 ;;; recentf -;; `recentf' is an Emacs package that maintains a list of recently -;; accessed files, making it easier to reopen files you have worked on -;; recently. +;; `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)) @@ -213,20 +208,21 @@ ;;; saveplace -;; `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. +;; Enables Emacs to remember the last location within a file upon reopening. (setq save-place-file (expand-file-name "saveplace" user-emacs-directory)) (setq save-place-limit 600) ;;; savehist ;; `savehist-mode' 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. +;; between sessions. (setq history-length 300) (setq savehist-save-minibuffer-history t) ;; Default +(setq savehist-additional-variables + '(kill-ring ; clipboard + register-alist ; macros + mark-ring global-mark-ring ; marks + search-ring regexp-search-ring)) ; searches ;;; Frames and windows @@ -248,39 +244,30 @@ ;;; Scrolling -;; Enables faster scrolling through unfontified regions. This may result in -;; brief periods of inaccurate syntax highlighting immediately after scrolling, -;; which should quickly self-correct. +;; Enables faster scrolling. This may result in brief periods of inaccurate +;; syntax highlighting, which should quickly self-correct. (setq fast-but-imprecise-scrolling t) ;; Move point to top/bottom of buffer before signaling a scrolling error. (setq scroll-error-top-bottom t) -;; Keeps screen position if the scroll command moved it vertically out of the -;; window. +;; Keep screen position if scroll command moved it vertically out of the window. (setq scroll-preserve-screen-position t) -;; Emacs spends excessive time recentering the screen when the cursor moves more -;; than N lines past the window edges (where N is the value of -;; `scroll-conservatively`). This can be particularly slow in larger files -;; during extensive scrolling. If `scroll-conservatively` is set above 100, the -;; window is never automatically recentered. The default value of 0 triggers -;; recentering too aggressively. Setting it to 10 reduces excessive recentering -;; and only recenters the window when scrolling significantly off-screen. -(setq scroll-conservatively 10) +;; If `scroll-conservatively' is set above 100, the window is never +;; automatically recentered, which decreases the time spend recentering. +(setq scroll-conservatively 101) -;; Enables smooth scrolling by making Emacs scroll the window by 1 line whenever -;; the cursor moves off the visible screen. -(setq scroll-step 1) - -;; Reduce cursor lag by : -;; 1. Prevent automatic adjustments to `window-vscroll' for long lines. -;; 2. Resolve the issue of random half-screen jumps during scrolling. +;; 1. Preventing automatic adjustments to `window-vscroll' for long lines. +;; 2. Resolving the issue of random half-screen jumps during scrolling. (setq auto-window-vscroll nil) ;; Number of lines of margin at the top and bottom of a window. (setq scroll-margin 0) +;; Number of lines of continuity when scrolling by screenfuls. +(setq next-screen-context-lines 0) + ;; Horizontal scrolling (setq hscroll-margin 2 hscroll-step 1) @@ -298,8 +285,6 @@ ;; The blinking cursor is distracting and interferes with cursor settings in ;; some minor modes that try to change it buffer-locally (e.g., Treemacs). -;; Additionally, it can cause freezing, especially on macOS, for users with -;; customized and colored cursors. (blink-cursor-mode -1) ;; Don't blink the paren matching the one at point, it's too distracting. @@ -319,9 +304,8 @@ ;; Avoid automatic frame resizing when adjusting settings. (setq global-text-scale-adjust-resizes-frames nil) -;; This controls how long Emacs will blink to show the deleted pairs with -;; `delete-pair'. A longer delay can be annoying as it causes a noticeable pause -;; after each deletion, disrupting the flow of editing. +;; A longer delay can be annoying as it causes a noticeable pause after each +;; deletion, disrupting the flow of editing. (setq delete-pair-blink-delay 0.03) (setq-default left-fringe-width 8) @@ -354,44 +338,32 @@ (setq read-extended-command-predicate #'command-completion-default-include-p) ;; Enable multi-line commenting which ensures that `comment-indent-new-line' -;; properly continues comments onto new lines, which is useful for writing -;; longer comments or docstrings that span multiple lines. +;; properly continues comments onto new lines. (setq comment-multi-line t) +;; Ensures that empty lines within the commented region are also commented out. +;; This prevents unintended visual gaps and maintains a consistent appearance. +(setq comment-empty-lines t) + ;; We often split terminals and editor windows or place them side-by-side, ;; making use of the additional horizontal space. (setq-default fill-column 80) -;; Disable the obsolete practice of end-of-line spacing from the -;; typewriter era. +;; Disable the obsolete practice of end-of-line spacing from the typewriter era. (setq sentence-end-double-space nil) -;; According to the POSIX, a line is defined as "a sequence of zero or -;; more non-newline characters followed by a terminating newline". +;; According to the POSIX, a line is defined as "a sequence of zero or more +;; non-newline characters followed by a terminating newline". (setq require-final-newline t) -;; Remove duplicates from the kill ring to reduce clutter -(setq kill-do-not-save-duplicates t) - -;; Ensures that empty lines within the commented region are also commented out. -;; This prevents unintended visual gaps and maintains a consistent appearance, -;; ensuring that comments apply uniformly to all lines, including those that are -;; otherwise empty. -(setq comment-empty-lines t) - ;; Eliminate delay before highlighting search matches (setq lazy-highlight-initial-delay 0) ;;; Modeline -;; Setting `display-time-default-load-average' to nil makes Emacs omit the load -;; average information from the mode line. +;; Makes Emacs omit the load average information from the mode line. (setq display-time-default-load-average nil) -;; Display the current line and column numbers in the mode line -(setq line-number-mode t) -(setq column-number-mode t) - ;;; Filetype ;; Do not notify the user each time Python tries to guess the indentation offset @@ -408,8 +380,6 @@ dired-recursive-deletes 'top dired-recursive-copies 'always dired-create-destination-dirs 'ask - ;; Revert the Dired buffer without prompting. - dired-auto-revert-buffer #'dired-buffer-stale-p image-dired-thumb-size 150) (setq dired-vc-rename-file t) @@ -419,16 +389,7 @@ ;; dired-omit-mode (setq dired-omit-verbose nil) -(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_.*")) +(setq dired-omit-files (concat "\\`[.]\\'")) ;; ls-lisp (setq ls-lisp-verbosity nil) @@ -437,8 +398,8 @@ ;;; Ediff ;; Configure Ediff to use a single frame and split windows horizontally -(setq ediff-window-setup-function #'ediff-setup-windows-plain - ediff-split-window-function #'split-window-horizontally) +(setq ediff-window-setup-function 'ediff-setup-windows-plain + ediff-split-window-function 'split-window-horizontally) ;;; Help @@ -446,13 +407,12 @@ (setq apropos-do-all t) ;; Fixes #11: Prevents help command completion from triggering autoload. -;; (e.g., apropos-command, apropos-variable, apropos...) -;; Loading additional files for completion can slow down help commands -;; and may unintentionally execute initialization code from some libraries. +;; Loading additional files for completion can slow down help commands and may +;; unintentionally execute initialization code from some libraries. (setq help-enable-completion-autoload nil) (setq help-enable-autoload nil) (setq help-enable-symbol-autoload nil) -(setq help-window-select t) +(setq help-window-select t) ;; Focus new help windows when opened ;;; Eglot @@ -468,9 +428,7 @@ (setq eglot-report-progress nil) ; Prevent Eglot minibuffer spam ;; Eglot optimization: Disable `eglot-events-buffer' to maintain consistent -;; performance in long-running Emacs sessions. By default, it retains 2,000,000 -;; lines, and each new event triggers pretty-printing of the entire buffer, -;; leading to a gradual performance decline. +;; performance in long-running Emacs sessions. (setq eglot-events-buffer-config '(:size 0 :format full)) ;;; Flymake @@ -525,8 +483,8 @@ ;;; xref ;; Enable completion in the minibuffer instead of the definitions buffer -(setq xref-show-definitions-function #'xref-show-definitions-completing-read - xref-show-xrefs-function #'xref-show-definitions-completing-read) +(setq xref-show-definitions-function 'xref-show-definitions-completing-read + xref-show-xrefs-function 'xref-show-definitions-completing-read) ;;; abbrev @@ -543,8 +501,21 @@ (setq dabbrev-ignored-buffer-modes '(archive-mode image-mode docview-mode tags-table-mode pdf-view-mode)) +;;; Remove warnings from narrow-to-region, upcase-region... + +(dolist (cmd '(list-timers narrow-to-region upcase-region downcase-region + erase-buffer scroll-left dired-find-alternate-file)) + (put cmd 'disabled nil)) + ;;; Load post init -(minimal-emacs-load-user-init "post-init.el") +(when (fboundp 'minimal-emacs-load-user-init) + (minimal-emacs-load-user-init "post-init.el")) +(setq minimal-emacs--success t) (provide 'init) + +;; Local variables: +;; byte-compile-warnings: (not obsolete free-vars) +;; End: + ;;; init.el ends here