This is a literate configuration for Emacs.
Feel free to take whatever you like! There are some neat little tricks and workarounds in here that might be useful to you person-who-is-reading this..!
This file changes over time, and new tricks are added occasionally. And if this file is able to help more than just myself, that’s kinda awesome.
The package manager in Emacs is pretty sweet, but the default repos are quite limited in their selection of packages… So instead, I add a couple more repos. (I am sure almost everyone has something like this in their configuration somewhere)
;; Package Manager URLs
(setq package-archives
'(("gnu" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")
("org" . "https://orgmode.org/elpa/")))
And then we initialize the package manager, straight
, and use-package
:
(package-initialize)
;; Installs use-package from the above repos if we don't have it. This is used
;; for pretty much every package from here on out
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
;; Installs straight, which lets me use git repos for packages
;; https://github.com/raxod502/straight.el/blob/master/README.md#getting-started
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
I love Doom Emacs’ and spaceline’s appearance! I also like to trim my interface to be a bit more on the minimal side, so, I cut out a few UI elements here. However, these things are only relevant in the event we are running GUI Emacs, so this section gets mostly skipped if started in a terminal.
That said, since we might also be starting Emacs as a daemon (without a frame, but intended for GUI clients), we also defer loading these modifications until we actually have a frame to work with when starting it this way.
And here is all of the actual theming (It gets pulled into the code block above with noweb):
;; Not too big on toolbars, and I can't seem to get the scrollbar to look good
;; (Which is okay because I navigate with other means usually), soooo...
(tool-bar-mode 0)
(scroll-bar-mode -1)
;; Doom dracula is my favorite theme! We also style helm to have a purple color
;; instead of its default here
(use-package doom-themes
:ensure t
:init
(setq doom-themes-enable-bold t
doom-themes-enable-italic t)
(load-theme 'doom-dracula t)
(doom-themes-org-config))
;; This configures spaceline to have a fancy swooping curve seperating its
;; elements, and also colors it with a pretty purple~
(use-package spaceline-config
:ensure spaceline
:config (spaceline-emacs-theme)
:custom (powerline-default-separator 'wave)
:custom-face
(spaceline-evil-normal ((t (:background "orchid4" :foreground "#eee" :inherit (quote mode-line)))))
(spaceline-highlight-face ((t (:background "orchid4" :foreground "#eee" :inherit (quote mode-line)))))
(spaceline-unmodified ((t (:background "orchid4" :foreground "#eee" :inherit (quote mode-line))))))
;; And here is my dashboard screen, along with a silly name that I'm finally
;; dubbing my config~
(use-package dashboard
:after org
:ensure t
:demand t
:config
(switch-to-buffer "*dashboard*")
(dashboard-insert-startupify-lists)
:custom
(dashboard-banner-logo-title "Archemacs")
(dashboard-startup-banner (expand-file-name "Archemacs/splash.png" user-emacs-directory))
(dashboard-center-content t)
(dashboard-show-shortcuts nil)
(dashboard-set-footer nil)
(initial-buffer-choice
(lambda () (if (buffer-file-name)
(current-buffer)
(get-buffer "*dashboard*"))))
(dashboard-items '((recents . 5)
(bookmarks . 5)
(projects . 5)
(agenda . 5)
(registers . 5)))
:hook (add-hook 'before-make-frame-hook #'dashboard-refresh-buffer))
;; And finally, for some reason, helm doesn't seem to want to respect styling in
;; use-package unless done explicitly after
(custom-set-faces
'(helm-candidate-number ((t (:foreground "#f1fa8c" :background nil)))))
Here are a bunch of things that I find handy universally. Which means most of the things in here aren’t going to be lazily loaded, and will contribute to how heavy my default Emacs is:
;; Perf things in case this emacs ever does heavy lifting
(setq gc-cons-threshold 100000000)
(setq read-process-output-max 1000000)
;; Set up company, and auto-complete-like quickdocs!
;; We bind both tabs because turns out they aren't equivelant between
;; GUI and terminals
(use-package company-quickhelp
:ensure t
:init
(global-company-mode 1)
(company-quickhelp-mode 1)
:bind (:map company-active-map
("<tab>" . company-complete-selection)
("TAB" . company-complete-selection)))
;; Projectile using helm's selection framework! I find it makes fuzzy finding
;; files in projects really fast and easy with C-M-f
(use-package projectile
:ensure helm-projectile
:init (projectile-mode)
:config (require 'helm-projectile)
:bind (("C-M-f" . projectile-find-file)
("C-p" . projectile-find-file))
:custom
(projectile-enable-caching nil)
(projectile-completion-system 'helm))
;; This pops up menus with the continuation of key chords I started using, which
;; makes it sigificantly easier to use things that I have a hard time
;; remembering the keys for
(use-package which-key
:ensure t
:config (which-key-mode t))
;; Smart xref jumping is nice without needing tags! This is especially nice with
;; ripgrep installed
(use-package dumb-jump
:ensure t
:init
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
(setq xref-show-definitions-function #'xref-show-definitions-completing-read))
;; LSP support where we want it--hooks into xref and other things too!
(use-package eglot)
;; C-o for imenu is almost always nice!
(use-package helm
:bind (("C-o" . helm-imenu)
("C-S-p" . helm-M-x))
:custom (helm-M-x-show-short-doc t))
;; I prefer spaces to tabs, because I like to author my code with appearance in
;; mind too. (And spaces let me indent things to be on the same column of
;; expressions not at the beginning of lines)
(setq indent-tabs-mode nil)
;; Squelch the creation of .#<filename> files
(setq create-lockfiles nil)
;; Emacs auto-backups feel nicer when they collect in my ~/.emacs.d/backups
;; instead of the current folder
(setq backup-directory-alist '((".*" . "~/.emacs.d/backups/"))
auto-save-file-name-transforms '((".*" "~/.emacs.d/backups/" t)))
;; yes or no questions are now y/n questions
(defalias 'yes-or-no-p 'y-or-n-p)
;; Parenthesis (and other things) pair highlighting
(show-paren-mode)
;; Eldoc is useful for most language support
(global-eldoc-mode 1)
;; Marks files with shebangs as executable automatically
(add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p)
;; Deletes trailing whitespace before saving
(add-hook 'before-save-hook 'delete-trailing-whitespace)
;; Starts an emacs server I can connect to with emacsclients if I send a USR1
;; signal to Emacs
(define-key special-event-map (kbd "<sigusr1>") 'server-start)
;; This causes dired to open files in the same buffer as itself when you click
;; things (Deffered until we actually use dired)
(with-eval-after-load 'dired
(define-key dired-mode-map (kbd "<mouse-2>") 'dired-mouse-find-file))
;; Ctrl + Click for jump to definition is nice!
(global-set-key (kbd "C-<mouse-1>") 'xref-find-definitions-at-mouse)
;; Save my minibuffer history between sessions plz
(savehist-mode 1)
Language-specific support!
Emacs’ Semantic mode is pretty good at C! So this is largely just setting that up; With it, we get definition jumping and some pretty intelligent completion.
;; Setting up C and C++ with Semantic completion, jumping, summaries, and a
;; bunch of other nice things!
(defun c-modes-hook ()
(semantic-mode)
(semantic-idle-summary-mode 1))
(add-hook 'c-mode-hook 'c-modes-hook)
(add-hook 'c++-mode-hook 'c-modes-hook)
;; CIDER makes Clojure a joy to work with, both in literate org files and outside
(use-package cider
:straight (cider :type git :host github :repo "clojure-emacs/cider")
:ensure t
:after org
:custom (org-babel-clojure-backend 'cider))
;; A mode for Fennel, which is a cool Clojure-like language I like a lot
(use-package fennel-mode
:straight (:repo "https://git.sr.ht/~technomancy/fennel-mode"
:type git :host nil :branch "main")
:mode "\\.fnl$")
My Elisp configuration is largely just setting up erefactor
and then adding it to the three Elisp modes.
;; Gives me passive highlighting of variables under point, and lets me refactor,
;; rename, and other neat things~ (Using straight to pull my version of the
;; package to remove a deprecated function call and warning)
(use-package erefactor
:ensure t
:straight (erefactor :type git :host github :repo "mhayashi1120/Emacs-erefactor"
:fork (:host github :repo "Archenoth/Emacs-erefactor"))
:hook ((emacs-lisp-mode lisp-interaction-mode ielm-mode) . erefactor-lazy-highlight-turn-on)
:bind-keymap ("C-c r" . erefactor-map))
For most markup-centric web development, I start up web-mode
. Having Emmet available is nice too!
;; Web Mode for HTML, JSPs, etc...
(use-package web-mode
:ensure t
:after yasnippet
:mode "\\.\\(?:jsp\\|tag\\|erb\\|ejs\\|[sjp]?html?x?\\)$"
:init
(setq web-mode-engines-alist '(("jsp" . "\\.tag\\'")))
(setq web-mode-html-offset 2)
(setq web-mode-css-offset 2)
(setq web-mode-script-offset 2))
;; Allows me to convert CSS selectors into the HTML that they represent to make
;; HTML authoring faster
(use-package emmet-mode
:ensure t
:after web-mode
:init (setq emmet-indentation 2)
:hook
((web-mode . emmet-mode)))
My JavaScript configuration is largely centered around js2 and it’s tremendous JavaScript parsing ability.
;; A JavaScript mode that recognizes all kinds of useful things about JavaScript
;; code, like variable scope, words not in the standard, syntax, and a whole
;; heap of other things
(use-package js2-mode
:ensure js2-mode
:mode "\\.js$")
;; Uses JS2 to let me rename variables and stuff~
(use-package js2-refactor
:ensure t
:after js2-mode
:bind (:map js-mode-map ("C-c r" . js2r-rename-var))
:hook ((js2-mode . js2-refactor-mode)))
;; Basic Lua support
(use-package lua-mode :ensure t)
;; Markdown, for Jekyll and stuff!
(use-package markdown-mode
:ensure markdown-mode
:mode "\\.\\(?:md\\|markdown\\)$")
My favorite Pico-8 mode isn’t on MELPA and friends, so we end up using straight here.
;; Fetches a mode that lets me edit pico-8 file lua with native support,
;; documentation file parsing, and support for showing me what graphics look
;; like in-buffer
(use-package pico8-mode
:straight (pico8-mode :type git :host github :repo "Kaali/pico8-mode"))
Allow for the standard C-c C-c
keybind to eval the thing under my cursor in Ruby code. (Though this isn’t exactly perfect like Lisp evaluation is, but does allow for some nice REPL-based dev in Ruby)
(use-package inf-ruby
:ensure t
:bind (:map inf-ruby-minor-mode-map ("C-c C-c" . ruby-send-block)))
Eglot is all the support we need for Rust:
(use-package rust-mode
:ensure t
:hook ((rust-mode . eglot-ensure)))
Emacs seems to fail at escaping backslashes in SQL files… So I have slightly modified the syntax entry for the backslash character in SQL files so it acts like a proper escape:
;; Fix syntax escaping for SQL modes in buffers
(use-package sql
:config (modify-syntax-entry ?\\ "\\" sql-mode-syntax-table))
I don’t use VBS often–but I guess often enough to want an editor to play around with it. (This one also isn’t on MELPA)
(use-package vbscript-mode
:straight (vbscript-mode :type git :host github :repo "nverno/vbs-mode")
:mode "\\.vbs$")
Non-language Emacs applications
(use-package nov
:ensure t
:mode "\\.epub$")
In here, I define a special browse-url
function for gopher and gemini links, and then register them!
(use-package elpher
:ensure t
:config
(defun browse-url-elpher (url &rest _)
(elpher-go url))
(setq browse-url-handlers
'(("^gopher:" . browse-url-elpher)
("^gemini:" . browse-url-elpher))))
This lets me see casually, the way variables and other things are used in programming buffers!
(use-package idle-highlight-mode
:ensure t
:custom
(idle-highlight-exceptions-face
'(font-lock-keyword-face font-lock-string-face font-lock-comment-face))
(idle-highlight-ignore-modes
'(emacs-lisp-mode lisp-interaction-mode ielm-mode))
:hook
(prog-mode . (lambda ()
(unless (member major-mode idle-highlight-ignore-modes)
(idle-highlight-mode)))))
Hexl lacks some functionality, such as the ability to go to address offsets, so I stole a code block from here to do that:
;; Credit https://emacs.stackexchange.com/a/45805/2039
(defun ext/hexl-hex-forward-char (hex-offset)
"Move to right HEX-OFFSET bytes (left if negative) in Hexl mode."
(interactive "sHex Offset: ")
(hexl-goto-address
(+ (hexl-current-address)
(hexl-hex-string-to-integer hex-offset))))
And then I wrote a function to measure the length of the region:
(defun arch/hexl-measure-region ()
"Measure how large the active region is."
(interactive)
(if (region-active-p)
(save-excursion
(let ((point (hexl-current-address)))
(exchange-point-and-mark)
(let ((diff (abs (- point (hexl-current-address)))))
(exchange-point-and-mark)
(message "Range is %d bytes (0x%08x)" diff diff))))
(message "Current address: 0x%08x" (hexl-current-address))))
As for the bindings to use this:
(add-hook 'hexl-mode-hook
(lambda ()
(local-set-key (kbd "M-f") #'ext/hexl-hex-forward-char)
(local-set-key (kbd "M-s") #'arch/hexl-measure-region)))
One of the best Git frontends! It’s good enough that I actually use it instead of the CLI sometimes, which I feel very comfortable with~
(use-package magit :ensure t)
This adds multiple-cursor bindings similar to other editors that I find pretty handy!
(use-package multiple-cursors
:ensure t
:bind (("C-d" . mc/mark-next-like-this)
("C-M-<up>" . mc/mmlte--up)
("C-M-<down>" . mc/mmlte--down)
("C-S-<mouse-1>" . mc/add-cursor-on-click)))
My Org mode setup includes support for spell checking, grammar checking (Which requires languagetool-commandline.jar
from here), tangling source files from Org mode, visual-line-mode
, and syntax coloring.
I also add nice looking Unicode bullet points.
;; Catches weasel works and other fun things like that.
(use-package writegood-mode :ensure t)
(use-package org
:ensure t
:demand t
:straight t
:init
(setq org-export-latex-listings 'minted)
:custom-face
(org-level-1 ((t (:inherit outline-1 :height 1.3))))
:custom
(org-hide-emphasis-markers t)
(org-src-fontify-natively t)
:hook
((org-mode . flyspell-mode)
(org-mode . visual-line-mode)
(org-mode . org-indent-mode)
(org-mode . writegood-mode)))
;; Requires a languagetool-commandline.jar from
;; https://www.languagetool.org/download/snapshots/
(use-package langtool
:ensure t)
(use-package org-bullets
:ensure t
:hook ((org-mode . org-bullets-mode))
:custom-face
(org-bullet-blue ((t (:foreground "#61bfff"))))
(org-bullet-face ((t (:inherit outline-1)))))
;; Global org-mode bindings
(global-set-key (kbd "C-c a") 'org-agenda)
(global-set-key (kbd "C-c c") 'org-capture)
I like org-roam! But it is pretty heavy, so I want to defer that to only happen when I actively open a folder with a .dir-locals.el
containing this init!
(defun arch/init-roam ()
(when (not (fboundp 'org-roam-mode))
(use-package org-roam
:straight t
:bind (("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)
("C-c n g" . org-roam-graph)
("C-c n i" . org-roam-node-insert)
("C-c n c" . org-roam-capture)
("C-c n j" . org-roam-dailies-capture-today))
:config
(org-roam-db-autosync-mode)
(add-hook 'org-roam-mode-hook 'visual-line-mode))))
The aforementioned .dir-locals
would look something like this:
((nil . ((eval . (arch/init-roam))
(org-roam-directory . "/home/archenoth/Documents/org/brain")
(org-roam-db-location . "/home/archenoth/Documents/org/brain/org-roam.db"))))
This is a built-in package, but I like to customize it so it uses ripgrep
;; Use ripgrep; it is extremely fast
(use-package grep
:custom
(grep-command '("rg -n -H --no-heading -e '' $(git rev-parse --show-toplevel || pwd)" . 27))
(grep-find-command '("rg -n -H --no-heading -g '*' -e '' $(git rev-parse --show-toplevel || pwd)" . 34)))