23 KiB
Emacs Configuration
Literate emacs configuration
I can never rembember what various configs do. Jumping on this literate emacs config to see if it helps!
Start the emacs server
Starting as a server lets me connect externally to do things like change themes, save buffers via cron and other such dumbary!
(require 'bind-key)
(load "server")
(unless (server-running-p) (server-start))
Interface and Behavior
Interface
Global font
(set-frame-font "Go Mono 11")
Use 80 columns, this helps keep things readable when windows are split
(setq whitespace-style '(trailing lines space-before-tab)
whitespace-line-column 80)
(setq-default fill-column 80)
I know I am in emacs, don't need to see the startup screen.
(setq inhibit-startup-screen t)
Use UTF8 where ever possible
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
Change various UI bits
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(column-number-mode +1)
(global-font-lock-mode 1)
direnv
(use-package direnv
:config
(direnv-mode))
project.el
(setq project-list-file (expand-file-name "~/.emacs.d/projects"))
transient
(setq transient-history-file (expand-file-name "~/.emacs.d/transient"))
Behavior
Switch various defaults to be more comfortable for myself.
(fset 'yes-or-no-p 'y-or-n-p)
(show-paren-mode t)
(setq desktop-dirname "~/.emacs.d/"
desktop-base-file-name "emacs.desktop"
desktop-base-lock-name "lock"
desktop-path (list desktop-dirname)
desktop-save t
desktop-files-not-to-save "^$" ;reload tramp paths
desktop-load-locked-desktop nil
desktop-auto-save-timeout 30)
(desktop-save-mode 1)
(setq backup-directory-alist '(("." . "~/.emacs-saves")))
(setq auto-mode-alist
(append
(list
'("\\.gpg$" . sensitive-minor-mode)
)
auto-mode-alist))
(setq auth-sources
'("/run/secrets/netrc"))
Use spelling and auto-fill when we are in text mode.
(add-hook 'text-mode-hook (lambda ()
(auto-fill-mode 1)
(turn-on-flyspell)))
(setq eshell-history-file-name (expand-file-name "~/.emacs.d/eshell/history"))
Unset custom-file
The customization file mostly just causes churn in the SCM so we disable it here.
(setq custom-file (make-temp-file ""))
Packages
rust-mode
(use-package rust-mode)
uxntal
(use-package uxntal-mode)
breadcrumb
Handy breadcrumbs for seeing where things are in the LSP.
(use-package breadcrumb)
ollama
Simple wrapper for ollama
that lets me pipe regions and what not into various buffers.
(use-package ollama
:init
(setopt
ollama:endpoint "https://ollama.otter-alligator.ts.net/api/generate"
ollama:language "English"
ollama:model "llama3.1"))
web-mode
(use-package web-mode
:config
(setq web-mode-markup-indent-offset 2)
(add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.php?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.ts?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.js?\\'" . web-mode)))
scpaste
Post pastes to https://paste.suah.dev
(use-package scpaste
:config (setq scpaste-scp-destination "suah.dev:/var/www/paste"
scpaste-http-destination "https://paste.suah.dev"))
htmlize
This is needed for publishing org stuff
(use-package htmlize)
parchment-theme
This is a nice theme that resembles acme in plan9. Minimal.
(use-package parchment-theme
:config (load-theme 'parchment t))
keychain-environment
I make heavy use of ssh-agent
this lets emacs pickup / use the existing
agents I have running.
(use-package keychain-environment
;;:pin "melpa"
:init
(keychain-refresh-environment))
ivy
ivy
is fantastic. It gives me nice visual search for buffers,
code.. etc. Combined with smex
for sorting (shows last used things first) and
counsel
(extends ivy into various areas like the help stuff).
(use-package counsel)
(setq smex-save-file (expand-file-name "~/.emacs.d/smex.save"))
(use-package smex)
(use-package ivy
:hook (after-init . ivy-mode)
:bind
("C-s" . swiper-isearch)
("M-x" . counsel-M-x)
("C-x C-f" . counsel-find-file)
("C-x b" . ivy-switch-buffer))
magit
Magit is a awesome. Not sure what else to say about it. :P
(use-package magit
:bind ("C-c m" . magit-status)
:init
(setq magit-completing-read-function 'ivy-completing-read))
lsp
Use eglot
for lsp stuff. It's built in and shows a bit more information for
auto-completion stuff.
(use-package eglot
:config
(add-hook 'elm-mode-hook 'eglot-ensure)
(add-hook 'go-mode-hook 'eglot-ensure)
(add-hook 'haskell-mode-hook 'eglot-ensure)
(add-hook 'nix-mode-hook 'eglot-ensure)
(add-hook 'perl-mode-hook 'eglot-ensure)
(add-hook 'ruby-mode-hook 'eglot-ensure)
(add-hook 'rust-mode-hook 'eglot-ensure)
(add-hook 'typescript-mode-hook 'eglot-ensure)
(add-to-list 'eglot-server-programs '(c-mode . ("clangd")))
(add-to-list 'eglot-server-programs '(c++-mode . ("clangd")))
(add-to-list 'eglot-server-programs '(rust-mode . ("rust-analyzer")))
(define-key eglot-mode-map (kbd "C-c r") 'eglot-rename)
(define-key eglot-mode-map (kbd "C-c f") 'eglot-format)
:hook
(eglot-managed-mode . (lambda() (add-hook 'before-save-hook 'eglot-format-buffer nil 'local))))
company and friends
company
allows for auto-completion of various things. It can interface with lsp-mode
to complete
things like Go.
(use-package company
:config
(setq company-tooltip-limit 20
company-minimum-prefix-length 1
company-idle-delay .3
company-echo-delay 0)
:hook (prog-mode . company-mode))
gitgutter
This gives me a nice in-ui way to see modifications and what not.
(use-package git-gutter
:hook
(after-init . global-git-gutter-mode)
:config
(global-set-key (kbd "C-x g r") 'git-gutter:revert-hunk)
(global-set-key (kbd "C-x g p") 'git-gutter:previous-hunk)
(global-set-key (kbd "C-x g n") 'git-gutter:next-hunk))
shell
I don't often use the shell from emacs, but when I do these bits make it easier for me to treat it like a regular shell.
;; Kill terminal buffers on exit so I din't have to kill the buffer after I exit.
(defadvice term-handle-exit
(after term-kill-buffer-on-exit activate)
(kill-buffer))
Language Configurations
Shell
(use-package shfmt)
(add-hook 'sh-mode-hook 'shfmt-on-save-mode)
Typescript
(use-package typescript-mode)
Nix
(use-package nix-mode
:mode "\\.nix\\'")
Elm
(use-package elm-mode)
Haskell
(use-package haskell-mode)
Go
go-add-tags
This lets one select a struct
or similar and auto add the `json:"NAME"`
bits.
(use-package go-add-tags)
go-mode
This allows for things like gofmt
and auto adding / removing of imports.
(use-package go-mode
:bind
("C-c t" . go-add-tags))
(defun xin-eglot-organize-imports () (interactive)
(eglot-code-actions nil nil "source.organizeImports" t))
(defun lsp-go-install-save-hooks ()
(add-hook 'before-save-hook 'xin-eglot-organize-imports nil t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)
go-eldoc
This extends eldoc to be able to speak Go - quite handy for quickly looking up what things do.
(use-package go-eldoc
:hook
(go-mode . go-eldoc-setup))
org-mode
Oh org-mode
. It's the reason I started using emacs.. and it's the reason I
can't quit!
Config
(org-babel-do-load-languages
'org-babel-load-languages
'((plantuml . t)
(dot . t)
(shell . t)
(latex . t)))
Publish bits
I publish some of my notes on suah.dev/p. Also some recipes.
(setq org-export-with-broken-links t)
(defun exo-touch-and-publish (plist filename dir)
(progn
(set-file-times "~/org-roam/index.org")
(org-html-publish-to-html plist filename dir)))
(setq my-org-publish-alist
'(("exo" :components ("org-roam" "org-roam-static" "org-roam-org"))
("bolddaemon" :components ("bolddaemon-web" "bolddaemon-static"))
("notes" :components ("org-notes" "notes-static" "notes-rss"))
("deftly" :components ("deftly-blog" "deftly-static"))
("ohmyksh" :components ("ohmy-web" "ohmy-static"))
("org-roam"
:publishing-directory "/ssh:suah.dev:/var/www/exo.suah.dev/"
:recursive t
:html-link-home "http://exo.suah.dev/"
:html-link-up "../"
:publishing-function exo-touch-and-publish
:base-directory "~/org-roam")
("org-roam-org"
:publishing-directory "/ssh:suah.dev:/var/www/exo.suah.dev/"
:publishing-function org-org-publish-to-org
:recursive t
:base-directory "~/org-roam")
("org-roam-static"
:base-directory "~/org-roam"
:recursive t
:publishing-directory "/ssh:suah.dev:/var/www/exo.suah.dev/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|svg"
:publishing-function org-publish-attachment)
("org-roam-rss"
:publishing-directory "/ssh:suah.dev:/var/www/exo.suah.dev/"
:publishing-function org-rss-publish-to-rss
:rss-extension "xml"
:base-directory "~/org-roam")
("org-notes"
:auto-preamble t
:auto-sitemap t
:headline-levels 4
:publishing-directory "/ssh:suah.dev:/var/www/suah.dev/p/"
:publishing-function org-html-publish-to-html
:recursive t
:section-numbers nil
:html-head "<link rel=\"stylesheet\" href=\"https://suah.dev/p/css/stylesheet.css\" type=\"text/css\" />"
:html-link-home "http://suah.dev/p/"
:html-link-up "../"
:style-include-default nil
:sitemap-filename "index.org"
:sitemap-title "Notes"
:with-title t
:author-info nil
:creator-info nil
:base-directory "~/org/notes")
("deftly-blog"
:auto-preamble t
:auto-sitemap t
:headline-levels 1
:publishing-directory "/ssh:suah.dev:/var/www/deftly.net/new/"
:publishing-function org-html-publish-to-html
:recursive t
:section-numbers nil
:html-head "<link rel=\"stylesheet\" href=\"https://deftly.net/new/css/stylesheet.css\" type=\"text/css\" />"
:html-link-home "http://deftly.net/new"
:html-link-up "../"
:style-include-default nil
:sitemap-title "Deftly.net"
:with-title t
:author-info t
:creator-info nil
:base-directory "~/org/deftly")
("ohmy-web"
:auto-preamble t
:auto-sitemap nil
:headline-levels 2
:publishing-directory "/ssh:suah.dev:/var/www/deftly.net/ohmyksh/"
:publishing-function org-html-publish-to-html
:recursive t
:section-numbers nil
:html-head "<link rel=\"stylesheet\" href=\"https://deftly.net/ohmyksh/css/stylesheet.css\" type=\"text/css\" />"
:html-link-home "http://deftly.net/ohmyksh"
:html-link-up "../"
:style-include-default nil
:with-title t
:author-info t
:creator-info nil
:base-directory "~/src/ohmyksh")
("notes-static"
:base-directory "~/org/notes"
:publishing-directory "/ssh:suah.dev:/var/www/suah.dev/p/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|svg"
:recursive t
:publishing-function org-publish-attachment)
("deftly-static"
:base-directory "~/org/deftly"
:publishing-directory "/ssh:suah.dev:/var/www/deftly.net/new/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg"
:recursive t
:publishing-function org-publish-attachment)
("ohmy-static"
:base-directory "~/src/ohmyksh"
:publishing-directory "/ssh:suah.dev:/var/www/deftly.net/ohmyksh/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg"
:recursive t
:publishing-function org-publish-attachment)
("notes-rss"
:publishing-directory "/ssh:suah.dev:/var/www/suah.dev/p/"
:publishing-function org-rss-publish-to-rss
:recursive t
:rss-extension "xml"
:section-numbers nil
:exclude ".*"
:include ("index.org")
:table-of-contents nil
:base-directory "~/org/notes")
("recipes"
:auto-preamble t
:auto-sitemap t
:headline-levels 4
:publishing-directory "/ssh:suah.dev:/var/www/suah.dev/recipes/"
:publishing-function org-html-publish-to-html
:recursive t
:section-numbers nil
:html-head "<link rel=\"stylesheet\" href=\"https://suah.dev/p/css/stylesheet.css\" type=\"text/css\" />"
:html-link-home "http://suah.dev/recipes/"
:html-link-up "../"
:style-include-default nil
:sitemap-filename "index.org"
:sitemap-title "Recipes"
:with-title t
:author-info nil
:creator-info nil
:base-directory "~/org/recipes")
("bolddaemon-web"
:auto-preamble t
:auto-sitemap t
:headline-levels 4
:publishing-directory "/ssh:suah.dev:/var/www/bolddaemon.com/"
:publishing-function org-html-publish-to-html
:recursive t
:section-numbers nil
:html-link-home "http://bolddaemon.com"
:html-link-up "../"
:style-include-default nil
:with-title t
:author-info nil
:creator-info nil
:base-directory "~/org/bold.daemon")
("bolddaemon-static"
:base-directory "~/org/bold.daemon"
:publishing-directory "/ssh:suah.dev:/var/www/bolddaemon.com/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg"
:recursive t
:publishing-function org-publish-attachment)
))
Capture templates
(setq my-org-capture-templates
`(("t" "TODO"
entry (file+headline "~/org/todo.org" "TODOs")
,(concat
"* TODO %?\n"
":PROPERTIES:\n"
":LOGGING: TODO(!) WAIT(!) DONE(!) CANCELED(!)\n"
":END:\n") :prepend t)
("f" "TODO with File"
entry (file+headline "~/org/todo.org" "TODOs")
,(concat
"* TODO %?\n"
":PROPERTIES:\n"
":LOGGING: TODO(!) WAIT(!) DONE(!) CANCELED(!)\n"
":END:\n"
"%i\n %a") :prepend t)
("b" "Bug"
entry (file+olp+datetree "~/org/bugs.org" "Bugs")
"* BUG %?\nEntered on %U\n :PROPERTIES:\n :FILE: %a\n :END:\n" :prepend t)
("p" "Protocol"
entry (file+headline "~/org/links.org" "Links")
"* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?")
("L" "Protocol Link" entry (file+headline "~/org/links.org" "Links")
"* %? %:link\n%:description\n")
("j" "Journal"
entry (file+olp+datetree "~/org/journal.org")
"* %?\nEntered on %U\n %i\n")))
org
(use-package org
:hook
(org-mode . (lambda ()
(turn-on-flyspell)
(auto-revert-mode)
(auto-fill-mode 1)))
:bind
("C-c c" . org-capture)
("C-c p" . org-publish)
("C-c l" . org-store-link)
("C-c a" . org-agenda)
("C-c b" . org-iswitchb)
:config
(load-library "find-lisp")
(setq org-directory "~/org"
org-agenda-files (find-lisp-find-files "~/org" "\.org$")
org-startup-indented t
org-log-done 'time
org-export-with-sub-superscripts nil
org-html-inline-images t
org-log-into-drawer t
org-src-tab-acts-natively t
org-agenda-skip-scheduled-if-deadline-is-shown t
org-todo-keywords '((sequence "TODO(t)" "|" "DONE(d)")
(sequence "REPORT(r)" "BUG(b)" "KNOWNCAUSE(k)" "|" "FIXED(f)")
(sequence "|" "CANCELED(c)")))
(setq org-publish-project-alist my-org-publish-alist)
(setq org-capture-templates my-org-capture-templates))
(use-package org-contrib)
(use-package ox-rss)
Extra bits
(use-package org-journal
:defer t
:config
(setq org-journal-dir "~/org/journal/"
org-journal-file-format "%Y/%m-%d"
org-journal-date-format "%A, %d %B %Y"))
Add in some org-mode helpers:
org-habit
lets me keep track of TODOs and other things.-
org-checklist
lets me reset checklists for reoccurring tasks.- This requires one to
pkg_add a2ps
. RESET_CHECK_BOXES
property to be set tot
on a task headline. (properties can be set viaC-c C-x d
- This requires one to
(require 'org-habit)
(require 'org-checklist)
Custom agenda commands for various things.
Daily habits
shows how well I am keeping track of daily things.
(setq org-agenda-custom-commands
'(("h" "Daily habits"
((agenda ""))
((org-agenda-show-log t)
(org-agenda-ndays 7)
(org-agenda-log-mode-items '(state))))))
(use-package org-roam
:after org
:custom
(org-roam-directory (file-truename "~/org-roam/"))
: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 r" . org-roam-ref-add)
("C-c n s" . org-roam-db-sync)
("C-c n t" . org-roam-tag-add)
("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
(setq org-roam-completion-everywhere t)
(setq org-roam-node-display-template
(concat "${title:40} "
(propertize "${tags:40}" 'face 'org-tag)
"${file}"))
(require 'org-roam-protocol))
Extending org with the ability to transclude makes for a powerhouse!
(use-package org-transclusion
:after org)
gnus
[2024-08-22 Thu] Might need to switch go gnus. mu4e has been not showing new mail for some things and having to sync state between two sources is a pita.
(setq gnus-fetch-old-headers t
gnus-select-method '(nnimap "imap.fastmail.com")
gnus-secondary-select-methods '((nntp "news.gwene.org"))
gnus-sum-thread-tree-false-root ""
gnus-sum-thread-tree-indent " "
gnus-sum-thread-tree-leaf-with-other "├► "
gnus-sum-thread-tree-root ""
gnus-sum-thread-tree-single-leaf "╰► "
gnus-sum-thread-tree-vertical "│"
gnus-summary-line-format "%U%R%z %(%&user-date>; %-15,15f %B%s%)\n"
gnus-summary-thread-gathering-function 'gnus-gather-threads-by-references
gnus-thread-sort-functions '(gnus-thread-sort-by-date)
gnus-user-date-format-alist '((t . "%Y-%m-%d %H:%M")))
(with-eval-after-load 'gnus
(setq gnus-select-method '(nnimap "imap.fastmail.com")
gnus-secondary-select-methods '((nntp "news.gwene.org"))))
mu4e
mu
has been the best mail client for me on emacs.
Initializing mu
The defaults mu
uses make no sense. ~/.cache
is for .. caching data, not
persistent databases.. So we init things with sane defaults:
mu init --muhome=/home/qbit/.mu -m /home/qbit/Maildir/fastmail/ --my-address="aaron@bolddaemon.com"
General mail configuration
(require 'smtpmail)
(setq user-mail-address "aaron@bolddaemon.com"
user-full-name "Aaron Bieber"
message-send-mail-function 'smtpmail-send-it
message-kill-buffer-on-exit t
smtpmail-smtp-user "qbit@fastmail.com"
smtpmail-smtp-server "smtp.fastmail.com"
smtpmail-smtp-service 465
smtpmail-default-smtp-server "smtp.fastmail.com"
smtpmail-stream-type 'ssl)
mu4e specific configs
(use-package mu4e
:init
(setq mail-user-agent 'mu4e-user-agent
read-mail-command 'mu4e
mu4e-get-mail-command "mbsync fastmail"
mu4e-update-interval 420
mu4e-compose-context-policy nil
mu4e-context-policy 'pick-first
mu4e-drafts-folder "/Drafts"
mu4e-sent-folder "/Sent Items"
mu4e-trash-folder "/Trash"
mu4e-maildir-shortcuts
'( ("/INBOX" . ?i)
("/Archive" . ?a)
("/Sent Items" . ?s))
org-mu4e-link-query-in-headers-mode nil
mu4e-attachment-dir
(lambda (fname mtype)
(cond
((and fname (string-match "\\.diff$" fname)) "~/patches")
((and fname (string-match "\\.patch$" fname)) "~/patches")
((and fname (string-match "\\.diff.gz$" fname)) "~/patches")
(t "~/Downloads")))
mu4e-bookmarks
`(( :name "Inbox"
:query "maildir:/Inbox AND NOT flag:trashed"
:key ?i)
( :name "TODO"
:query "maildir:/TODO AND NOT flag:trashed"
:key ?T)
( :name "Unread messages"
:query "flag:unread AND NOT flag:trashed AND NOT list:ports-changes.openbsd.org AND NOT list:source-changes.openbsd.org"
:key ?u)
( :name "Today's messages"
:query "date:today..now"
:key ?d)
( :name "Hackers"
:query "list:hackers.openbsd.org AND NOT flag:trashed"
:key ?h)
( :name "Bugs"
:query "list:bugs.openbsd.org AND NOT flag:trashed"
:key ?b)
( :name "Tech"
:query "list:tech.openbsd.org AND NOT flag:trashed"
:key ?t)
( :name "Ports"
:query "list:ports.openbsd.org AND NOT flag:trashed"
:key ?p)
( :name "Misc"
:query "list:misc.openbsd.org AND NOT flag:trashed"
:key ?m)
( :name "9front"
:query "list:9front.9front.org AND NOT flag:trashed"
:key ?9)
( :name "GOT"
:query "list:gameoftrees.openbsd.org AND NOT flag:trashed"
:key ?g))))