#+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
(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
'((:source "~/.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
** 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
** 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)
("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 'go-mode-hook 'eglot-ensure)
(add-hook 'elm-mode-hook 'eglot-ensure)
(add-hook 'nix-mode-hook 'eglot-ensure)
(add-hook 'typescript-mode-hook 'eglot-ensure)
(add-hook 'ruby-mode-hook 'eglot-ensure)
(add-hook 'perl-mode-hook 'eglot-ensure)
(define-key eglot-mode-map (kbd "C-c r") 'eglot-rename)
(define-key eglot-mode-map (kbd "C-c f") 'eglot-format)
(add-hook 'before-save-hook 'eglot-format-buffer))
#+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))
#+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
** sr-speedbar
Speedbar is almost perfect.. If it only ran in the current frame!! :D
**** Enter sr-speedbar
#+begin_src emacs-lisp
(use-package sr-speedbar)
#+end_src
*** Speedbar options
#+begin_src emacs-lisp
(setq
speedbar-show-unknown-files t
sr-speedbar-right-side nil)
(global-set-key (kbd "C-x C-n") 'sr-speedbar-toggle)
#+end_src
* Language Configurations
** 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)
(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 my-org-publish-alist
'(("notes" :components ("org-notes" "notes-static" "notes-rss"))
("deftly" :components ("deftly-blog" "deftly-static"))
("ohmyksh" :components ("ohmy-web" "ohmy-static"))
("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")
("mammoth"
:publishing-directory "/ssh:suah.dev:/var/www/mammothcircus.com/"
:publishing-function org-html-publish-to-html
:author-info nil
:creator-info nil
:section-numbers nil
:recursive t
:base-directory "~/org/mammoth")
("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")
))
#+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)
#+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
* 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:
#+begin_src shell
mu init --muhome=/home/qbit/.mu -m /home/qbit/Maildir/fastmail/ --my-address="aaron@bolddaemon.com"
#+end_src
** General mail configuration
#+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
** mu4e specific configs
#+begin_src emacs-lisp
(use-package mu4e
:init
(require 'mu4e-speedbar)
(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))))
#+end_src