From 8672526d9c9c983ce9e9629901bd4b336f264884 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:06:42 +0300 Subject: [PATCH 1/8] Add early-init.el for startup tuning Raise the GC threshold during startup and restore a modest value afterwards, disable the tool bar via frame parameters, inhibit implied frame resizing, native-compile packages at install time, and fix the missing LANG locale in GUI Emacs on macOS. --- CHANGELOG.md | 1 + core/prelude-ui.el | 6 ++-- early-init.el | 68 ++++++++++++++++++++++++++++++++++++++++++++++ init.el | 5 ++-- 4 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 early-init.el diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c4a0bcd0..ee561e27bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Changes +- Add an `early-init.el` that tunes startup: it raises the GC threshold while Emacs loads (restoring a modest value once startup is over), disables the tool bar via frame parameters so the initial frame is never drawn with one, sets `frame-inhibit-implied-resize`, native-compiles packages at install time (`package-native-compile`), and sets a sane `LANG` for GUI Emacs on macOS (which otherwise starts in the `C` locale and breaks spell-checker dictionaries and subprocess sorting). - Tidy up `prelude-common-lisp`: drop stale `slime-autodoc-use-multiline-p` setting (the variable was removed from upstream SLIME; modern autodoc honors `eldoc-echo-area-use-multiline-p`), set `inferior-lisp-program` to `sbcl` so `M-x run-lisp` works without SLIME, and add `slime-quicklisp` to `slime-contribs` for Quicklisp integration. ### Bugs fixed diff --git a/core/prelude-ui.el b/core/prelude-ui.el index 00a7f5da52..2063ca5059 100644 --- a/core/prelude-ui.el +++ b/core/prelude-ui.el @@ -31,10 +31,8 @@ ;;; Code: -;; the toolbar is just a waste of valuable screen estate -;; in a tty tool-bar-mode does not properly auto-load, and is -;; already disabled anyway -(tool-bar-mode -1) +;; the toolbar is disabled via `default-frame-alist' in early-init.el, +;; so the initial frame is never drawn with one (when prelude-minimalistic-ui (menu-bar-mode -1)) diff --git a/early-init.el b/early-init.el new file mode 100644 index 0000000000..75282af8bb --- /dev/null +++ b/early-init.el @@ -0,0 +1,68 @@ +;;; early-init.el --- Prelude's early configuration. +;; +;; Copyright (c) 2011-2026 Bozhidar Batsov +;; +;; Author: Bozhidar Batsov +;; URL: https://github.com/bbatsov/prelude +;; Version: 1.1.0 +;; Keywords: convenience + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; This file is loaded before the package system and the UI are +;; initialized, which makes it the right place for a few settings that +;; have to be applied very early during startup. + +;;; License: + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. +;; +;; 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Code: + +;; Raise the garbage collection threshold as high as possible for the +;; duration of startup, so we don't pay for repeated collections while +;; a lot of code is being loaded. A modest threshold is restored once +;; startup is over (a permanently huge threshold trades frequent short +;; pauses for rare, long freezes). +(setq gc-cons-threshold most-positive-fixnum) + +(add-hook 'emacs-startup-hook + (lambda () + (setq gc-cons-threshold (* 50 1000 1000) ; 50MB + gc-cons-percentage 0.2))) + +;; Disable the tool bar via frame parameters, so the initial frame is +;; never created with one in the first place. Toggling `tool-bar-mode' +;; later forces an expensive frame resize. +(push '(tool-bar-lines . 0) default-frame-alist) + +;; Don't resize the frame in response to font or UI changes during +;; startup - it's needless work before the frame is even visible. +(setq frame-inhibit-implied-resize t) + +;; Native-compile packages when they are installed rather than lazily +;; on first load, so you don't hit compilation pauses while working. +(setq package-native-compile t) + +;; GUI Emacs on macOS doesn't inherit the environment from the shell, +;; so without LANG it ends up in the "C" locale, which breaks things +;; like spell-checker dictionaries and subprocess sorting. +(when (and (eq system-type 'darwin) (not (getenv "LANG"))) + (setenv "LANG" "en_US.UTF-8")) + +;;; early-init.el ends here diff --git a/init.el b/init.el index 8f81ec1b05..145c2e8754 100644 --- a/init.el +++ b/init.el @@ -94,9 +94,8 @@ by Prelude.") (add-to-list 'load-path prelude-vendor-dir) (prelude-add-subfolders-to-load-path prelude-vendor-dir) -;; reduce the frequency of garbage collection by making it happen on -;; each 50MB of allocated data (the default is on every 0.76MB) -(setq gc-cons-threshold 50000000) +;; the garbage collection threshold is tuned in early-init.el (raised +;; during startup, restored to a modest value once startup is over) ;; warn when opening files bigger than 100MB (setq large-file-warning-threshold 100000000) From adea612f2b8189ee16955f7d0db99d1cddd93dc7 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:08:35 +0300 Subject: [PATCH 2/8] Add tree-sitter grammar recipes and modernize eglot event logging Populate treesit-language-source-alist so users can install any missing grammar with treesit-install-language-grammar, and prefer the newer eglot-events-buffer-config over the obsolete eglot-events-buffer-size. --- CHANGELOG.md | 2 ++ core/prelude-core.el | 30 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee561e27bc..fb57a9b8a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ ### Changes - Add an `early-init.el` that tunes startup: it raises the GC threshold while Emacs loads (restoring a modest value once startup is over), disables the tool bar via frame parameters so the initial frame is never drawn with one, sets `frame-inhibit-implied-resize`, native-compiles packages at install time (`package-native-compile`), and sets a sane `LANG` for GUI Emacs on macOS (which otherwise starts in the `C` locale and breaks spell-checker dictionaries and subprocess sorting). +- Populate `treesit-language-source-alist` with recipes for the languages Prelude's modules use, so a missing grammar can be installed with `M-x treesit-install-language-grammar` without hunting down repository URLs. +- Modernize the Eglot event-log setting to prefer `eglot-events-buffer-config` on newer Eglot, falling back to the obsolete `eglot-events-buffer-size` on older versions. - Tidy up `prelude-common-lisp`: drop stale `slime-autodoc-use-multiline-p` setting (the variable was removed from upstream SLIME; modern autodoc honors `eldoc-echo-area-use-multiline-p`), set `inferior-lisp-program` to `sbcl` so `M-x run-lisp` works without SLIME, and add `slime-quicklisp` to `slime-contribs` for Quicklisp integration. ### Bugs fixed diff --git a/core/prelude-core.el b/core/prelude-core.el index 1731cb6cab..c02b32a7ca 100644 --- a/core/prelude-core.el +++ b/core/prelude-core.el @@ -151,6 +151,30 @@ Does nothing if Emacs was compiled without tree-sitter support." (treesit-ready-p grammar t)) (add-to-list 'major-mode-remap-alist (cons old-mode new-mode)))) +;; Grammar recipes for the languages Prelude's modules know about, so a +;; missing grammar can be installed with `M-x treesit-install-language-grammar' +;; (or `treesit-install-language-grammar' for the whole set) instead of +;; hunting down repository URLs. Add your own recipes from personal config. +(when (require 'treesit nil t) + (dolist (recipe + '((bash "https://github.com/tree-sitter/tree-sitter-bash") + (c "https://github.com/tree-sitter/tree-sitter-c") + (cpp "https://github.com/tree-sitter/tree-sitter-cpp") + (css "https://github.com/tree-sitter/tree-sitter-css") + (elixir "https://github.com/elixir-lang/tree-sitter-elixir") + (go "https://github.com/tree-sitter/tree-sitter-go") + (gomod "https://github.com/camdencheek/tree-sitter-go-mod") + (heex "https://github.com/phoenixframework/tree-sitter-heex") + (javascript "https://github.com/tree-sitter/tree-sitter-javascript") + (json "https://github.com/tree-sitter/tree-sitter-json") + (python "https://github.com/tree-sitter/tree-sitter-python") + (ruby "https://github.com/tree-sitter/tree-sitter-ruby") + (rust "https://github.com/tree-sitter/tree-sitter-rust") + (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") + (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") + (yaml "https://github.com/ikatyang/tree-sitter-yaml"))) + (add-to-list 'treesit-language-source-alist recipe))) + (defun prelude-lsp-enable () "Enable the LSP client configured via `prelude-lsp-client'." (pcase prelude-lsp-client @@ -162,7 +186,11 @@ Does nothing if Emacs was compiled without tree-sitter support." ;; Eglot configuration (with-eval-after-load 'eglot (setq eglot-autoshutdown t) - (setq eglot-events-buffer-size 0) + ;; don't log every LSP event - the logging adds overhead with chatty + ;; servers (set these back when you need to debug an LSP session) + (if (boundp 'eglot-events-buffer-config) + (setq eglot-events-buffer-config '(:size 0 :format full)) ; newer Eglot + (setq eglot-events-buffer-size 0)) ; older Eglot (setq eglot-extend-to-xref t) (define-key eglot-mode-map (kbd "C-c C-l r") #'eglot-rename) From 5259b102851ae20db2b650335004c2623e70846c Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:10:10 +0300 Subject: [PATCH 3/8] Bridge Eglot diagnostics into Flycheck When eglot is the LSP client, install flycheck-eglot on demand and enable global-flycheck-eglot-mode so LSP diagnostics reach Prelude's Flycheck UI (eglot otherwise reports only through Flymake). --- CHANGELOG.md | 1 + modules/prelude-programming.el | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb57a9b8a6..bc2b43b38d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Add an `early-init.el` that tunes startup: it raises the GC threshold while Emacs loads (restoring a modest value once startup is over), disables the tool bar via frame parameters so the initial frame is never drawn with one, sets `frame-inhibit-implied-resize`, native-compiles packages at install time (`package-native-compile`), and sets a sane `LANG` for GUI Emacs on macOS (which otherwise starts in the `C` locale and breaks spell-checker dictionaries and subprocess sorting). - Populate `treesit-language-source-alist` with recipes for the languages Prelude's modules use, so a missing grammar can be installed with `M-x treesit-install-language-grammar` without hunting down repository URLs. - Modernize the Eglot event-log setting to prefer `eglot-events-buffer-config` on newer Eglot, falling back to the obsolete `eglot-events-buffer-size` on older versions. +- Bridge Eglot diagnostics into Flycheck via [flycheck-eglot](https://github.com/flycheck/flycheck-eglot) when `prelude-lsp-client` is `eglot`, so LSP diagnostics show up in Prelude's Flycheck UI instead of only through Flymake. Installed on demand; lsp-mode users are unaffected (lsp-mode has its own Flycheck integration). - Tidy up `prelude-common-lisp`: drop stale `slime-autodoc-use-multiline-p` setting (the variable was removed from upstream SLIME; modern autodoc honors `eldoc-echo-area-use-multiline-p`), set `inferior-lisp-program` to `sbcl` so `M-x run-lisp` works without SLIME, and add `slime-quicklisp` to `slime-contribs` for Quicklisp integration. ### Bugs fixed diff --git a/modules/prelude-programming.el b/modules/prelude-programming.el index 9047337020..f2be9a7c33 100644 --- a/modules/prelude-programming.el +++ b/modules/prelude-programming.el @@ -79,6 +79,15 @@ (global-flycheck-mode +1) (add-hook 'prog-mode-hook 'flycheck-mode)) +;; When Eglot is the LSP client, route its diagnostics through Flycheck +;; as well. On its own Eglot reports only via Flymake, so without this +;; bridge LSP diagnostics wouldn't show up in Prelude's Flycheck UI. +;; (lsp-mode has its own Flycheck integration, so this is Eglot-only.) +(when (eq prelude-lsp-client 'eglot) + (prelude-require-package 'flycheck-eglot) + (require 'flycheck-eglot) + (global-flycheck-eglot-mode +1)) + ;; Makefiles require tabs for indentation (defun prelude-makefile-mode-defaults () (whitespace-toggle-options '(tabs)) From b6eb76579f0a276368180df0932babecdfc0e3c3 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:11:52 +0300 Subject: [PATCH 4/8] Polish the vertico and corfu completion modules Enable vertico-directory (RET descends, DEL kills a path component), corfu-history-mode (recency sorting, persisted via savehist), and set consult-narrow-key to < for group narrowing. --- CHANGELOG.md | 3 +++ modules/prelude-corfu.el | 6 +++++- modules/prelude-vertico.el | 22 +++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2b43b38d..c4042a356f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - Populate `treesit-language-source-alist` with recipes for the languages Prelude's modules use, so a missing grammar can be installed with `M-x treesit-install-language-grammar` without hunting down repository URLs. - Modernize the Eglot event-log setting to prefer `eglot-events-buffer-config` on newer Eglot, falling back to the obsolete `eglot-events-buffer-size` on older versions. - Bridge Eglot diagnostics into Flycheck via [flycheck-eglot](https://github.com/flycheck/flycheck-eglot) when `prelude-lsp-client` is `eglot`, so LSP diagnostics show up in Prelude's Flycheck UI instead of only through Flymake. Installed on demand; lsp-mode users are unaffected (lsp-mode has its own Flycheck integration). +- Enable the `vertico-directory` extension in `prelude-vertico`: `RET` descends into the selected directory, `DEL` deletes a whole path component, and `M-DEL` deletes a word of it. +- Enable `corfu-history-mode` in `prelude-corfu` so recently chosen completion candidates sort first (persisted across sessions via savehist). +- Set `consult-narrow-key` to `<` in `prelude-vertico`, so you can narrow consult candidates to a single group (e.g. `< b` for buffers in `consult-buffer`). - Tidy up `prelude-common-lisp`: drop stale `slime-autodoc-use-multiline-p` setting (the variable was removed from upstream SLIME; modern autodoc honors `eldoc-echo-area-use-multiline-p`), set `inferior-lisp-program` to `sbcl` so `M-x run-lisp` works without SLIME, and add `slime-quicklisp` to `slime-contribs` for Quicklisp integration. ### Bugs fixed diff --git a/modules/prelude-corfu.el b/modules/prelude-corfu.el index caaced447a..d7851ae446 100644 --- a/modules/prelude-corfu.el +++ b/modules/prelude-corfu.el @@ -49,7 +49,11 @@ :init (global-corfu-mode) :config - (corfu-popupinfo-mode)) + (corfu-popupinfo-mode) + ;; sort candidates by recency of selection, so the ones you pick + ;; often bubble to the top; the history persists across sessions + ;; when savehist-mode is on (which Prelude enables in core) + (corfu-history-mode)) (use-package cape :ensure t diff --git a/modules/prelude-vertico.el b/modules/prelude-vertico.el index 0c6464d585..d4845e7d14 100644 --- a/modules/prelude-vertico.el +++ b/modules/prelude-vertico.el @@ -50,6 +50,21 @@ ;; (setq vertico-cycle t) ) +;; Smarter path editing in file prompts (ships as part of Vertico): +;; RET descends into the selected directory instead of opening it in +;; Dired, DEL deletes a whole directory component at once, and M-DEL +;; deletes just a word of it. +(use-package vertico-directory + :ensure nil ; comes with vertico + :after vertico + :bind (:map vertico-map + ("RET" . vertico-directory-enter) + ("DEL" . vertico-directory-delete-char) + ("M-DEL" . vertico-directory-delete-word)) + ;; tidy the shadowed part of the path when you re-root it (e.g. type + ;; ~/ or / in the middle of a path) + :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) + ;; A few more useful configurations for Vertico (use-package emacs :init @@ -131,7 +146,12 @@ ("M-s L" . consult-line-multi) ("M-s m" . multi-occur) ("M-s k" . consult-keep-lines) - ("M-s u" . consult-focus-lines))) + ("M-s u" . consult-focus-lines)) + :config + ;; press < followed by a group key to narrow the candidates to a + ;; single group (e.g. in consult-buffer, < b shows only buffers); + ;; press < again to remove the narrowing + (setq consult-narrow-key "<")) (provide 'prelude-vertico) ;;; prelude-vertico.el ends here From beaf00919dd7dca57699f26247815fbc67529a9e Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:14:32 +0300 Subject: [PATCH 5/8] Add jinx as a spell-checker option New prelude-spell-checker defcustom selects flyspell (default) or jinx. When jinx is chosen it's installed on demand and enabled as a single global mode, and the per-buffer flyspell hooks stand down. --- CHANGELOG.md | 1 + core/prelude-custom.el | 12 +++++++++++- core/prelude-editor.el | 17 +++++++++++++++-- modules/prelude-programming.el | 5 +++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4042a356f..5abb6677e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### New features +- Add a `prelude-spell-checker` option to choose between `flyspell` (the default) and [jinx](https://github.com/minad/jinx), a faster libenchant-based spell checker that only checks the visible part of the buffer. Jinx is installed on demand and enabled as a single global mode; set `(setq prelude-spell-checker 'jinx)` in your personal config to opt in (requires libenchant and a C compiler). - Add `prelude-ai` module: a thin wrapper around [gptel](https://github.com/karthink/gptel) for LLM-backed chat (Claude, GPT, Gemini, Ollama, etc.). Binds `gptel-menu` to `C-c q`. Backends and API keys are configured in personal config -- see the module documentation for examples. - Add `prelude-forge` module: enables [Forge](https://github.com/magit/forge) on top of Magit so you can read and reply to GitHub/GitLab/Gitea pull requests and issues without leaving Emacs. - Add `prelude-eglot-booster` module: speeds up Eglot via the [emacs-lsp-booster](https://github.com/blahgeek/emacs-lsp-booster) wrapper. The Emacs side ([eglot-booster](https://github.com/jdtsmith/eglot-booster)) is auto-installed via `package-vc-install` when the booster binary is on `PATH`; otherwise the module no-ops. diff --git a/core/prelude-custom.el b/core/prelude-custom.el index b863eda3e7..9e06668940 100644 --- a/core/prelude-custom.el +++ b/core/prelude-custom.el @@ -78,10 +78,20 @@ Will only occur if `prelude-whitespace' is also enabled." :group 'prelude) (defcustom prelude-flyspell t - "Non-nil values enable Prelude's flyspell support." + "Non-nil values enable Prelude's spell checking support." :type 'boolean :group 'prelude) +(defcustom prelude-spell-checker 'flyspell + "The spell checker to use when `prelude-flyspell' is enabled. +`flyspell' is the classic built-in checker (it needs an external +aspell/hunspell binary). `jinx' is a faster, libenchant-based +checker that only checks the visible portion of the buffer, but it +requires libenchant and a C compiler to build its native module." + :type '(choice (const :tag "Flyspell (built-in)" flyspell) + (const :tag "Jinx (libenchant)" jinx)) + :group 'prelude) + (defcustom prelude-user-init-file (expand-file-name "personal/" user-emacs-directory) "Path to your personal customization file. diff --git a/core/prelude-editor.el b/core/prelude-editor.el index 9370f79695..58c3e255b6 100644 --- a/core/prelude-editor.el +++ b/core/prelude-editor.el @@ -206,10 +206,23 @@ ispell-extra-args '("--sug-mode=ultra"))) (defun prelude-enable-flyspell () - "Enable command `flyspell-mode' if `prelude-flyspell' is not nil." - (when (and prelude-flyspell (executable-find ispell-program-name)) + "Enable command `flyspell-mode' when Prelude's spell checker is Flyspell. +Does nothing when `prelude-spell-checker' is set to something else +\(e.g. `jinx', which is a single global mode enabled below)." + (when (and prelude-flyspell + (eq prelude-spell-checker 'flyspell) + (executable-find ispell-program-name)) (flyspell-mode +1))) +;; jinx is an enchant-based spell checker; unlike flyspell it's a +;; single global mode that checks only the visible part of the buffer, +;; so it's enabled once here rather than per-buffer. The enable is +;; guarded so a missing libenchant only warns instead of aborting startup. +(when (and prelude-flyspell (eq prelude-spell-checker 'jinx)) + (prelude-require-package 'jinx) + (with-demoted-errors "Prelude: could not enable jinx: %S" + (global-jinx-mode +1))) + (defun prelude-cleanup-maybe () "Invoke `whitespace-cleanup' if `prelude-clean-whitespace-on-save' is not nil." (when prelude-clean-whitespace-on-save diff --git a/modules/prelude-programming.el b/modules/prelude-programming.el index f2be9a7c33..9d89761caa 100644 --- a/modules/prelude-programming.el +++ b/modules/prelude-programming.el @@ -59,8 +59,9 @@ (defun prelude-prog-mode-defaults () "Default coding hook, useful with any programming language." - (when (and (executable-find ispell-program-name) - prelude-flyspell) + (when (and prelude-flyspell + (eq prelude-spell-checker 'flyspell) + (executable-find ispell-program-name)) (flyspell-prog-mode)) (when prelude-guru (guru-mode +1) From 653463d049a8cfea385a5e9c7388bfeab6fc6fac Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:15:42 +0300 Subject: [PATCH 6/8] Add ws-butler as a whitespace-cleanup style option New prelude-whitespace-cleanup-style defcustom selects whitespace-cleanup (default, whole file) or ws-butler (edited lines only), the latter installed on demand and enabled per buffer. --- CHANGELOG.md | 1 + core/prelude-custom.el | 10 ++++++++++ core/prelude-editor.el | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5abb6677e9..8a8553bdfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### New features - Add a `prelude-spell-checker` option to choose between `flyspell` (the default) and [jinx](https://github.com/minad/jinx), a faster libenchant-based spell checker that only checks the visible part of the buffer. Jinx is installed on demand and enabled as a single global mode; set `(setq prelude-spell-checker 'jinx)` in your personal config to opt in (requires libenchant and a C compiler). +- Add a `prelude-whitespace-cleanup-style` option to choose how whitespace is cleaned on save: `whitespace-cleanup` (the default, tidies the whole file) or [ws-butler](https://github.com/lewang/ws-butler), which trims only the lines you actually edited so saving a file in someone else's project no longer produces noisy whitespace-only diffs. ws-butler is installed on demand. - Add `prelude-ai` module: a thin wrapper around [gptel](https://github.com/karthink/gptel) for LLM-backed chat (Claude, GPT, Gemini, Ollama, etc.). Binds `gptel-menu` to `C-c q`. Backends and API keys are configured in personal config -- see the module documentation for examples. - Add `prelude-forge` module: enables [Forge](https://github.com/magit/forge) on top of Magit so you can read and reply to GitHub/GitLab/Gitea pull requests and issues without leaving Emacs. - Add `prelude-eglot-booster` module: speeds up Eglot via the [emacs-lsp-booster](https://github.com/blahgeek/emacs-lsp-booster) wrapper. The Emacs side ([eglot-booster](https://github.com/jdtsmith/eglot-booster)) is auto-installed via `package-vc-install` when the booster binary is on `PATH`; otherwise the module no-ops. diff --git a/core/prelude-custom.el b/core/prelude-custom.el index 9e06668940..6fcbb28099 100644 --- a/core/prelude-custom.el +++ b/core/prelude-custom.el @@ -77,6 +77,16 @@ Will only occur if `prelude-whitespace' is also enabled." :type 'boolean :group 'prelude) +(defcustom prelude-whitespace-cleanup-style 'whitespace-cleanup + "How whitespace is cleaned up on save when enabled. +`whitespace-cleanup' (the default) tidies the whole file, which can +produce noisy whitespace-only diffs in projects that aren't already +clean. `ws-butler' instead trims only the lines you actually edited, +leaving the rest of the file untouched (installed on demand)." + :type '(choice (const :tag "whitespace-cleanup (whole file)" whitespace-cleanup) + (const :tag "ws-butler (edited lines only)" ws-butler)) + :group 'prelude) + (defcustom prelude-flyspell t "Non-nil values enable Prelude's spell checking support." :type 'boolean diff --git a/core/prelude-editor.el b/core/prelude-editor.el index 58c3e255b6..4137564bf6 100644 --- a/core/prelude-editor.el +++ b/core/prelude-editor.el @@ -228,11 +228,24 @@ Does nothing when `prelude-spell-checker' is set to something else (when prelude-clean-whitespace-on-save (whitespace-cleanup))) +;; ws-butler trims trailing whitespace on save, but only on the lines +;; you actually edited, so saving a file in someone else's project +;; doesn't produce noisy whitespace-only diffs. Only pull it in when +;; it's the chosen cleanup style. +(when (and prelude-whitespace + prelude-clean-whitespace-on-save + (eq prelude-whitespace-cleanup-style 'ws-butler)) + (prelude-require-package 'ws-butler) + (require 'ws-butler nil t)) + (defun prelude-enable-whitespace () "Enable `whitespace-mode' if `prelude-whitespace' is not nil." (when prelude-whitespace ;; keep the whitespace decent all the time (in this buffer) - (add-hook 'before-save-hook 'prelude-cleanup-maybe nil t) + (when prelude-clean-whitespace-on-save + (if (eq prelude-whitespace-cleanup-style 'ws-butler) + (ws-butler-mode +1) + (add-hook 'before-save-hook 'prelude-cleanup-maybe nil t))) (whitespace-mode +1))) (add-hook 'text-mode-hook 'prelude-enable-flyspell) From b188c8956565be0d16eecf09adcbf5b1901d6d9f Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:21:31 +0300 Subject: [PATCH 7/8] Add prelude-swift and prelude-mistty modules Swift support via swift-ts-mode + sourcekit-lsp, and a MisTTY-based terminal module (pure elisp, rebinds C-c t). Register both in the sample module list, installation docs, per-module doc pages and the mkdocs nav. --- CHANGELOG.md | 2 ++ docs/installation.md | 2 ++ docs/modules/mistty.md | 17 +++++++++++ docs/modules/swift.md | 31 ++++++++++++++++++++ mkdocs.yml | 2 ++ modules/prelude-mistty.el | 51 ++++++++++++++++++++++++++++++++ modules/prelude-swift.el | 61 +++++++++++++++++++++++++++++++++++++++ sample/prelude-modules.el | 2 ++ 8 files changed, 168 insertions(+) create mode 100644 docs/modules/mistty.md create mode 100644 docs/modules/swift.md create mode 100644 modules/prelude-mistty.el create mode 100644 modules/prelude-swift.el diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a8553bdfb..00dcbf9cb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### New features +- Add `prelude-swift` module: Swift support via the tree-sitter powered `swift-ts-mode` and `sourcekit-lsp` (registered with Eglot, since Eglot has no built-in entry for Swift). Starts an LSP session through the usual `prelude-lsp-enable` abstraction. +- Add `prelude-mistty` module: a terminal based on [MisTTY](https://github.com/szermatt/mistty), a pure-elisp shell/comint hybrid on top of `term.el` (no native module to compile). Rebinds Prelude's terminal key `C-c t` to `mistty`. - Add a `prelude-spell-checker` option to choose between `flyspell` (the default) and [jinx](https://github.com/minad/jinx), a faster libenchant-based spell checker that only checks the visible part of the buffer. Jinx is installed on demand and enabled as a single global mode; set `(setq prelude-spell-checker 'jinx)` in your personal config to opt in (requires libenchant and a C compiler). - Add a `prelude-whitespace-cleanup-style` option to choose how whitespace is cleaned on save: `whitespace-cleanup` (the default, tidies the whole file) or [ws-butler](https://github.com/lewang/ws-butler), which trims only the lines you actually edited so saving a file in someone else's project no longer produces noisy whitespace-only diffs. ws-butler is installed on demand. - Add `prelude-ai` module: a thin wrapper around [gptel](https://github.com/karthink/gptel) for LLM-backed chat (Claude, GPT, Gemini, Ollama, etc.). Binds `gptel-menu` to `C-c q`. Backends and API keys are configured in personal config -- see the module documentation for examples. diff --git a/docs/installation.md b/docs/installation.md index baa541ea41..32c529f209 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -173,6 +173,7 @@ modules visit the [docs](modules/index.md). ;; (require 'prelude-scheme) (require 'prelude-shell) ;; (require 'prelude-scss) +;; (require 'prelude-swift) ;; (require 'prelude-ts) (require 'prelude-web) ;; Emacs mode for web templates (require 'prelude-xml) @@ -180,6 +181,7 @@ modules visit the [docs](modules/index.md). ;;; Misc (require 'prelude-erc) ;; A popular Emacs IRC client +;; (require 'prelude-mistty) ;; MisTTY terminal (pure elisp, no native module) ``` You'll need to adjust your `prelude-modules.el` file once the diff --git a/docs/modules/mistty.md b/docs/modules/mistty.md new file mode 100644 index 0000000000..950f266f8e --- /dev/null +++ b/docs/modules/mistty.md @@ -0,0 +1,17 @@ +# Prelude MisTTY + +[MisTTY](https://github.com/szermatt/mistty) is a shell/comint hybrid +built on top of `term.el`. It gives you full terminal emulation for TUI +programs while keeping ordinary Emacs editing and motion on the command +line. Unlike `vterm` it's pure Emacs Lisp, so there's no native module +to compile. + +## Key bindings + +Enabling this module rebinds Prelude's terminal key, C-c t, +from `crux-visit-term-buffer` to `mistty`, on the assumption that if +you've turned it on you'd rather that key opened MisTTY. You can pick a +different binding in your personal config if you prefer. + +Inside a MisTTY buffer, C-c C-j and C-c C-q toggle +between the terminal and the Emacs editing modes. diff --git a/docs/modules/swift.md b/docs/modules/swift.md new file mode 100644 index 0000000000..e38900c122 --- /dev/null +++ b/docs/modules/swift.md @@ -0,0 +1,31 @@ +# Prelude Swift + +!!! Note + + This module builds on top of the shared [Programming](programming.md) module. + +## Package Prerequisites + +For the proper functioning of this module, you'll need the following on +your system: + +- a Swift toolchain (`swift`, `swiftc`) +- `sourcekit-lsp` (bundled with the Xcode command line tools or a Swift toolchain) + +## Swift Mode + +The module uses the tree-sitter powered `swift-ts-mode` for editing +Swift code, so you'll need the `swift` tree-sitter grammar installed. +Prelude ships a recipe for it, so `M-x treesit-install-language-grammar +RET swift` will fetch and build it for you. + +Whenever you are editing Swift code run C-h m to look at the +Swift mode key bindings. + +## LSP support + +The module starts an LSP session automatically via Prelude's +`prelude-lsp-enable`, using whichever client `prelude-lsp-client` +selects (Eglot by default). Eglot has no built-in entry for Swift, so +the module registers `sourcekit-lsp` for it. lsp-mode users get Swift +support through the `lsp-sourcekit` package. diff --git a/mkdocs.yml b/mkdocs.yml index ba19f788b4..380577dfc2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -42,6 +42,7 @@ nav: - Scheme: modules/scheme.md - SCSS: modules/scss.md - Shell: modules/shell.md + - Swift: modules/swift.md - TypeScript: modules/ts.md - Web/HTML: modules/web.md - XML: modules/xml.md @@ -58,6 +59,7 @@ nav: - Forge (PRs/Issues): modules/forge.md - Key Chord: modules/key_chord.md - Literate Programming: modules/literate-programming.md + - MisTTY (Terminal): modules/mistty.md - Org Mode: modules/orgmode.md - FAQ: faq.md - Troubleshooting: troubleshooting.md diff --git a/modules/prelude-mistty.el b/modules/prelude-mistty.el new file mode 100644 index 0000000000..73d0161d2f --- /dev/null +++ b/modules/prelude-mistty.el @@ -0,0 +1,51 @@ +;;; prelude-mistty.el --- Emacs Prelude: mistty terminal. +;; +;; Copyright © 2011-2026 Bozhidar Batsov +;; +;; Author: Bozhidar Batsov +;; URL: https://github.com/bbatsov/prelude + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; MisTTY is a shell/comint hybrid built on top of term.el: it gives +;; you full terminal emulation for TUI programs while keeping ordinary +;; Emacs editing and motion on the command line. Unlike vterm it's +;; pure Emacs Lisp, so there's no native module to compile. + +;; This module rebinds Prelude's terminal key (`C-c t') from +;; `crux-visit-term-buffer' to `mistty', on the assumption that if +;; you've enabled it you'd rather that key opened MisTTY. + +;;; License: + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. +;; +;; 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Code: + +(require 'prelude-mode) + +(use-package mistty + :ensure t + :commands (mistty) + :init + ;; take over Prelude's terminal key inside prelude-mode-map (a global + ;; binding would be shadowed by that minor-mode map) + (define-key prelude-mode-map (kbd "C-c t") #'mistty)) + +(provide 'prelude-mistty) +;;; prelude-mistty.el ends here diff --git a/modules/prelude-swift.el b/modules/prelude-swift.el new file mode 100644 index 0000000000..fdeea29510 --- /dev/null +++ b/modules/prelude-swift.el @@ -0,0 +1,61 @@ +;;; prelude-swift.el --- Emacs Prelude: Swift programming support. +;; +;; Copyright © 2011-2026 Bozhidar Batsov +;; +;; Author: Bozhidar Batsov +;; URL: https://github.com/bbatsov/prelude + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; Prelude configuration for Swift, based on the tree-sitter powered +;; `swift-ts-mode' and the sourcekit-lsp language server (which ships +;; with the Xcode command line tools or a Swift toolchain). + +;;; License: + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. +;; +;; 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Code: + +(require 'prelude-programming) + +;; You may need to install the following on your system: +;; * a Swift toolchain (swift, swiftc) +;; * sourcekit-lsp (bundled with the Xcode command line tools) + +;; Eglot has no built-in entry for Swift, so point it at sourcekit-lsp. +;; (lsp-mode users get Swift support via the lsp-sourcekit package.) +(with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs '(swift-ts-mode . ("sourcekit-lsp")))) + +(defun prelude-swift-mode-defaults () + "Default coding hook, useful with Swift." + ;; CamelCase aware editing operations + (subword-mode +1) + (prelude-lsp-enable)) + +(setq prelude-swift-mode-hook 'prelude-swift-mode-defaults) + +;; Tree-sitter based major mode for Swift (requires the swift grammar) +(use-package swift-ts-mode + :ensure t + :mode "\\.swift\\'" + :hook (swift-ts-mode . (lambda () (run-hooks 'prelude-swift-mode-hook)))) + +(provide 'prelude-swift) +;;; prelude-swift.el ends here diff --git a/sample/prelude-modules.el b/sample/prelude-modules.el index 166790209e..7f272fd71d 100644 --- a/sample/prelude-modules.el +++ b/sample/prelude-modules.el @@ -94,6 +94,7 @@ ;; (require 'prelude-scheme) (require 'prelude-shell) ;; (require 'prelude-scss) +;; (require 'prelude-swift) ;; (require 'prelude-ts) (require 'prelude-web) ;; Emacs mode for web templates (require 'prelude-xml) @@ -103,6 +104,7 @@ ;; (require 'prelude-ai) ;; LLM-backed chat via gptel ;; (require 'prelude-erc) ;; A popular Emacs IRC client ;; (require 'prelude-forge) ;; GitHub/GitLab/Gitea PRs and issues via Magit +;; (require 'prelude-mistty) ;; MisTTY terminal (pure elisp, no native module; rebinds C-c t) (provide 'prelude-modules) ;;; prelude-modules.el ends here From 4c5ef7e31be97cdea0d83e4f1485516e6e8d96ab Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 18 Jul 2026 17:41:00 +0300 Subject: [PATCH 8/8] Add Embark to the vertico module Bind embark-act (C-.), embark-dwim (C-;) and embark-bindings (C-h B), plus embark-consult for exporting consult results into editable grep/occur buffers. Leave prefix-help-command to which-key and note the Flyspell key overlap in the docs. --- CHANGELOG.md | 1 + docs/modules/vertico.md | 32 ++++++++++++++++++++++++++++++++ modules/prelude-vertico.el | 21 +++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00dcbf9cb7..0d29ce3662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Populate `treesit-language-source-alist` with recipes for the languages Prelude's modules use, so a missing grammar can be installed with `M-x treesit-install-language-grammar` without hunting down repository URLs. - Modernize the Eglot event-log setting to prefer `eglot-events-buffer-config` on newer Eglot, falling back to the obsolete `eglot-events-buffer-size` on older versions. - Bridge Eglot diagnostics into Flycheck via [flycheck-eglot](https://github.com/flycheck/flycheck-eglot) when `prelude-lsp-client` is `eglot`, so LSP diagnostics show up in Prelude's Flycheck UI instead of only through Flymake. Installed on demand; lsp-mode users are unaffected (lsp-mode has its own Flycheck integration). +- Add [Embark](https://github.com/oantolin/embark) (and `embark-consult`) to `prelude-vertico`: a keyboard-driven context menu bound to `C-.` (`embark-act`), `C-;` (`embark-dwim`) and `C-h B` (`embark-bindings`), with `embark-export` into editable grep/occur buffers. In Flyspell buffers those keys keep their Flyspell auto-correct meaning; Embark still works in the minibuffer and elsewhere. - Enable the `vertico-directory` extension in `prelude-vertico`: `RET` descends into the selected directory, `DEL` deletes a whole path component, and `M-DEL` deletes a word of it. - Enable `corfu-history-mode` in `prelude-corfu` so recently chosen completion candidates sort first (persisted across sessions via savehist). - Set `consult-narrow-key` to `<` in `prelude-vertico`, so you can narrow consult candidates to a single group (e.g. `< b` for buffers in `consult-buffer`). diff --git a/docs/modules/vertico.md b/docs/modules/vertico.md index 61a510c352..65b8bc2b39 100644 --- a/docs/modules/vertico.md +++ b/docs/modules/vertico.md @@ -17,8 +17,14 @@ packages for a modern and lightweight completion experience. - [vertico](https://github.com/minad/vertico) - the vertical completion UI - [orderless](https://github.com/oantolin/orderless) - flexible completion style (space-separated patterns) +- [marginalia](https://github.com/minad/marginalia) - rich annotations + in the minibuffer (docstrings, file sizes, etc.) - [consult](https://github.com/minad/consult) - enhanced versions of built-in commands +- [embark](https://github.com/oantolin/embark) - a keyboard-driven + context menu for candidates and things at point +- [embark-consult](https://github.com/oantolin/embark) - integration + between Embark and Consult ## Consult Key Bindings @@ -74,3 +80,29 @@ Prelude: The module configures the `orderless` completion style, which allows you to type space-separated patterns that can match in any order. For example, typing `buf swi` would match `switch-to-buffer`. + +## Acting on Candidates with Embark + +[Embark](https://github.com/oantolin/embark) is a keyboard-driven context +menu. Point it at a minibuffer candidate or a thing at point (a file, a URL, a +symbol, ...) and it offers the actions that make sense for it. + +| Key | Command | Description | +| --- | ------- | ----------- | +| C-. | `embark-act` | Act on the thing at point or candidate | +| C-; | `embark-dwim` | Run the default action directly | +| C-h B | `embark-bindings` | Browse the bindings at point | + +A particularly handy trick is `embark-export`: from a `consult-ripgrep` (or +`consult-line`, etc.) session you can export the whole candidate set into a +proper `grep`/`occur` buffer, which you can then edit in place with +[wgrep](https://github.com/mhayashi1120/Emacs-wgrep). + +!!! Note + + C-. and C-; are also bound by `flyspell-mode` + (auto-correct), so in buffers where Prelude enables Flyspell those keys + keep their Flyspell meaning. Embark still works everywhere else, including + the minibuffer, which is where you'll use it most with Vertico and Consult. + Switch to the `jinx` spell checker (see `prelude-spell-checker`), or rebind + the keys, if you'd rather have Embark at point in prose buffers too. diff --git a/modules/prelude-vertico.el b/modules/prelude-vertico.el index d4845e7d14..48ca504880 100644 --- a/modules/prelude-vertico.el +++ b/modules/prelude-vertico.el @@ -153,5 +153,26 @@ ;; press < again to remove the narrowing (setq consult-narrow-key "<")) +;; Embark - a keyboard-driven context menu. Point it at a minibuffer +;; candidate or a thing at point (a file, a URL, a symbol, ...) and it +;; offers the actions that make sense for it. +;; +;; Note: C-. and C-; are also bound by `flyspell-mode' (auto-correct), +;; so in buffers where Prelude enables Flyspell those keys keep their +;; Flyspell meaning; Embark still works everywhere else, including the +;; minibuffer. We deliberately leave `prefix-help-command' alone so +;; which-key keeps handling the C-h-after-a-prefix help. +(use-package embark + :ensure t + :bind (("C-." . embark-act) ;; act on the thing at point / candidate + ("C-;" . embark-dwim) ;; run the default action + ("C-h B" . embark-bindings))) ;; browse bindings via completing-read + +;; Integration between Embark and Consult, e.g. `embark-export' from a +;; consult-ripgrep session into an editable grep buffer. +(use-package embark-consult + :ensure t + :hook (embark-collect-mode . consult-preview-at-point-mode)) + (provide 'prelude-vertico) ;;; prelude-vertico.el ends here