Files
emacs/config.org
2025-10-30 14:35:56 -05:00

22 KiB

My Ever Changing Literate Emacs Configuration

Quick Look

./img/preview.png

Just the usual flex of doing as little editing as possible in a screenshot.

Motivation

Over a surprisingly short amount of time my emacs configuration has become quite a mess. Almost every visit to it requiring some digging to get to the relevant configuration. So as a result I've decided to make a literate configuration. Mostly for my own mental house keeping. However, if some poor soul reads this and finds it useful, then that's a bonus.

Inspirations, and sometimes outright copy/paste sources

Initial Bits and Bobs

Before anything else the appearance, and some performance settings need to be tweaked as they can cause issues if done mid-start.

Initial setup

The LSP packages perform better with plists so an environment variable needs to be set to inform them that this is intended. I've also removed default package handling as I intend to use another package manager. Lastly I suppress some compilation warnings.

(setq package-enable-at-startup nil)
(setq inhibit-default-init nil)

(setq native-comp-async-report-warnings-errors nil)
(setenv "LSP_USE_PLISTS" "true")
(setq lsp-use-plists t)

Appearance

I like a minimal look, so I disable menu bars, tool bars, all the bars. I have Emacs loading as a blank slate with only the scratch buffer open.

(defvar default-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)

(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

(setq server-client-instructions nil)

(when (and (fboundp 'startup-redirect-eln-cache)
           (fboundp 'native-comp-available-p)
           (native-comp-available-p))
  (startup-redirect-eln-cache
   (convert-standard-filename
    (expand-file-name  "var/eln-cache/" user-emacs-directory))))

(setq frame-inhibit-implied-resize t)
(advice-add #'x-apply-session-resources :override #'ignore)
(setq desktop-restore-forces-onscreen nil)
(setq ring-bell-function #'ignore
      inhibit-startup-screen t)

(push '(font . "Victor Mono-13") default-frame-alist)
(set-face-font 'default "Victor Mono-13")
(set-face-font 'variable-pitch "Victor Mono-13")

(copy-face 'default 'fixed-pitch)

(provide 'early-init)
;;; early-init.el ends here

;; Local Variables:
;; no-byte-compile: t
;; no-native-compile: t
;; no-update-autoloads: t
;; End:

Package Management

I am using Elpaca as my package manager. I've found it to be quite quick, and easy to use.

Elpaca supports use-package so hook that up, and add a use-feature macro that adds a similar construct for configuring already loaded emacs packages and features.

<<elpaca-boilerplate>>

(defmacro use-feature (name &rest args)
  "Like `use-package' but accounting for asynchronous installation.
  NAME and ARGS are in `use-package'."
  (declare (indent defun))
  `(use-package ,name
     :ensure nil
     ,@args))

(elpaca elpaca-use-package
  (require 'elpaca-use-package)
  (elpaca-use-package-mode)
  (setq use-package-always-ensure t))

Modern Completion Stack

I'm using modern to mean current, and as the colloquial usage given by the community at large. At least based on my observations anyway. Most of these serve to bolster the existing facilities of emacs rather than replace them.

Prescient

Prescient provides sorting for completion candidates; So recently used, and frequent selections come first.

(use-package prescient
  :defer t
  :config
  (prescient-persist-mode))

(use-package corfu-prescient
  :after (corfu prescient)
  :config (corfu-prescient-mode))

(use-package vertico-prescient
  :after (prescient vertico))

Corfu

Corfu provides completion within buffers from various sources. Though it doesn't provide much in the way of sources itself. It works in conjunction with the other packages in this section to provide it with candidates.

(use-package corfu
  :custom
  (corfu-auto t)               ;; Enable auto completion
  (corfu-preselect 'directory) ;; Select the first candidate, except for directories

  :init
  (global-corfu-mode)
  (corfu-popupinfo-mode) ;; Show docs next to candidates.

  :config
  ;; Free the RET key for less intrusive behavior.
  ;; Option 1: Unbind RET completely
  ;; (keymap-unset corfu-map "RET")
  ;; Option 2: Use RET only in shell modes
  (keymap-set corfu-map "RET" `( menu-item "" nil :filter
                                 ,(lambda (&optional _)
                                    (and (derived-mode-p 'eshell-mode 'comint-mode)
                                         #'corfu-send)))))

Cape

The Cape package (Completion At Point Extensions) provides access to Corfu to various backends. Things like file completions and simple buffer completion are examples of good backends to add here. Other backends are listed here.

(use-package cape
  :bind ("M-<tab>" . cape-prefix-map)
  :init
  (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))

Orderless

This provides numerous flexible methods for matching completion candidates. Rather than just matching strings exactly it can also match portions, or a custom regular expression, and more.

Link

(use-package orderless
  :ensure t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles partial-completion)))))

Vertico

Vertico is one of the best minibuffer improvement packages out there. Combined with the other packages in this section it makes minibuffer completions concise, and descriptive.

(use-package vertico
  :demand t
  :custom (vertico-cycle t)
  :config
  (setf (car vertico-multiline) "\n") ;; don't replace newlines
  (vertico-mode)
  (vertico-prescient-mode)
  (define-key vertico-map (kbd "C-h") #'+minibuffer-up-dir))

Marginalia

Marginalia adds completion annotations on the right side of the minibuffer.

(use-package marginalia
  :defer 2
  :config (marginalia-mode)
  (setf (alist-get 'elpaca-info marginalia-command-categories) 'elpaca))

Consult

Consult provides search and navigation commands based on the emacs completion function completing-read.

Think about this as a tightly integrate search that can tie into many aspects of a project, and convey the results to various completion facilities.

(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)     ;; orig. repeat-complex-command
         ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
         ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
         ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
         ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
         ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
         ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)
         ;; Other custom bindings
         ("M-y" . consult-yank-pop)                ;; orig. yank-pop
         ;; M-g bindings in `goto-map'
         ("M-g e" . consult-compile-error)
         ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
         ("M-g g" . consult-goto-line)             ;; orig. goto-line
         ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
         ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)
         ;; M-s bindings in `search-map'
         ("M-s d" . consult-find)                  ;; Alternative: consult-fd
         ("M-s c" . consult-locate)
         ("M-s g" . consult-grep)
         ("M-s G" . consult-git-grep)
         ("M-s r" . consult-ripgrep)
         ("M-s l" . consult-line)
         ("M-s L" . consult-line-multi)
         ("M-s k" . consult-keep-lines)
         ("M-s u" . consult-focus-lines)
         ;; Isearch integration
         ("M-s e" . consult-isearch-history)
         :map isearch-mode-map
         ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
         ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
         ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
         ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
         ;; Minibuffer history
         :map minibuffer-local-map
         ("M-s" . consult-history)                 ;; orig. next-matching-history-element
         ("M-r" . consult-history))                ;; orig. previous-matching-history-element

  ;; Enable automatic preview at point in the *Completions* buffer. This is
  ;; relevant when you use the default completion UI.
  :hook (completion-list-mode . consult-preview-at-point-mode)

  ;; The :init configuration is always executed (Not lazy)
  :init

  ;; Tweak the register preview for `consult-register-load',
  ;; `consult-register-store' and the built-in commands.  This improves the
  ;; register formatting, adds thin separator lines, register sorting and hides
  ;; the window mode line.
  (advice-add #'register-preview :override #'consult-register-window)  (setq register-preview-delay 0.5)

  ;; Use Consult to select xref locations with preview
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)

  ;; Configure other variables and modes in the :config section,
  ;; after lazily loading the package.
  :config

  ;; Optionally configure preview. The default value
  ;; is 'any, such that any key triggers the preview.
  ;; (setq consult-preview-key 'any)
  ;; (setq consult-preview-key "M-.")
  ;; (setq consult-preview-key '("S-<down>" "S-<up>"))
  ;; For some commands and buffer sources it is useful to configure the
  ;; :preview-key on a per-command basis using the `consult-customize' macro.
  (consult-customize
   consult-theme :preview-key '(:debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep consult-man
   consult-bookmark consult-recent-file consult-xref
   consult--source-bookmark consult--source-file-register
   consult--source-recent-file consult--source-project-recent-file
   ;; :preview-key "M-."
   :preview-key '(:debounce 0.4 any))

  ;; Optionally configure the narrowing key.
  ;; Both < and C-+ work reasonably well.
  (setq consult-narrow-key "<") ;; "C-+"

  ;; Optionally make narrowing help available in the minibuffer.
  ;; You may want to use `embark-prefix-help-command' or which-key instead.
  ;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
  )

=====

>>>>>>> caf7087 (Moved completion section into its own file.)

Tree Sitter

Tree sitter is included with emacs, but there are a couple of packages that make managing tree sitter easier. Mainly with automatically updating, and using tree sitter versions of major modes, and installing the parsers if needed.

;; Treesit
(setq treesit-language-source-alist
      '((rust "https://github.com/tree-sitter/tree-sitter-rust")))

(use-package treesit-auto
  :custom
  (treesit-auto-install 'prompt)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all)
  (global-treesit-auto-mode))

(use-package treesit-fold
  :ensure t
  :defer t)

Major Modes

I use quite a few major modes. Mostly those using tree-sitter; Which should be selected automatically. As most of these are pretty straight forward I won't bother with an explanation on each.

;; Markdown
(use-package markdown-mode
  :ensure t
  :mode ("\\.md\\'" . gfm-mode)
  :init (setq markdown-command "multimarkdown")
  :bind (:map markdown-mode-map
              ("C-c C-e" . markdown-do)))

(use-package slint-mode
  :defer t
  :config
  (add-to-list 'auto-mode-alist '("\\.slint\\'" . slint-mode)))

(use-package zig-mode
  :defer t
  :config
  (add-to-list 'auto-mode-alist '("\\.\\(zig\\|zon\\)\\'" . zig-mode)))

(use-package rainbow-mode
  :commands (rainbow-mode))

;; Clojure
(use-package clojure-ts-mode
  :ensure t
  :hook
  ((clojure-ts-mode . cider-mode)
   (clojure-ts-mode . rainbow-delimiters-mode)
   (clojure-ts-mode . clj-refactor-mode)))

;; Gleam
(use-package gleam-ts-mode
  :mode (rx ".gleam" eos))

(use-package cider
  :ensure t
  :defer t)

(use-package inf-elixir
  :defer t)

;; Go
(use-package go-mode
  :demand t
  :mode "\\.go\\'")

;; Meson
(use-package meson-mode
  :demand t
  :mode "\\.build\\'")

;; rust-mode
(use-package rust-mode
  :ensure t
  :init
  (setq rust-mode-treesitter-derive t))

(use-package rustic
  :ensure (:host github :repo "emacs-rustic/rustic")
  :after (rust-ts-mode)
  :config
  (setq rustic-cargo-clippy-trigger-fix 'on-compile
        rustic-rustfmt-args "+nightly"))

;; Scheme
(use-package geiser-chez :ensure t)

Org Mode

Org mode is a handy note taking, todo list managing, journal writing, and literate programming tool among other things. I use it for writing some of my configurations, and managing my notes.

(use-feature org
  :defer t
  :config
  (setq org-confirm-babel-evaluate nil)
  :custom
  (org-ellipsis (nth 5 '("↴" "˅" "…" " ⬙" " ▽" "▿")))
  (org-priority-lowest ?D)
  (org-fontify-done-headline t)
  (global-set-key (kbd "C-c l") #'org-store-link)
  (global-set-key (kbd "C-c a") #'org-agenda)
  (global-set-key (kbd "C-c c") #'org-capture))

Htmlize

The htmlize package enables exporting org documents, and other buffers into HTML format.

(use-package htmlize
  :after (org)
  :defer t)

Org Modern

org-modern provides a cleaner representation of org documents while being edited. It displays the intended formatting without all the markup.

(use-package org-modern
  :after (org)
  :config
  (global-org-modern-mode)
  (remove-hook 'org-agenda-finalize-hook 'org-modern-agenda))

Literate Tools

These are packages useful for literate programming, and its presentation. Though not necessarily exlusive to literate programming as they can improve the look of most any org document.

(use-feature ob-tangle
  :after (org)
  :custom
  (org-src-window-setup 'current-window)
  (org-src-preserve-indentation t))

;; Maybe unnecessary... I'll see.
(use-package org-contrib)

(use-package org-make-toc
  :commands (org-make-toc))

Note Taking

Org-roam is my go to for note taking. While out of the box it isn't quite as snazzy as something like Obsidian it does offer a lot of flexibility that no other note taking tool has.

(use-package org-roam
  :after (org)
  :ensure t
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert))
  :config
  (setq org-roam-directory "~/Documents/org-roam"
        org-roam-dailies-directory "daily/")

  (setq org-roam-capture-templates
        '(("d" "default plain" plain
           "%?"
           :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
                              "#+title: ${title}\n"))
          ("D" "default encrypted" plain
           "%?"
           :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org.gpg"
                              "#+title: ${title}\n"))))

  (setq org-roam-dailies-capture-templates
        '(("d" "default" plain
           "%?"
           :target (file+head "%<%Y-%m-%d>.org"
                              "#+title: %<%Y-%m-%d>\n\n* Tasks\n\n* Notes"))))

  (org-roam-db-autosync-mode))

(use-package orgmdb
  :ensure t)

(use-package org-roam-ui
  :after (org-roam)
  :ensure t
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))