I try to stay out of my configs as much as possible, and to help me do that I keep a few things in mind:
- only configure what I need.
- keep everything declarative.
- use tools with sane defaults.
Sometimes I don’t adhere to these, it is what it is.
Declaring upstream package archives: ELPA and MELPA.
(require 'package)
(setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")))
(package-initialize)
use-package lets me declare and configure packages inside of a single macro. This keeps everything relatively declarative and isolated.
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
exec-path-from-shell ensures that Emacs will inherit my environment. I connect to Emacs from a client, so this configurations runs conditionally if Emacs is a daemon.
(use-package exec-path-from-shell
:ensure t
:if (daemonp)
:config
(setq exec-path-from-shell-variables '("PATH"))
(exec-path-from-shell-initialize))
Customizations that emacs tries to add are stored in a separate file. I don’t want to declare things like font size or color themes so they reside here.
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)
By default, scratch is an empty org-mode buffer. This makes it easy for me to write random things down.
(setq default-directory "~/")
(setq initial-major-mode 'org-mode)
(setq inhibit-startup-message t)
(setq initial-scratch-message nil)
When operating on regions, this will remove a highlighted region before text insertion, giving the behavior of replacing text.
(delete-selection-mode)
When using the clipboard, setting select-enable-clipboard
to t
I can copy
and paste between Emacs and the system.
(setq select-enable-clipboard t)
Many languages, linters, and operating systems require the presence of an empty line at the bottom of a file.
(setq require-final-newline t)
Enables the mouse wheel, which I prefer over the typical scrolling keychords.
(mouse-wheel-mode t)
(setq mouse-drag-copy-region nil)
I keep all backups and auto-save files in the temporary directory to avoid littering.
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))
(setq tramp-backup-directory-alist backup-directory-alist)
(setq auto-save-list-file-prefix temporary-file-directory)
the authinfo file keeps sensitive information out of my configuration. It’s not checked into git, and needs my gpg key to be accessed.
(setq auth-source-debug t)
(setq auth-sources '((:source "~/.emacs.d/.authinfo.gpg")))
I’m ensuring that I use ligature-friendly fonts. This is just for aesthetics it doesn’t help with anything.
(add-to-list 'default-frame-alist '(font . "Fira Code"))
(set-face-attribute 'default t :font "Fira Code")
diminish keeps minor modes from cluttering up the modeline. I don’t need an indication that most things are running.
(use-package diminish :ensure t)
displaying certain words in their greek letter makes me feel smart and superior
(global-prettify-symbols-mode 1)
Prevents the operating system’s dialog boxes from popping up with messages from Emacs. They’re annoying and get in the way.
(setq use-dialog-box nil)
I recover a little surface area by removing the various bars from the editor. When I was learning Emacs having those menus visible was helpful.
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
Highlights the current line the cursor is on. Very helpful for not getting lost.
(global-hl-line-mode t)
Lets me see what line and column number I’m on. I obsess about this.
(column-number-mode)
show-paren-mode highlights matching pairs of parentheses or brackets. By setting
show-paren-style
to mixed
the enclosed expression will be highlighted
instead if there is no matching parentheses.
(show-paren-mode)
(setq show-paren-style 'mixed)
electric-pair-mode will automatically insert a matching delimiter after one is inserted, with the cursor moved between them.
(electric-pair-mode 1)
(electric-indent-mode -1)
rainbow-delimiters highlights delimiters according to their depth. This makes it really easy to see how deeply nested in a loop you are, or where you are in a lisp form.
(use-package rainbow-delimiters
:ensure t
:hook (prog-mode . rainbow-delimiters-mode))
A full ligature configuration for Cascadia and FiraCode fonts. Just try not to look at this section if you don’t have to.
(use-package ligature
:ensure t
:config
(ligature-set-ligatures 't '("www"))
(ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
(ligature-set-ligatures 'prog-mode
'(("=" (rx (+ (or ">" "<" "|" "/" "~" ":" "!" "="))))
(";" (rx (+ ";")))
("&" (rx (+ "&")))
("!" (rx (+ (or "=" "!" "\." ":" "~"))))
("?" (rx (or ":" "=" "\." (+ "?"))))
("%" (rx (+ "%")))
("|" (rx (+ (or ">" "<" "|" "/" ":" "!" "}" "\]"
"-" "=" ))))
("\\" (rx (or "/" (+ "\\"))))
("+" (rx (or ">" (+ "+"))))
(":" (rx (or ">" "<" "=" "//" ":=" (+ ":"))))
("/" (rx (+ (or ">" "<" "|" "/" "\\" "\*" "!"
"="))))
("\." (rx (or "=" "-" "\?" "\.=" "\.<" (+ "\."))))
("-" (rx (+ (or ">" "<" "|" "~" "-"))))
("*" (rx (or ">" "/" ")" (+ "*"))))
("w" (rx (+ "w")))
("<" (rx (+ (or "\+" "\*" "\$" "<" ">" ":" "~" "!"
"-" "/" "|" "="))))
(">" (rx (+ (or ">" "<" "|" "/" ":" "=" "-"))))
("#" (rx (or ":" "=" "!" "(" "\?" "\[" "{" "_(" "_"
(+ "#"))))
("~" (rx (or ">" "=" "-" "@" "~>" (+ "~"))))
("_" (rx (+ (or "_" "|"))))
("0" (rx (and "x" (+ (in "A-F" "a-f" "0-9")))))
"Fl" "Tl" "fi" "fj" "fl" "ft"
"{|" "[|" "]#" "(*" "}#" "$>" "^="))
(global-ligature-mode t))
save-place-mode moves the cursor to it’s last location in the previously visited file.
(save-place-mode 1)
Enable auto-revert-mode to automatically update files that change externally to Emacs (such as external formatters or git checkouts):
(global-auto-revert-mode)
vertico is a minimalistic vertical completion interface similar to the built-in.
I moved from helm after realizing I didn’t use most of what it provided. I load
it with savehist-mode
so that my minibuffer history is preserved.
(use-package vertico
:ensure t
:init
(vertico-mode)
(savehist-mode)
:config
(setq vertico-cycle t))
orderless is a completion style that divides a pattern into parts and matches those parts in any order.
(use-package orderless
:ensure t
:config
(setq completion-styles '(orderless basic partial-completion)
completion-category-overrides '((eglot (styles . (basic))))))
marginalia enriches minibuffer completions with additional information.
(use-package marginalia
:ensure t
:after vertico
:init
(marginalia-mode))
consult provides various commands that can be invoked based on completion
candidates. The consult-get-project-root
function constrains consult to the
root directory of a git project when in a git directory.
(defun consult-get-project-root ()
(when (fboundp 'projectile-project-root)
(projectile-project-root)))
(use-package consult
:ensure t
:bind
(("C-s" . consult-line)
("C-r" . consult-history)
("C-c i" . consult-imenu)
("C-c g" . consult-ripgrep))
:custom
(consult-project-root-function #'consult-get-project-root))
embark lets you take actions on items under cursor in the minibuffer. For example: renaming a file, moving a file, etc. embark-consult provides additional functionality for use with consult.
(use-package embark
:ensure t
:bind
(("C-." . embark-act)
("C-;" . embark-dwim)
("C-h B" . embark-bindings))
:init
(setq prefix-help-command #'embark-prefix-help-command))
(use-package embark-consult
:ensure t
:hook
(embark-collect-mode . consult-preview-at-point-mode))
corfu is a minimalisting completion-at-point framework that works well with vertico.
(use-package corfu
:ensure t
:custom
(corfu-auto t)
(corfu-quit-no-match t)
:init
(global-corfu-mode))
;; (use-package cape
;; :ensure t
;; :init
;; (add-to-list 'completion-at-point-functions #'cape-file)
;; (add-to-list 'completion-at-point-functions #'cape-line))
which-key will, after a brief delay, popup a list of options for an incomplete command.
(use-package which-key
:ensure t
:diminish which-key-mode
:custom
(setq which-key-popup-type 'side-window)
:init (which-key-mode))
perspective helps me keep the minibuffer clean, and allows me to create groups of buffers that make moving between projects and programming languages easier.
(use-package perspective
:ensure t
:bind (("C-c b" . persp-switch))
:config
(persp-mode))
I use projectile to quickly switch between the different projects I have. I keep personal and work projects all in their own folders under one directory so projectile can easily find everything.
(use-package projectile
:ensure t
:diminish projectile-mode
:config (projectile-mode)
:custom ((projectile-completion-system 'vertico))
:init
(setq projectile-project-search-path '("~/Code/")))
(use-package consult-projectile
:ensure t
:bind
(("C-c p" . consult-projectile)))
I use Magit for interacting with Git through emacs. It’s a much nicer interface than most hosted VCS/SCM tools.
(use-package magit
:ensure t
:custom
(magit-git-executable "/usr/bin/git")
(vc-follow-symlinks t)
:bind (("C-x v" . magit-status)))
I also enable git-commit-mode for better editing of commit messages:
(use-package git-commit
:ensure t
:config
(global-git-commit-mode))
tree-sitter is an incremental parsing library that provides an intelligent form of syntax highlighting. It’s now included with Emacs.
(require 'treesit)
(add-to-list 'major-mode-remap-alist
'((python-mode . python-ts-mode)
(typescript-mode . typescript-ts-mode)
(clojure-mode . clojure-ts-mode)))
apheleia is a formatter that works for multiple languages.
(use-package apheleia
:ensure t
:config
(apheleia-global-mode +1))
eglot is an unintrusive LSP that works well with tree-sitter. eldoc-box makes documentation pop up in a childframe under the cursor.
(use-package eglot
:ensure t
:defer t
:bind (("C-c e" . eglot-code-actions))
:hook
((clojure-mode . eglot-ensure)
(typescript-mode . eglot-ensure)
(python-mode . eglot-ensure)
(python-mode . (lambda () (set-fill-column 88)))))
(setq eldoc-echo-area-use-multiline-p nil)
yasnippet is a template system that helps save a lot of time when writing code. I also load a package that provides a large library of existing snippets.
(use-package yasnippet
:ensure t
:diminish yas-minor-mode
:config
(add-to-list 'yas-snippet-dirs "~/.emacs.d/snippets")
:bind (("C-c y i" . yas-insert-snippet)
("C-c y v" . yas-visit-snippet-file)))
(add-hook 'prog-mode-hook #'yas-minor-mode)
(use-package yasnippet-snippets
:ensure t)
paredit provides structural editing capabilties when working with lisp dialects.
(use-package paredit
:ensure t
:diminish paredit-mode
:hook
((cider-repl-mode-hook . paredit-mode)
(cider-mode-hook . paredit-mode)
(clojure-ts-mode . paredit-mode)))
ChatGPT provides a conversational interface that replaces the google search bar.
(use-package chatgpt-shell
:ensure t
:defer t
:config
(setq chatgpt-shell-openai-key
(auth-source-pick-first-password :host "api.openai.com")))
I use org-mode to manage my notes, documentation, todos, calendar items, etc.
(use-package org
:ensure t
:config
(progn
(setq org-agenda-files '("/mnt/media/org/todo.org" "/mnt/media/org/calendar.org")
org-agenda-span 'day
org-log-done t
org-directory "/mnt/media/org/"
org-todo-keywords '((sequence "TODO" "PROGRESS" "|" "DONE"))
org-capture-templates
'(("t" "todo" entry (file "/mnt/media/org/todo.org")
"* TODO %?\n%u\n%a\n" :clock-in t :clock-resume t))
org-refile-targets (quote ((nil :maxlevel . 9)
(org-agenda-files :maxlevel . 9)))))
:bind (("C-c a" . org-agenda)
("C-c c" . org-capture)))
org-pomodoro lets me employ the pomodoro technique while working on org todo’s.
(use-package org-pomodoro
:ensure t
:commands (org-pomodoro)
:config
(setq alert-user-configuration '((((:category . "org-pomodoro")) libnotify nil))))
org-modern provides a modern style to org. that’s it.
(use-package org-modern
:ensure t
:init
(global-org-modern-mode))
org-roam implements much of the same functionality provided by Roam or Obsidian in org-mode. It is a powerful way to build a knowledgebase. org-roam-ui provides a clean interface for traversing roam nodes.
(use-package org-roam
:ensure t
:custom
(org-roam-directory (file-truename "/mnt/media/org/roam"))
:bind (("C-c r l" . org-roam-buffer-toggle)
("C-c r f" . org-roam-node-find)
("C-c r g" . org-roam-graph)
("C-c r i" . org-roam-node-insert)
("C-c r c" . org-roam-capture)
;; Dailies
("C-c r j" . org-roam-dailies-capture-today))
:config
(setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
(org-roam-db-autosync-mode))
(use-package org-roam-ui
:after org-roam
: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))
khalel allows me to see my calendar events in the org-agenda which is very nice. Under the hood it is using vdirsyncer and khal to manage multiple calendars.
(use-package khalel
:ensure t
:commands (khalel-export-org-subtree-to-calendar
khalel-import-events
khalel-edit-calendar-event
khalel-add-capture-template)
:config
(progn
(setq khalel-khal-command "khal"
khalel-vdirsyncer-command "vdirsyncer"
khalel-import-org-file (concat org-directory "calendar.org")
khalel-import-org-file-confirm-overwrite nil
khalel-import-end-date- "+7d")))
managing mail with mu4e. I keep this in a separate file because it contains a lot of mail-specific information that I don’t want to share in git, so the mail.el file isn’t checked into the repository.
(load "~/.emacs.d/mail")
elfeed is a feed reader for RSS and Atom. Works well enough with Eww.
(use-package elfeed
:ensure t
:bind ("C-x w" . 'elfeed)
:config
(setq browse-url-browser-function 'eww-browse-url))
I am gradually replacing the typical language modes with the new modes supplied by tree-sitter. This area remains for language-specific tools.
(use-package clojure-mode
:ensure t)
(use-package clj-deps-new
:ensure t)
(use-package cider
:ensure t
:config
(setq cider-clojure-cli-global-options "-A:portal"))
(defun portal.api/open ()
(interactive)
(cider-nrepl-sync-request:eval
"(do (ns dev) (def portal ((requiring-resolve 'portal.api/open))) (add-tap (requiring-resolve 'portal.api/submit)))"))
(defun portal.api/clear ()
(interactive)
(cider-nrepl-sync-request:eval "(portal.api/clear)"))
(defun portal.api/close ()
(interactive)
(cider-nrepl-sync-request:eval "(portal.api/close)"))
(use-package python
:ensure t
:config
(setq python-indent-guess-indent-offset-verbose nil))
(use-package poetry
:ensure t
:defer t
:config
(setq poetry-tracking-strategy 'switch-buffer)
:hook ((python-mode . poetry-tracking-mode)))
(use-package typescript-mode
:ensure t)
(use-package vue-mode
:ensure t
:config
(setq mmm-submode-decoration-level 0))
We set the GC threshold lower for interactive use (this undoes a setting from
early-init.el
). This needs to be the last thing in the file to get the
benefits of faster startup.
(setq gc-cons-threshold (* 2 1000 1000))