Files
emacs/config.org

479 lines
17 KiB
Org Mode
Raw Normal View History

2025-09-11 14:47:19 -05:00
#+TITLE: My Ever Changing Literate Emacs Configuration
#+AUTHOR: xulfer <xulfer at cheapbsd.net>
#+PROPERTY: header-args :tangle init.el :noweb yes :exports code
# For PDF export allow line wrapping.
#+LATEX_HEADER: \usepackage{listings}
#+LATEX_HEADER: \lstset{breaklines=true, breakatwhitespace=true, basicstyle=\ttfamily\footnotesize, columns=fullflexible}
#+begin_src emacs-lisp :exports none :tangle no
;; This asks for a file which it uses to store the processed org
;; data with the includes into. It can be anything and can safely
;; be deleted afterwards. I think README.org is a good file to
;; pick if using a repository.
2025-09-11 14:47:19 -05:00
(defun tangle-literate-config ()
"Tangle config files with proper include handling."
2025-09-11 14:47:19 -05:00
(interactive)
(let ((org-confirm-babel-evaluate nil))
;; Create a temporary buffer with expanded includes
(with-temp-buffer
(insert-file-contents "config.org")
(org-mode)
;; This expands all #+INCLUDE directives
(org-export-expand-include-keyword)
;; Now tangle from this buffer which has everything in one place
(org-babel-tangle nil "init.el"))
(message "Configuration tangled!")))
2025-09-11 14:47:19 -05:00
#+end_src
#+BEGIN_SRC emacs-lisp :exports none
;;; init.el -*- lexical-binding: t; -*-
;;; Code:
#+END_SRC
#+begin_src emacs-lisp :tangle early-init.el :exports none
;;; early-init.el -*- lexical-binding: t; -*-
;;; Code:
#+end_src
* 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
- [[https://github.com/progfolio/.emacs.d][Progfolio's Config]]
* 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.
#+begin_src emacs-lisp :tangle early-init.el
(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)
2025-09-11 14:47:19 -05:00
#+end_src
** 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.
#+begin_src emacs-lisp :tangle early-init.el
(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:
#+end_src
* Package Management
I am using [[https://github.com/progfolio/elpaca][Elpaca]] as my package manager. I've found it to be quite quick, and easy to
use.
#+name: elpaca-boilerplate
#+begin_src emacs-lisp :exports none :tangle no
(defvar elpaca-installer-version 0.11)
(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)
(let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
(if debug-on-error
(setq use-package-verbose t
use-package-expand-minimally nil
use-package-compute-statistics t)
(setq use-package-verbose nil
use-package-expand-minimally t))
#+end_src
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.
#+begin_src emacs-lisp
<<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))
#+end_src
#+INCLUDE: "config/editing.org" :minlevel 1
#+INCLUDE: "config/gc.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/tidy.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/auth.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/profiling.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/general.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/formatting.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/documentation.org" :minlevel 1
2025-09-19 16:05:37 -05:00
#+INCLUDE: "config/social.org" :minlevel 1
2025-09-11 14:47:19 -05:00
2025-10-30 13:50:08 -05:00
#+INCLUDE: "config/modal.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/qol.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/bufferlo.org" :minlevel 1
#+INCLUDE: "config/error.org" :minlevel 1
2025-09-11 14:47:19 -05:00
2025-10-30 13:53:37 -05:00
#+INCLUDE: "config/error.org" :minlevel 1
2025-09-11 14:47:19 -05:00
* 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
[[https://github.com/radian-software/prescient.el][Prescient]] provides sorting for completion candidates; So recently used, and
frequent selections come first.
#+begin_src emacs-lisp
(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))
#+end_src
** Corfu
[[https://github.com/minad/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.
#+begin_src emacs-lisp
(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.
2025-09-11 14:47:19 -05:00
: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)))))
#+end_src
** Cape
The [[https://github.com/minad/cape][Cape]] package (Completion At Point Extensions) provides access to [[https://github.com/minad/corfu][Corfu]]
to various backends. Things like file completions and simple buffer
completion are examples of good backends to add here. Other backends
are listed [[https://github.com/minad/cape#available-capfs][here]].
#+begin_src emacs-lisp
(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))
#+end_src
** 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.
[[https://stable.melpa.org/#/orderless][Link]]
#+begin_src emacs-lisp
(use-package orderless
:ensure t
2025-09-11 14:47:19 -05:00
:custom
(completion-styles '(orderless basic))
(completion-category-defaults nil)
(completion-category-overrides '((file (styles partial-completion)))))
#+end_src
** Vertico
[[https://github.com/minad/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.
#+begin_src emacs-lisp
(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))
#+end_src
** Marginalia
[[https://github.com/minad/marginalia][Marginalia]] adds completion annotations on the right side of the minibuffer.
#+begin_src emacs-lisp
(use-package marginalia
:defer 2
:config (marginalia-mode)
(setf (alist-get 'elpaca-info marginalia-command-categories) 'elpaca))
#+end_src
** Consult
#+begin_quote
[[https://github.com/minad/consult][Consult]] provides search and navigation commands based on the emacs completion
function completing-read.
#+end_quote
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.
#+begin_src emacs-lisp
(use-package consult
:ensure t
2025-09-11 14:47:19 -05:00
: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)
)
#+end_src
=======
#+INCLUDE: "config/completion.org" :minlevel 1
>>>>>>> caf7087 (Moved completion section into its own file.)
2025-09-11 14:47:19 -05:00
2025-10-30 14:02:08 -05:00
#+INCLUDE: "config/files.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/terminal.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/style.org" :minlevel 1
2025-09-11 14:47:19 -05:00
2025-10-30 14:05:18 -05:00
#+INCLUDE: "config/vcs.org" :minlevel 1
2025-09-11 14:47:19 -05:00
2025-10-30 14:06:17 -05:00
#+INCLUDE: "config/lsp.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/treesitter.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/major_modes.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/org_mode.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+INCLUDE: "config/custom.org" :minlevel 1
2025-09-11 14:47:19 -05:00
#+begin_src emacs-lisp :exports none
2025-09-13 16:10:38 -05:00
(maybe-load-rel "custom.el")
2025-09-11 14:47:19 -05:00
(provide 'init)
;;; init.el ends here
#+end_src