#+PROPERTY: header-args:emacs-lisp :tangle yes
#+TITLE: 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!
#+begin_src emacs-lisp
(require 'bind-key)
(load "server")
(unless (server-running-p) (server-start))
#+end_src
** Interface and Behavior
*** Interface
Global font
#+begin_src emacs-lisp
(set-frame-font "Go Mono 11")
#+end_src
Use 80 columns, this helps keep things readable when windows are split
#+begin_src emacs-lisp
(setq whitespace-style '(trailing lines space-before-tab)
whitespace-line-column 80)
(setq-default fill-column 80)
#+end_src
I know I am in emacs, don't need to see the startup screen.
#+begin_src emacs-lisp
(setq inhibit-startup-screen t)
#+end_src
**** Use UTF8 where ever possible
#+begin_src emacs-lisp
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
#+end_src
**** Change various UI bits
#+begin_src emacs-lisp
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(column-number-mode +1)
(global-font-lock-mode 1)
#+end_src
**** direnv
#+begin_src emacs-lisp
(use-package direnv
:config
(direnv-mode))
#+end_src
**** project.el
#+begin_src emacs-lisp
(setq project-list-file (expand-file-name "~/.emacs.d/projects"))
#+end_src
**** transient
#+begin_src emacs-lisp
(setq transient-history-file (expand-file-name "~/.emacs.d/transient"))
#+end_src
*** Behavior
Switch various defaults to be more comfortable for myself.
#+begin_src emacs-lisp
(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"))
#+end_src
Use spelling and auto-fill when we are in text mode.
#+begin_src emacs-lisp
(add-hook 'text-mode-hook (lambda ()
(auto-fill-mode 1)
(turn-on-flyspell)))
#+end_src
#+begin_src emacs-lisp
(setq eshell-history-file-name (expand-file-name "~/.emacs.d/eshell/history"))
#+end_src
** Unset custom-file
The customization file mostly just causes churn in the SCM so we disable it
here.
#+begin_src emacs-lisp
(setq custom-file (make-temp-file ""))
#+end_src
* Packages
** yaml
#+begin_src emacs-lisp
(use-package yaml-mode)
#+end_src
** rust-mode
#+begin_src emacs-lisp
(use-package rust-mode)
#+end_src
** uxntal
#+begin_src emacs-lisp
(use-package uxntal-mode)
#+end_src
** breadcrumb
Handy breadcrumbs for seeing where things are in the LSP.
#+begin_src emacs-lisp
(use-package breadcrumb)
#+end_src
** ollama
Simple wrapper for ~ollama~ that lets me pipe regions and what not into various buffers.
#+begin_src emacs-lisp
(use-package ollama
:init
(setopt
ollama:endpoint "https://ollama.otter-alligator.ts.net/api/generate"
ollama:language "English"
ollama:model "llama3.1"))
#+end_src
** web-mode
#+begin_src emacs-lisp
(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)))
#+end_src
** scpaste
Post pastes to https://paste.suah.dev
#+begin_src emacs-lisp
(use-package scpaste
:config (setq scpaste-scp-destination "suah.dev:/var/www/paste"
scpaste-http-destination "https://paste.suah.dev"))
#+end_src
** htmlize
This is needed for publishing org stuff
#+begin_src emacs-lisp
(use-package htmlize)
#+end_src
** parchment-theme
This is a nice theme that resembles acme in plan9. Minimal.
#+begin_src emacs-lisp
(use-package parchment-theme
:config (load-theme 'parchment t))
#+end_src
** keychain-environment
I make heavy use of ~ssh-agent~ this lets emacs pickup / use the existing
agents I have running.
#+begin_src emacs-lisp
(use-package keychain-environment
;;:pin "melpa"
:init
(keychain-refresh-environment))
#+end_src
** 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).
#+begin_src emacs-lisp
(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))
#+end_src
** magit
Magit is a awesome. Not sure what else to say about it. :P
#+begin_src emacs-lisp
(use-package magit
:bind ("C-c m" . magit-status)
:init
(setq magit-completing-read-function 'ivy-completing-read))
#+end_src
** lsp
Use ~eglot~ for lsp stuff. It's built in and shows a bit more information for
auto-completion stuff.
#+begin_src emacs-lisp
(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))))
#+end_src
** company and friends
~company~ allows for auto-completion of various things. It can interface with ~lsp-mode~ to complete
things like Go.
#+begin_src emacs-lisp
(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))
#+end_src
** gitgutter
This gives me a nice in-ui way to see modifications and what not.
#+begin_src emacs-lisp
(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))
#+end_src
** 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.
#+begin_src emacs-lisp
;; 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))
#+end_src
* Language Configurations
** OCaml
#+begin_src emacs-lisp
(use-package ocamlformat
:custom (ocamlformat-enable 'enable-outside-detected-project)
:hook (before-save . ocamlformat-before-save))
(use-package tuareg
:mode ("\\.ml$" . tuareg-mode))
(use-package merlin
:config
(add-hook 'tuareg-mode-hook #'merlin-mode))
#+end_src
** firewall stuff
*** nftables
#+begin_src emacs-lisp
(use-package nftables-mode)
#+end_src
** Shell
#+begin_src emacs-lisp
(use-package shfmt)
(add-hook 'sh-mode-hook 'shfmt-on-save-mode)
#+end_src
** Typescript
#+begin_src emacs-lisp
(use-package typescript-mode)
#+end_src
** Nix
#+begin_src emacs-lisp
(use-package nix-mode
:mode "\\.nix\\'")
#+end_src
** Elm
#+begin_src emacs-lisp
(use-package elm-mode)
#+end_src
** Haskell
#+begin_src emacs-lisp
(use-package haskell-mode)
#+end_src
** Go
*** go-add-tags
This lets one select a ~struct~ or similar and auto add the ~`json:"NAME"`~ bits.
#+begin_src emacs-lisp
(use-package go-add-tags)
#+end_src
*** go-mode
This allows for things like ~gofmt~ and auto adding / removing of imports.
#+begin_src emacs-lisp
(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)
#+end_src
*** go-eldoc
This extends eldoc to be able to speak Go - quite handy for quickly looking
up what things do.
#+begin_src emacs-lisp
(use-package go-eldoc
:hook
(go-mode . go-eldoc-setup))
#+end_src
* org-mode
Oh ~org-mode~. It's the reason I started using emacs.. and it's the reason I
can't quit!
** Config
#+begin_src emacs-lisp
(org-babel-do-load-languages
'org-babel-load-languages
'((plantuml . t)
(dot . t)
(shell . t)
(latex . t)))
#+end_src
** Publish bits
I publish some of my notes [[https://suah.dev/p][on suah.dev/p]]. Also some recipes.
#+begin_src emacs-lisp
(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 "../"
:html-head ""
: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 ""
: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 ""
: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 ""
: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 ""
: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)
))
#+end_src
** Capture templates
#+begin_src emacs-lisp
(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")))
#+end_src
** org
#+begin_src emacs-lisp
(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)
#+end_src
** Extra bits
#+begin_src emacs-lisp
(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"))
#+end_src
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 to ~t~ on a task
headline. (properties can be set via ~C-c C-x d~
#+begin_src emacs-lisp
(require 'org-habit)
(require 'org-checklist)
#+end_src
Custom agenda commands for various things.
- ~Daily habits~ shows how well I am keeping track of daily things.
#+begin_src emacs-lisp
(setq org-agenda-custom-commands
'(("h" "Daily habits"
((agenda ""))
((org-agenda-show-log t)
(org-agenda-ndays 7)
(org-agenda-log-mode-items '(state))))))
#+end_src
#+begin_src emacs-lisp
(use-package org-roam
:after org
:custom
(org-roam-directory (file-truename "~/org-roam/"))
(org-roam-capture-templates
'(("d" "default" plain
"%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+date: %U\n")
:unnarrowed t)
("b" "book" plain
"- Author: /%^{Author}/\n- Year: /%^{Year}/\n\n* Highlights / Notes\n"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+date: %U\n#+filetags: :Book:\n")
:unnarrowed 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 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))
#+end_src
Extending org with the ability to transclude makes for a powerhouse!
#+begin_src emacs-lisp
(use-package org-transclusion
:after org)
#+end_src
* Mail
** 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.
#+begin_src emacs-lisp
(use-package gnus
:custom
(gnus-use-cache t)
(gnus-use-full-window nil)
(gnus-suppress-duplicates t)
(gnus-inhibit-startup-message t)
(gnus-asynchronous t)
(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")))
(gnus-select-method '(nnimap "imap.fastmail.com"
(nnimap-server-port "imaps")
(nnimap-stream ssl)
(nnimap-expunge 'on-exit)))
(gnus-secondary-select-methods '((nntp "news.gmane.io"))))
#+end_src
** SMTP
#+begin_src emacs-lisp
(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)
#+end_src