emacs: accept the wisdom of minimal-emacs.d
6 files changed, 191 insertions(+), 143 deletions(-)
M flake.lock → flake.lock
@@ -350,6 +350,22 @@ "repo": "home-manager", "type": "github" } }, + "minimal-emacs": { + "flake": false, + "locked": { + "lastModified": 1765809829, + "narHash": "sha256-6pa6yDrCDYCf7T1hC72q4dZGKVsjhXGtOH9WxKe7RiY=", + "owner": "jamescherti", + "repo": "minimal-emacs.d", + "rev": "aee53420032eb4121e7ef57ba26a07e4033b1d39", + "type": "github" + }, + "original": { + "owner": "jamescherti", + "repo": "minimal-emacs.d", + "type": "github" + } + }, "mycal": { "inputs": { "flake-utils": "flake-utils_2",@@ -708,6 +724,7 @@ "darwin": "darwin", "elgit": "elgit", "flexoki": "flexoki", "home-manager": "home-manager", + "minimal-emacs": "minimal-emacs", "mycal": "mycal", "nix-rosetta-builder": "nix-rosetta-builder", "nixos-generators": "nixos-generators_2",
M flake.nix → flake.nix
@@ -34,6 +34,10 @@ elgit.url = "git+https://git.alin.ovh/elgit?shallow=0"; mycal.url = "git+https://git.alin.ovh/mycal?shallow=0"; searchix.url = "git+https://git.alin.ovh/searchix?shallow=0"; + minimal-emacs = { + url = "github:jamescherti/minimal-emacs.d"; + flake = false; + }; nixvim = { url = "github:nix-community/nixvim"; inputs.nixpkgs.follows = "nixpkgs";
M user/settings/emacs.nix → user/settings/emacs.nix
@@ -2,6 +2,7 @@ { config, pkgs, lib, + inputs, ... }: let@@ -63,6 +64,7 @@ extraPackages = epkgs: (with epkgs; [ + aggressive-indent apheleia avy buffer-terminator@@ -80,6 +82,7 @@ dtrt-indent envrc editorconfig eldoc-box + elisp-refs esh-buf-stack esh-help eshell-fringe-status@@ -88,7 +91,6 @@ eshell-up evil evil-anzu evil-collection - evil-commentary evil-embrace evil-exchange evil-lion@@ -112,6 +114,7 @@ git-timemachine gl-conf-mode # gitolite goto-chg helpful + highlight-defined jinx just-ts-mode kind-icon@@ -143,7 +146,6 @@ tree-sitter-yaml ] )) try - ultra-scroll vc-msg vertico vertico-prescient@@ -165,10 +167,12 @@ home.packages = with pkgs; [ editorScript enchant ]; - xdg.configFile."emacs/early-init.el" = { - source = direct_symlink "user/settings/emacs/early-init.el"; - }; - xdg.configFile."emacs/init.el" = { - source = direct_symlink "user/settings/emacs/init.el"; + xdg.configFile = { + "emacs/early-init.el".source = "${inputs.minimal-emacs}/early-init.el"; + "emacs/init.el".source = "${inputs.minimal-emacs}/init.el"; + "emacs/pre-early-init.el".source = direct_symlink "user/settings/emacs/pre-early-init.el"; + "emacs/post-early-init.el".source = direct_symlink "user/settings/emacs/post-early-init.el"; + "emacs/pre-init.el".source = direct_symlink "user/settings/emacs/pre-init.el"; + "emacs/post-init.el".source = direct_symlink "user/settings/emacs/post-init.el"; }; }
D user/settings/emacs/early-init.el
@@ -1,12 +0,0 @@ -(setq inhibit-startup-screen t - initial-scratch-message "" - initial-major-mode 'fundamental-mode - frame-inhibit-implied-resize t - byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local cl-functions)) - -;; Disable all the bars, unless on macOS, in which case, keep the menu bar. -(unless (eq window-system 'ns) - (menu-bar-mode -1)) -(scroll-bar-mode -1) -(tool-bar-mode -1) -(set-fringe-mode '(4 . 4))
M user/settings/emacs/init.el → user/settings/emacs/post-init.el
@@ -1,23 +1,4 @@ -;;; init --- user init file -*- lexical-binding: t; -*- -(eval '(setq inhibit-startup-echo-area-message "alan")) -(defvar default-file-name-handler-alist file-name-handler-alist) -(setq file-name-handler-alist nil - gc-cons-threshold most-positive-fixnum - gc-cons-percentage 0.6 - read-process-output-max (* 4 1024 1024)) -(defun set-max-gc-cons () - (setq gc-cons-threshold most-positive-fixnum)) -(defun set-default-gc-cons () - (setq gc-cons-threshold (* 16 1024 1024) - gc-cons-percentage 0.1)) -(add-hook 'minibuffer-setup-hook #'set-max-gc-cons) -(add-hook 'minibuffer-exit-hook #'set-default-gc-cons) -(add-hook 'after-init-hook #'set-default-gc-cons) - -(defun restore-file-name-handler-alist () - (setq file-name-handler-alist default-file-name-handler-alist)) -(add-hook 'emacs-startup-hook #'restore-file-name-handler-alist) - +;;; post-init --- user init file -*- lexical-binding: t; -*- (defun maybe-start-server () (require 'server) (unless (server-running-p)@@ -34,10 +15,11 @@ (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (run-with-idle-timer 300 nil #'package-refresh-contents) -(setq use-package-enable-imenu-support t) -(require 'use-package) +(eval-when-compile + (require 'use-package)) + (setq use-package-always-demand (daemonp) - use-package-compute-statistics nil) + use-package-always-ensure nil) (defmacro quietly (&rest body) `(let ((inhibit-message t))@@ -62,14 +44,6 @@ (general-unbind "s-x")))) ;;; Styles -;; I prefer an always-visible cursor. Feels less distracting. -(blink-cursor-mode -1) - -(setq-default truncate-lines t) - -;; Don't ring the bell -(setq ring-bell-function #'ignore) - (use-package exec-path-from-shell :if (eq system-type 'darwin) :config (progn@@ -78,16 +52,8 @@ (global-set-key (kbd "<pinch>") 'ignore) (global-set-key (kbd "<C-wheel-up>") 'ignore) (global-set-key (kbd "<C-wheel-down>") 'ignore) - -(setq font-lock-maximum-decoration '((t . 1)) - jit-lock-stealth-time 1.25 - jit-lock-stealth-nice 0.5 - jit-lock-chunk-size 4096) - -(context-menu-mode +1) ;;; Chrome -(column-number-mode -1) (line-number-mode -1) (use-package nerd-icons@@ -113,14 +79,11 @@ (when (eq system-type 'darwin) (add-to-list 'default-frame-alist '(undecorated . t)) (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)) - (add-to-list 'default-frame-alist '(ns-appearance . 'light)) - (setq ns-use-srgb-colorspace nil)) + (add-to-list 'default-frame-alist '(ns-appearance . 'light))) (add-to-list 'default-frame-alist '(width . 100)) (add-to-list 'default-frame-alist '(height . 40)) -(setq-default display-line-numbers 'relative - display-line-numbers-widen t - display-line-numbers-width 3) +(setq-default display-line-numbers 'relative) (defun turn-off-display-line-numbers-mode () (interactive)@@ -131,7 +94,6 @@ (interactive) (display-line-numbers-mode (default-value 'display-line-numbers))) (setq frame-resize-pixelwise t - window-resize-pixelwise t display-buffer-alist `(("\\*\\(?:shell\\|compilation\\)\\*" display-buffer-in-side-window (side . bottom) (slot . 0) (preserve-size . (nil . t)) (no-other-window . t) (no-delete-other-windows . t))))@@ -175,19 +137,20 @@ (add-hook 'evil-insert-state-entry-hook #'noct-absolute) (add-hook 'evil-insert-state-exit-hook #'noct-relative) -(use-package ultra-scroll - :defer 1 +(use-package pixel-scroll + :ensure nil + :if (not ;; i.e. not emacs-macport + (or (eq window-system 'mac) + (bound-and-true-p mac-carbon-version-string))) :config (progn - (setq scroll-margin 0 - scroll-conservatively 101) - (ultra-scroll-mode +1))) + (setq pixel-scroll-precision-use-momentum t) + (pixel-scroll-precision-mode 1) )) ;;; Encoding (setq-default bidi-paragraph-direction 'left-to-right bidi-display-reordering 'left-to-right) -(setq bidi-inhibit-bpa t - require-final-newline t) +(setq bidi-inhibit-bpa t) (add-to-list 'auto-coding-alist '("prescient-save\\.el\\'" . utf-8-emacs)) ;;; Dates & Times@@ -255,6 +218,10 @@ (define-key global-map (kbd "s-k") #'kill-current-buffer :remove)) (use-package avy :defer 2 + :commands (avy-goto-char + avy-goto-char-2 + avy-next) + :general ("C-'" #'avy-goto-char-2) :config (setq avy-all-windows nil)) (use-package link-hint@@ -264,32 +231,56 @@ ;; Popup keybindings following a prefix automatically. (use-package which-key :defer 5 + :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) :config (progn (which-key-mode +1) (which-key-setup-side-window-right-bottom))) ;;; Minibuffer -(setq enable-recursive-minibuffers t - save-silently t - uniquify-buffer-name-style 'forward - read-extended-command-predicate #'command-completion-default-include-p) +(setq save-silently t) (minibuffer-depth-indicate-mode t) (use-package hydra :defer 2) (use-package recentf - :defer 1 - :custom (recentf-auto-cleanup 1800) + :ensure nil + :commands (recentf-mode recentf-cleanup) + :custom + (recentf-auto-cleanup (if (daemonp) 1800 'never)) + (recentf-exclude + (list "\\.tar$" "\\.tbz2$" "\\.tbz$" "\\.tgz$" "\\.bz2$" + "\\.bz$" "\\.gz$" "\\.gzip$" "\\.xz$" "\\.zip$" + "\\.7z$" "\\.rar$" + "COMMIT_EDITMSG\\'" + "\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\|bmp\\|xpm\\)$" + "-autoloads\\.el$" "autoload\\.el$")) + :config (progn - (quietly (recentf-mode +1)) - (advice-add 'recentf-cleanup :around #'quiet))) + ;; A cleanup depth of -90 ensures that `recentf-cleanup' runs before + ;; `recentf-save-list', allowing stale entries to be removed before the list + ;; is saved by `recentf-save-list', which is automatically added to + ;; `kill-emacs-hook' by `recentf-mode'. + (add-hook 'kill-emacs-hook #'recentf-cleanup -90))) (use-package savehist - :init (savehist-mode +1) + :ensure nil + :commands (savehist-mode savehist-save) :config (progn - (add-to-list 'savehist-additional-variables 'command-history))) + (quietly (recentf-mode +1)) + (advice-add 'recentf-cleanup :around #'quiet)) + :custom + (savehist-autosave-interval 600) + (savehist-additional-variables + '(kill-ring ; clipboard + register-alist ; macros + mark-ring global-mark-ring ; marks + search-ring regexp-search-ring))) (use-package persist-state :defer 1@@ -313,9 +304,10 @@ (vertico-prescient-mode +1))) (use-package marginalia :after vertico + :commands (marginalia-mode marginalia-cycle) + :hook (after-init . marginalia-mode) :general (:keymaps 'minibuffer-local-map - "M-A" #'marginalia-cycle) - :init (marginalia-mode +1)) + "M-A" #'marginalia-cycle)) (setq completion-ignore-case t read-buffer-completion-ignore-case t@@ -324,6 +316,8 @@ completion-styles '(flex substring basic) completion-category-overrides '((file (styles basic partial-completion)))) (use-package consult + ;; Enable automatic preview at point in the *Completions* buffer. + :hook (completion-list-mode . consult-preview-at-point-mode) :general ([remap isearch-forward] #'consult-line [remap isearch-backward] #'consult-line [remap project-switch-to-buffer] #'consult-project-buffer@@ -374,21 +368,6 @@ (unpackaged/smerge-hydra/body))))) ;;; Windows -(defun split-window-properly (&optional window) - (let ((window (or window (selected-window)))) - (or (and (window-splittable-p window) - ;; Split window vertically. - (with-selected-window window - (split-window-below))) - (and (window-splittable-p window t) - ;; Split window horizontally. - (with-selected-window window - (split-window-right)))))) - -(setq split-window-preferred-function #'split-window-properly - split-height-threshold 20 - split-width-threshold 160) - (use-package winner :defer 8 :config (progn@@ -493,10 +472,12 @@ :after evil :config (progn (evil-exchange-cx-install))) -(use-package evil-commentary - :after evil - :defer 2 - :config (evil-commentary-mode +1)) +(with-eval-after-load "evil" + (evil-define-operator my-evil-comment-or-uncomment (beg end) + "Toggle comment for the region between BEG and END." + (interactive "<r>") + (comment-or-uncomment-region beg end)) + (evil-define-key 'normal 'global (kbd "gc") 'my-evil-comment-or-uncomment)) (use-package evil-lion :after evil@@ -562,31 +543,40 @@ ;;; Completion (use-package corfu :defer 1 + :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) + (corfu-auto t) + (corfu-auto-delay 0.1) + (corfu-auto-prefix 3) + (corfu-on-exact-match nil) + (corfu-preview-current nil) + (corfu-preselect 'prompt) + (corfu-on-exact-match 'quit) :config (progn (global-corfu-mode +1) - (setq corfu-auto t - corfu-auto-delay 0.1 - corfu-auto-prefix 3 - corfu-on-exact-match nil - corfu-preview-current nil - corfu-preselect 'prompt - corfu-on-exact-match 'quit) - (defun corfu-enable-in-minibuffer () - "Enable Corfu in the minibuffer if `completion-at-point' is bound." - (when (where-is-internal #'completion-at-point (list (current-local-map))) - (setq-local corfu-auto nil) ;; Enable/disable auto completion - (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup - corfu-popupinfo-delay nil) - (corfu-mode 1))) - (add-hook 'minibuffer-setup-hook #'corfu-enable-in-minibuffer) (add-hook 'eshell-mode-hook (lambda () (setq-local corfu-auto nil) (corfu-mode +1))))) (use-package cape :after (corfu) + :commands (cape-dabbrev cape-file cape-elisp-block) :general (:states 'insert - "C-x C-f" #'cape-file)) + "C-x C-f" #'cape-file + "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)) (use-package kind-icon :after (corfu)@@ -624,26 +614,37 @@ :defer 15 :general ([remap help-map] 'electric-help-map)) (use-package helpful - :general (ehelp-map - "k" #'helpful-key - "v" #'helpful-variable - "f" #'helpful-callable)) + :ensure 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)) ;;; Files ;;;; Auto-saving -(setq auto-save-default nil - make-backup-files nil - create-lockfiles nil) +(setq auto-save-default t + auto-save-interval 300 + auto-save-timeout 30) ;;;; Auto-reloading (use-package autorevert - :config (progn - (setq auto-revert-verbose nil - auto-revert-use-notify t) - (global-auto-revert-mode 1))) + :ensure nil + :commands (auto-revert-mode global-auto-revert-mode) + :hook (after-init . global-auto-revert-mode) + :custom (auto-revert-verbose nil)) (setq delete-by-moving-to-trash t)@@ -690,17 +691,26 @@ (remove-hook 'compilation-mode-hook #'tramp-compile-disable-ssh-controlmaster-options)))) ;;; Directories -(setq dired-dwim-target t - dired-recursive-copies 'top - dired-listing-switches "-alh" +(setq dired-listing-switches "-alhv" dired-kill-when-opening-new-dired-buffer t + dired-movement-style 'bounded-files dired-recursive-deletes (if delete-by-moving-to-trash 'always 'top)) -(add-hook 'dired-mode-hook - (lambda () - (dired-hide-details-mode))) +(add-hook 'dired-mode-hook #'dired-hide-details-mode) + +(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) ;;; Shells@@ -760,14 +770,11 @@ "C-d" #'comint-delchar-or-maybe-eof)) (use-package comint :defer t + :ensure nil :general (:keymaps 'comint-mode-map "C-c C-l" #'counsel-shell-history)) ;;; Editing - -(setq-default tab-always-indent 'complete - indent-tabs-mode nil - tab-width 4) (if (fboundp 'kill-ring-deindent-mode) (kill-ring-deindent-mode +1))@@ -894,6 +901,21 @@ :ghook '(emacs-lisp-mode-hook clojure-mode-hook racket-mode-hook)) +(use-package aggressive-indent + :ghook '(emacs-lisp-mode-hook + clojure-mode-hook + racket-mode-hook)) + +(use-package highlight-defined-mode + :ghook '(emacs-lisp-mode-hook)) + +(use-package elisp-refs + :commands (elisp-refs-function + elisp-refs-macro + elisp-refs-variable + elisp-refs-special + elisp-refs-symbol)) + ;;;; org (use-package org@@ -1100,7 +1122,11 @@ (apheleia-mode -1))) (remove-hook 'before-save-hook #'eglot-format-buffer 'local))) (use-package eglot + :ensure nil :defer 3 + :commands (eglot-ensure + eglot-rename + eglot-format-buffer) :general (:states 'normal :keymaps 'eglot-mode-map "gr" #'xref-find-references "C-t" #'xref-go-back)@@ -1182,7 +1208,6 @@ ;;;; Reformat on save (use-package apheleia - :defer 11 :ghook 'prog-mode-hook :config (progn (setf (alist-get 'shfmt apheleia-formatters)@@ -1195,9 +1220,7 @@ (prettier-gotmpl "prettier" "--stdin-filepath" filepath "--parser=go-template" (apheleia-formatters-indent "--use-tabs" "--tab-width"))))) (setf (alist-get 'sql-mode apheleia-mode-alist) 'sqlfluff) - (setq apheleia-mode-alist (append apheleia-mode-alist '((toml-ts-mode . taplo))))) - :init (progn - (apheleia-global-mode +1))) + (setq apheleia-mode-alist (append apheleia-mode-alist '((toml-ts-mode . taplo)))))) ;;; Take me to my leader
A user/settings/emacs/pre-early-init.el
@@ -0,0 +1,12 @@ +;;; pre-early-init --- -*- no-byte-compile: t; lexical-binding: t; -*- +(setq custom-file (expand-file-name "custom.el" user-emacs-directory) + user-emacs-directory (expand-file-name "var/" minimal-emacs-user-directory)) + +(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)