home | section main page


Doom Literate Config

Table of Contents

1. config.el Configuration

This is a doom emacs configuration. If you are not using doom emacs, do not use this document.

1.1. Basic Information

My name, and the org mode directory on my computer, as well as basic editor configuration options. Below is the old documentation.

(setq user-full-name "Preston Pan"
      user-mail-address "preston@nullring.xyz")
(setq display-line-numbers-type t)
(setq x-select-enable-clipboard t)
(setq save-interprogram-paste-before-kill t)
(setq yank-pop-change-selection t)
(setq org-directory "~/org/")
(setq warning-minimum-level :emergency)
  • `load!' for loading external *.el files relative to this one
  • `add-load-path!' for adding directories to the `load-path', relative to this file. Emacs searches the `load-path' when you load packages with `require' or `use-package'.

1.2. Modeline

In order to display the time in the modeline:

(display-time-mode 1)

To display the battery percentage:

(display-battery-mode 1)

1.3. Transparency

Let's make emacs transparent.

(set-frame-parameter nil 'alpha-background 90)

(add-to-list 'default-frame-alist '(alpha-background . 90))

1.4. EXWM

First we load our packages:

;; (use-package! exwm)
;; (use-package! exwm-config)
;; (exwm-config-example)

Now, we set our keybindings:

;; (setq exwm-input-global-keys
;;       `(
;;         ([?\s-r] . exwm-reset)
;;         ([?\s-w] . exwm-workspace-switch)
;;         ,@(mapcar (lambda (i)
;;                     `(,(kbd (format "s-%d" i)) .
;;                       (lambda ()
;;                         (interactive)
;;                         (exwm-workspace-switch-create ,i))))
;;                   (number-sequence 0 9))
;;         ([?\s-&] . (lambda (command)
;;                      (interactive (list (read-shell-command "$ ")))
;;                      (start-process-shell-command command nil command)))

;;         ([?\s-d] . (lambda ()
;;                      (interactive)
;;                      (dired default-directory)))

;;         ([?\s-f] . (lambda ()
;;                      (interactive)
;;                      (exwm-layout-toggle-mode-line)
;;                      (exwm-workspace-toggle-minibuffer)))

;;         ([?\s-b] . exwm-workspace-switch-to-buffer)


;;         ([?\s-w] . (lambda ()
;;                      (interactive)
;;                      (start-process "" nil "qutebrowser")))
;;         ([?\s-n] . (lambda ()
;;                      (interactive)
;;                      (start-process "" nil "nyxt")))
;;         ([?\s-k] . (lambda ()
;;                      (interactive)
;;                      (start-process "" nil "krita")))
;;         ([?\s-g] . (lambda ()
;;                      (interactive)
;;                      (start-process "" nil "gimp")))
;;         ([?\s-b] . (lambda ()
;;                      (interactive)
;;                      (start-process "" nil "blender")))
;;         ([?\s-c] . (lambda ()
;;                      (interactive)
;;                      (start-process "" nil "chromium")))
;;         ([s-f2] . (lambda ()
;;                       (interactive)
;;                       (start-process "" nil "/usr/bin/slock")))))

And we also need to set up our media keys:

;; (exwm-input-set-key (kbd "<XF86AudioNext>") 'emms-next)
;; (exwm-input-set-key (kbd "<XF86AudioPrev>") 'emms-previous)
;; (exwm-input-set-key (kbd "<XF86AudioPlay>") 'emms-pause)
;; (exwm-input-set-key
;;    (kbd "<XF86AudioRaiseVolume>")
;;    (lambda ()
;;      (interactive) (start-process-shell-command
;;                     "pactl" nil "pactl set-sink-volume 0 +5% && pactl set-sink-volume 1 +5%")))
;; (exwm-input-set-key
;;    (kbd "<XF86AudioLowerVolume>")
;;    (lambda ()
;;      (interactive) (start-process-shell-command
;;                     "pactl" nil "pactl set-sink-volume 0 -5% && pactl set-sink-volume 1 -5%")))
;; (exwm-input-set-key
;;    (kbd "<XF86AudioMute>")
;;    (lambda ()
;;      (interactive) (start-process-shell-command
;;                     "pactl" nil "pactl set-sink-mute 0 tog
;; gle && pactl set-sink-mute 1 toggle")))
;; Things to implement in exwm:
;;Key([], 'XF86MonBrightnessUp', lazy.spawn("light -A 10")),
;;Key([], 'XF86MonBrightnessDown', lazy.spawn("light -U 10")),
;;Key([], "Print", lazy.spawn("scrot '%Y-%m-%d-%s_screenshot_$wx$h.jpg' -e 'mv $f ~/img/scrot")),

1.5. Font

Now we configure fonts:

(setq doom-font (font-spec :family "Hack" :size 16 :weight 'semi-light)
      doom-variable-pitch-font (font-spec :family "Fira Sans" :size 16)
      doom-unicode-font (font-spec :family "Symbola" :size 16)
      doom-serif-font (font-spec :family "Fira Sans" :size 16)
      doom-big-font (font-spec :family "Hack" :size 28))

1.6. Color Scheme

I'm experimenting with many themes right now. One of these themes is the city-lights theme, another one of them is the catppuccin theme.

;; (setq doom-theme 'doom-ayu-light)
(setq doom-theme 'doom-gruvbox)
;; (setq catppuccin-flavor 'mocha)
;; (load-theme 'catppuccin)

1.7. Doom Module and Programs Configuration

1.7.1. Agenda

Now we add these two files to our agenda search path:

(require 'org-habit)
(setq org-agenda-files (list "~/org/agenda.org"
                             "~/org/contacts.org"
                             "~/org/notes.org"))
(setq org-default-notes-file (concat org-directory "/notes.org"))

And we also want to set up org-habit to start graphing our habits as soon as possible:

(setq org-habit-preceding-days 1)

1.7.2. IRC

Set up circe to connect to my bouncer:

;; (after! circe
;;   (set-irc-server! "nullring.xyz"
;;     `(:tls t
;;       :port 4095
;;       :nick "LiCoO2/AndreiNet"
;;       :user "LiCoO2/AndreiNet"
;;       :pass ,(+pass-get-secret "ZNC"))))

And another to connect to libera:

(after! circe (set-irc-server! "irc.libera.chat"
  `(:tls t
    :port 6697
    :nick "ret2pop"
    :sasl-username "ret2pop"
    :sasl-password (lambda (&rest _) (+pass-get-secret "libera.chat"))
    :channels ("#emacs" "#rwx"))))
(after! circe (set-irc-server! "nullring.xyz"
  `(:tls t
    :port 6697
    :nick "LiCoO2")))

1.7.3. Email

In order to use this configuration, you must install and configure mu and mbsync.

;; (setq send-mail-function 'smtpmail-send-it)
;; (setq smtpmail-default-smtp-server "mail.nullring.xyz")
;; (setq smtpmail-smtp-server "mail.nullring.xyz")
;; (setq smtpmail-smtp-service 465)
;; (setq smtpmail-stream-type 'starttls)

(require 'smtpmail)
(setq send-mail-function 'smtpmail-send-it)
(setq smtpmail-smtp-server "mail.nullring.xyz")
(setq smtpmail-default-smtp-server "mail.nullring.xyz")
(setq smtpmail-smtp-service 465)
(setq smtpmail-smtp-user "preston@nullring.xyz")
(setq smtpmail-stream-type 'ssl)
(setq smtpmail-debug-info t)
(setq smtpmail-auth-credentials '(("mail.nullring.xyz" 465 "preston@nullring.xyz" "lO7Y`\"-si<zU")))

(set-email-account! "prestonpan"
  '((mu4e-sent-folder       . "/Sent")
    (mu4e-drafts-folder     . "/Drafts")
    (mu4e-trash-folder      . "/Trash")
    (smtpmail-smtp-user     . "preston@nullring.xyz")
    (user-mail-address      . "preston@nullring.xyz")    ;; only needed for mu < 1.4
    (mu4e-compose-signature . "---\nPreston Pan"))
  t)

1.7.4. RSS

We need to set up elfeed with a list of rss feeds.

(after! elfeed
  (setq elfeed-search-filter "@1-month-ago +unread"))
(add-hook! 'elfeed-search-mode-hook #'elfeed-update)
(setq rmh-elfeed-org-files '("~/org/elfeed.org"))

1.7.5. EWW

We want the default search engine of eww to be google because duckduckgo is bad:

(setq search-engines
      '(
        (("google" "g") "https://google.com/search?q=%s")
        (("duckduckgo" "d" "ddg") "https://duckduckgo.com/?q=%s")
        (("rfc" "r") "https://www.rfc-editor.org/rfc/rfc%s.txt")
        (("rfc-kw" "rk") "https://www.rfc-editor.org/search/rfc_search_detail.php?title=%s")))

(setq search-engine-default "google")
(setq eww-search-prefix "https://google.com/search?q=")
(setq browse-url-secondary-browser-function 'browse-url-generic browse-url-generic-program "qutebrowser")
(setq browse-url-browser-function 'eww-browse-url)
(add-hook 'eww-mode-hook
          (lambda () (local-set-key (kbd "y Y") #'eww-copy-page-url)))

1.7.6. Music

In order to use this configuration, you must have mpd configured to use the same directory. We automatically connect to mpd.

(emms-all)
(setq emms-source-file-default-directory (expand-file-name "~/music/"))
(setq emms-player-mpd-music-directory "~/music/")
(setq emms-player-mpd-server-name "localhost")
(setq emms-player-mpd-server-port "6600")
(setq emms-player-list '(emms-player-mpd))
(add-to-list 'emms-info-functions 'emms-info-mpd)
(add-to-list 'emms-player-list 'emms-player-mpd)
(emms-player-mpd-connect)

1.8. Keybindings

Now we set up our keybindings for our applications:

(map! :leader
      :desc "Open irc"
      "i c" #'circe)
(map! :leader
      :desc "Open audio manager"
      "m m" #'emms)
(map! :leader
      :desc "Open RSS feed reader"
      "r s" #'elfeed)
(map! :leader
      :desc "Open password manager"
      "p w" #'ivy-pass)
(map! :leader
      :desc "Open dictionary program"
      "d i" #'dictionary)
(map! :leader
      :desc "Open rtorrent frontend"
      "r t" #'mentor)
(map! :leader
      :desc "Open eww web browser"
      "e w" #'eww)

1.8.1. Journal

First we set the journal to be in the website directory:

(setq org-journal-dir "~/org/website/journal/")
(setq org-journal-date-format "%A, %d %B %Y")

And then we add the headers needed to export the journal automatically:

(defun org-journal-file-header-func (time)
  "Custom function to create journal header."
  (concat
    (pcase org-journal-file-type
      (`daily "#+TITLE: Daily Journal\n#+STARTUP: showeverything\n#+DESCRIPTION: My daily journal entry\n#+AUTHOR: Preston Pan\n#+HTML_HEAD: <link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />\n#+html_head: <script src=\"https://polyfill.io/v3/polyfill.min.js?features=es6\"></script>\n#+html_head: <script id=\"MathJax-script\" async src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js\"></script>\n#+options: broken-links:t")
      (`weekly "#+TITLE: Weekly Journal\n#+STARTUP: folded")
      (`monthly "#+TITLE: Monthly Journal\n#+STARTUP: folded")
      (`yearly "#+TITLE: Yearly Journal\n#+STARTUP: folded"))))

(setq org-journal-file-header 'org-journal-file-header-func)
(setq org-journal-file-format "%Y%m%d.org")

To add everything to the agenda search path, we toggle:

(setq org-journal-enable-agenda-integration t)

1.8.2. Brain

I don't use this anymore, but it's good to have.

(setq org-brain-path "~/org/website/brain/")

1.8.3. Roam

This is the configuration for my mindmap.

(setq org-roam-graph-viewer "qutebrowser")
(setq org-roam-directory (file-truename "~/org/website/mindmap"))
(setq org-roam-capture-templates '(("d" "default" plain "%?"
                                    :target (file+head "${title}.org"
                                                       "#+title: ${title}\n#+author: Preston Pan\n#+html_head: <link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" />\n#+html_head: <script src=\"https://polyfill.io/v3/polyfill.min.js?features=es6\"></script>\n#+html_head: <script id=\"MathJax-script\" async src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js\"></script>\n#+options: broken-links:t")
                                    :unnarrowed t)))

1.8.4. Publishing

In order to publish my website, we need to configure emacs to publish it somewhere and with diferrent parameters:

(require 'ox-publish)
(setq org-publish-project-alist
      '(("website-org"
         :base-directory "~/org/website"
         :base-extension "org"
         :publishing-directory "~/website_html"
         :recursive t
         :publishing-function org-html-publish-to-html
         :headline-levels 4
         :html-preamble t
         :html-preamble-format (("en" "<p class=\"preamble\"><a href=\"/index.html\">home</a> | <a href=\"./index.html\">section main page</a></p><hr>")))
        ("website-static"
         :base-directory "~/org/website"
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|ico"
         :publishing-directory "~/website_html/"
         :recursive t
         :publishing-function org-publish-attachment)
        ("website" :auto-sitemap t :components ("website-org" "website-static"))))
;; (setq org-export-html-postamble-format '(("en" "<p class=\"preamble\"><a href=\"../index.html\">previous page</a> | <a href=\"/index.html\">home</a></p>")))
(setq org-html-postamble "Copyright © 2024 Preston Pan")

1.8.5. Contacts

Now we configure org-contacts, which allows me to store contacts in an org mode file:

(setq org-contacts-files '("~/org/contacts.org"))

And then we need to add some templates with org-capture in order to add entries to the contacts easier:

(defvar my/org-contacts-template "* %^{name}
:PROPERTIES:
:ADDRESS: %^{289 Cleveland St. Brooklyn, 11206 NY, USA}
:BIRTHDAY: %^{yyyy-mm-dd}
:EMAIL: %^{Email}
:NOTE: %^{NOTE}
:END:" "Template for org-contacts.")

(setq org-capture-templates
   `(("c" "Contact" entry (file+headline "~/org/contacts.org" "Friends"), my/org-contacts-template
      :empty-lines 1)))

1.8.6. Org Timer

Sometimes I want a timer to help me keep track of the time.

(setq org-clock-sound "~/audio/ding.wav")

1.9. External Packages

we want to include some packages that don't come with doom emacs.

1.9.1. KBD-Mode

kbd-mode allows us to edit kmonad kbd files with syntax highlighting:

(use-package! kbd-mode)

1.9.2. Pinentry

We now set up pinentry for the pass program. We need to set the mode to loopback in order to enable emacs to start itself as a pinentry program, and we need to allow loopbacks in gpg-agent.conf.

(use-package! pinentry
        :init (setq epa-pinentry-mode `loopback)
               (pinentry-start))

1.9.3. Rainbow Mode

This is not used currently but might in the future.

(define-globalized-minor-mode global-rainbow-mode rainbow-mode
  (lambda ()
    (when (not (memq major-mode
                (list 'org-agenda-mode)))
     (rainbow-mode 1))))

1.9.4. Automatically tangle

Tangling manually every single time is kind of painful. Instead, we allow ourselves to set a flag in org that allows org to know we should tangle on save:

(use-package! org-auto-tangle
  :hook (org-mode . org-auto-tangle-mode))

1.9.5. Notifications

We use ednc to manage notifications.

(ednc-mode 1)

(defun show-notification-in-buffer (old new)
  (let ((name (format "Notification %d" (ednc-notification-id (or old new)))))
    (with-current-buffer (get-buffer-create name)
      (if new (let ((inhibit-read-only t))
                (if old (erase-buffer) (ednc-view-mode))
                (insert (ednc-format-notification new t))
                (pop-to-buffer (current-buffer)))
        (kill-buffer)))))

(add-hook 'ednc-notification-presentation-functions
          #'show-notification-in-buffer)

(evil-define-key 'normal ednc-view-mode-map
  (kbd "d")   'ednc-dismiss-notification
  (kbd "RET") 'ednc-invoke-action
  (kbd "e")   'ednc-toggle-expanded-view)

1.9.6. Playing Video

(setq empv-invidious-instance "https://yewtu.be/api/v1")

1.9.7. Mastodon

(setq mastodon-instance-url "https://types.pl")
(setq mastodon-active-user "ret2pop")

1.9.8. Ement

;; (ement-connect :uri-prefix "http://localhost:8009")

1.9.9. Stem

I wrote a major mode for my programming language stem.

(use-package stem-mode)
(add-to-list 'auto-mode-alist '("\\.stem\\'" . stem-mode))

1.9.10. Tufte

Our website uses the tufte css styling and we must therefore tell emacs to generate html that is compliant with this html:

(use-package! ox-tufte)
(use-package! plan9-theme)

1.9.11. This is supposed to work

but it doesn't

(setq org-export-with-section-numbers nil)

2. packages.el Configuration

These are some external packages that I use that are not provided by doom modules.

(unpin! evil-collection)
(package! evil-collection
  :recipe (:repo "kepi/evil-collection" :branch "mu4e-development"))

(package! pinentry)
(package! kbd-mode
  :recipe (:host github
           :repo "kmonad/kbd-mode"))
(package! nasm-mode)
(package! org-contrib)
(package! exwm)
(package! org-auto-tangle)
(package! rainbow-mode)
(package! ednc)
(package! mentor)
(package! request) ;; dependency for lemmy client
(package! plz) ;; dependency for lemmy client; either request or plz is idk what to use
(package! curl-to-elisp)
(package! empv)
(package! elpher)
(package! ement)
(package! mastodon)
(package! go-translate)
(package! ts)
(package! chess)
(package! ox-tufte)
(package! plan9-theme)

3. init.el Configuration

This installs all the doom modules that we are going to be configuring:

(doom! :input
       ;;bidi              ; (tfel ot) thgir etirw uoy gnipleh
       chinese
       japanese
       ;;layout            ; auie,ctsrnm is the superior home row

       :completion
       company
       ;;helm              ; the *other* search engine for love and life
       ;;ido               ; the other *other* search engine...
       (ivy +icons +fuzzy)
       ;; vertico

       :ui
       ;;deft              ; notational velocity for Emacs
       doom
       doom-dashboard
       doom-quit
       (emoji +unicode)
       hl-todo
       hydra
       indent-guides
       (ligatures +extra +fira)
       minimap
       modeline
       ;;nav-flash         ; blink cursor line after big motions
       ;;neotree           ; a project drawer, like NERDTree for vim
       ophints
       (popup +defaults)
       ;; tabs
       treemacs
       unicode
       (vc-gutter +pretty)
       vi-tilde-fringe
       window-select
       workspaces
       zen

       :editor
       (evil +everywhere)
       file-templates
       fold
       (format +onsave)
       ;;god               ; run Emacs commands without modifier keys
       lispy
       ;;multiple-cursors  ; editing in many places at once
       ;;objed             ; text object editing for the innocent
       parinfer
       ;;rotate-text       ; cycle region at point between text candidates
       snippets
       word-wrap

       :emacs
       dired
       electric
       (ibuffer +icons)
       undo
       vc

       :term
       eshell            ; the elisp shell that works everywhere
       ;;shell             ; simple shell REPL for Emacs
       ;; term           ; basic terminal emulator for Emacs
       vterm

       :checkers
       syntax
       (spell +flyspell)
       grammar

       :tools
       ;;ansible
       ;;biblio            ; Writes a PhD for you (citation needed)
       (debugger +lsp)
       ;;direnv
       ;;docker
       editorconfig
       ein
       (eval +overlay)
       gist
       (lookup +dictionary +offline)
       lsp
       magit
       make
       pass
       pdf
       ;;prodigy           ; FIXME managing external services & code builders
       rgb
       ;;taskrunner        ; taskrunner for all your projects
       ;;terraform         ; infrastructure as code
       tmux
       tree-sitter
       ;;upload            ; map local to remote projects via ssh/ftp

       :os
       (:if IS-MAC macos)
       tty

       :lang
       ;;agda              ; types of types of types of types...
       ;;beancount         ; mind the GAAP
       (cc +lsp)
       ;;clojure           ; java with a lisp
       common-lisp
       ;;coq               ; proofs-as-programs
       ;;crystal           ; ruby at the speed of c
       ;;csharp            ; unity, .NET, and mono shenanigans
       data
       ;;(dart +flutter)   ; paint ui and not much else
       ;;dhall
       ;;elixir            ; erlang done right
       ;;elm               ; care for a cup of TEA?
       emacs-lisp
       ;;erlang            ; an elegant language for a more civilized age
       ess
       ;;factor
       ;;faust             ; dsp, but you get to keep your soul
       ;;fortran           ; in FORTRAN, GOD is REAL (unless declared INTEGER)
       ;;fsharp            ; ML stands for Microsoft's Language
       ;;fstar             ; (dependent) types and (monadic) effects and Z3
       ;;gdscript          ; the language you waited for
       (go +lsp)
       ;;(graphql +lsp)    ; Give queries a REST
       (haskell +lsp)    ; a language that's lazier than I am
       ;;hy                ; readability of scheme w/ speed of python
       ;;idris             ; a language you can depend on
       (json +lsp)
       ;;(java +lsp)       ; the poster child for carpal tunnel syndrome
       (javascript +lsp)
       ;;julia             ; a better, faster MATLAB
       ;;kotlin            ; a better, slicker Java(Script)
       (latex +lsp +fold +cdlatex)
       ;;lean              ; for folks with too much to prove
       ;;ledger            ; be audit you can be
       ;;lua               ; one-based indices? one-based indices
       (markdown +grip)
       ;;nim               ; python + lisp at the speed of c
       nix
       ;;ocaml             ; an objective camel
       (org +journal +jupyter +gnuplot +brain +pretty +roam2)
       ;;php               ; perl's insecure younger brother
       ;;plantuml          ; diagrams for confusing people more
       ;;purescript        ; javascript, but functional
       (python +lsp +tree-sitter)
       ;;qt                ; the 'cutest' gui framework ever
       ;;racket            ; a DSL for DSLs
       ;;raku              ; the artist formerly known as perl6
       ;;rest              ; Emacs as a REST client
       ;;rst               ; ReST in peace
       ;;(ruby +rails)     ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
       (rust +lsp)
       ;;scala             ; java, but good
       (scheme +guile)
       (sh +fish +lsp)
       ;;sml
       solidity          ; do you need a blockchain? No.
       ;;swift             ; who asked for emoji variables?
       ;;terra             ; Earth and Moon in alignment for performance.
       (web +lsp)
       (yaml +lsp)
       ;;zig               ; C, but simpler

       :email
       (mu4e +org)
       ;;notmuch
       ;;(wanderlust +gmail)

       :app
       calendar
       emms
       everywhere
       irc
       (rss +org)
       ;;twitter           ; twitter client https://twitter.com/vnought

       :config
       literate
       (default +bindings +smartparens))
Copyright © 2024 Preston Pan