My Doom Emacs Config
Trying to escape local optima on a random walk through life.

Links

General Settings

Set name and email:

(setq user-full-name "Niklas Bühler"
      user-mail-address "hi@niklasbuehler.com")

Set default frame size:

(add-to-list 'default-frame-alist '(height . 24))
(add-to-list 'default-frame-alist '(width . 80))

Set default directory:

(setq default-directory "~/org")

Add contrib directory to load path:

(add-to-list 'load-path "~/.doom.d/contrib/")

Editing

Define some general undo and saving settings:

(setq undo-limit 80000000
      evil-want-fine-undo t
      auto-save-default t
      make-backup-files t)

Use relative line numbers, but also make them work with folds:

(setq display-line-numbers-type 'visual)

Evil Mode

Define vim commands:

(evil-ex-define-cmd "W[rite]" 'save-buffer)
(evil-ex-define-cmd "V[split]" 'evil-window-vsplit)

Behaviour

Navigation

Dired

Make dired quickly accessible:

(map! :leader :desc "Open dired for project" "d" #'project-dired)

Define evil mappings for dired:

(map! :map dired-mode-map
      :n "h" #'dired-up-directory
      :n "l" #'dired-find-alternate-file)

Text Completion

Enable completion:

(after! company
  (setq company-idle-delay 0.5
        company-minimum-prefix-length 2)
  (setq company-show-numbers t)
  (add-hook 'evil-normal-state-entry-hook #'company-abort))

And improve its memory:

(setq-default history-length 1000)
(setq-default prescient-history-length 1000)

Enable Ispell:

(set-company-backend!
  '(text-mode
    markdown-mode
    gfm-mode)
  '(:seperate
    company-ispell
    company-files
    company-yasnippet))

Show the key bindings popup a bit faster:

(setq which-key-idle-delay 0.25)

Looks

Theme

Set the light doom-one theme as default:

(setq doom-theme 'doom-one-light)

Focus Mode

Configure focus mode:

;;(setq focus-mode-to-thing "defun")

Fonts

Use the doom default fonts:

(setq doom-font nil)

Define different font sizes in org headings and the agenda view:

(after! org
  (custom-set-faces!
    '(org-document-title :height 1.75 :weight extra-bold)
    '(org-level-1 :inherit outline-1 :weight extra-bold :height 1.5)
    '(org-level-2 :inherit outline-2 :weight bold :height 1.25)
    '(org-level-3 :inherit outline-3 :weight bold :height 1.125)
    '(org-level-4 :inherit outline-4 :weight bold :height 1.0)
    '(org-level-5 :inherit outline-5 :weight semi-bold :height 1.0)
    '(org-level-6 :inherit outline-6 :weight semi-bold :height 1.0)
    '(org-level-7 :inherit outline-7 :weight semi-bold)
    '(org-level-8 :inherit outline-8 :weight semi-bold)
    '(org-super-agenda-header :weight bold :height 1.5)
    ;; Ensure that anything that should be fixed-pitch in org buffers appears that
    ;; way
    ;;'(org-block nil :foreground nil :inherit 'fixed-pitch)
    ;;'(org-code nil   :inherit '(shadow fixed-pitch))
    ;;'(org-table nil   :inherit '(shadow fixed-pitch))
    ;;'(org-verbatim nil :inherit '(shadow fixed-pitch))
    ;;'(org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
    ;;'(org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
    ;;'(org-checkbox nil :inherit 'fixed-pitch)
    ))

Define fancy priorities:

(setq org-fancy-priorities-list '((?A . "‼")
                                  (?B . "❗")
                                  (?C . "🟩")))

Org-Mode

General

Define the org and agenda file paths:

(setq org-directory "~/org/"
      org-agenda-files (list
                        "~/org/agenda"
                        "\.org$")
      org-archive-location (concat org-directory ".trash/archive.org::"))

Looks

Start in overview mode:

(after! org
  (setq org-startup-folded 'overview
        org-ellipsis " ▾ "
        org-list-demote-modify-bullet '(("+" . "-") ("-" . "+"))))

(add-hook! 'org-mode-hook #'+org-pretty-mode) ;; #'mixed-pitch-mode #'gkh/org-mode-visual)

Add spacing around org headings:

(customize-set-variable 'org-blank-before-new-entry
                        '((heading . nil)
                          (plain-list-item . nil)))
(setq org-cycle-separator-lines 1)

Show one newline in overview mode, if there are 2 newlines between headings:

(setq org-cycle-separator-lines 1)

Don’t assign a tag when attaching an image:

(setq org-attach-auto-tag nil)

Automatically resize images in org mode:

(setq org-image-actual-width '(600))

Automatically render images and latex previews, but show the latex source when the cursor enters:

(setq org-startup-with-latex-preview 1)
(setq org-startup-with-inline-images 1)
(add-hook 'org-mode-hook 'org-fragtog-mode)

Behaviour

General

Define TODO keywords:

  • ! stores a timestamp when entered
  • @ stores a timestamp + note when entered
  • Show available colors with list-colors-display
(after! org
  (setq org-log-done nil
        org-log-into-drawer 't
        org-todo-keywords '((sequence "TODO(t)" "PROJ(p)" "HOLD(h)" "WAIT(w!)" "BUY(b)" "IDEA(i)" "|" "DONE(d!)" "KILL(k!)"))
        org-todo-keyword-faces
        '(("TODO" :foreground "lime green" :weight bold :underline t)
          ("BUY" :foreground "sky blue" :weight bold :underline t)
          ("PROJ" :foreground "light slate gray" :weight normal :underline nil)
          ("WAIT" :foreground "slate blue" :weight normal :underline nil)
          ("IDEA" :foreground "light green" :weight normal :underline nil)
          ("HOLD" :foreground "dark slate blue" :weight normal :underline nil)
          ("DONE" :foreground "slate gray" :weight normal :underline nil :strike-through t)
          ("KILL" :foreground "red" :weight normal :underline nil :strike-through t))))

Reset checkboxes after completion of TODO item:

(require 'org-checklist)

Navigation

Enable rifle and add a key binding:

(map! :leader
      :desc "Rifle on agenda files"
      "/" #'helm-org-rifle-agenda-files)

Add key bindings for narrowing and widening on org subtrees:

(defun my/org-focus-subtree ()
  "Toggle org narrow subtree / show everything"
  (interactive)
  (if (buffer-narrowed-p)
      (widen)
    (org-narrow-to-subtree)))

(map! :leader
      :desc "Narrow on org subtree"
      "j" #'my/org-focus-subtree)
(setq org-cycle-emulate-tab nil)
(setq org-startup-folded 'content)

Tag clicks show sparse tree instead of agenda view:

(defun tag-at-point-in-heading ()
  "Returns the tag at the current point in the string"
  (let ((str (buffer-string))
        (begin (point))
        (end (point)))
    (while (not (equal (aref str begin) ?:))
      (setq begin (- begin 1)))
    (while (not (equal (aref str end) ?:))
      (setq end (+ end 1)))
    (substring str (+ 1 begin) end)))

(defun open-sparse-view ()
  "Shows a sparse tree on clicking a tag instead of org-tags-view"
  ;; From org-open-at-point, sanity checking that we're on a headline with tags
  (when (and (org-element-lineage (org-element-context)
                                  '(headline inlinetask)
                                  t)
             (progn (save-excursion (beginning-of-line)
                                    (looking-at org-complex-heading-regexp))
                    (and (match-beginning 5)
                         (> (point) (match-beginning 5)))))
    (org-match-sparse-tree nil (concat "+" (tag-at-point-in-heading)))
    't))

(add-hook 'org-open-at-point-functions
          'open-sparse-view)

Capturing

Define the capture templates:

(setq org-capture-templates
      '(("t" "Todo" entry (file+headline "~/org/agenda/todo.org" "Inbox")
         "* TODO %?"
         :empty-lines 0)
        ("j" "Journal" entry (file+datetree "~/org/journal.org")
         "* %U %?"
         :empty-lines 0)
        ("n" "Note" entry (file+datetree "~/org/notes.org")
         "* %U %?"
         :empty-lines 0)
        ("v" "Visualization" entry (file+datetree "~/org/journal.org")
         "* %U Who do I want to be and how would that person act today? :VISUALIZATION:\n- %?"
         :empty-lines 0)
        ("r" "Review")
        ("rm" "Monthly Review" entry (file+datetree "~/org/journal.org")
         "* Monthly Review %? :REVIEW:MONTH:\n** Good stuff\n- \n** Bad stuff\n- \n** Key Insights\n- \n** Next Level\n- \n** Yearly Goal Review\n- "
         :empty-lines 0)
        ("rw" "Weekly Review" entry (file+datetree "~/org/journal.org")
         "* Weekly Review KW %? :REVIEW:WEEK:\n** Good stuff\n- \n** Bad stuff\n- \n** Key Insights\n- \n** Next Level\n- "
         :empty-lines 0)
        ("rd" "Daily Review" entry (file+datetree "~/org/journal.org")
         "\n* %u Daily Review :REVIEW:DAY:\n** Daily Gratitude\n1. %?\n2. \n3. \n** What did I learn today?\n- "
         :empty-lines 0)))

Super Agenda

Enable super agenda:

(org-super-agenda-mode)

Define icons per category (list of unicodes):

(setq org-agenda-category-icon-alist
      '(("adventure" (print "🏕") nil nil :ascent center)
        ("vacation" (print "🏖") nil nil :ascent center)
        ("books" (print "📚") nil nil :ascent center)
        ("orga" (print "🗓") nil nil :ascent center)
        ("routines" (print "☑") nil nil :ascent center)
        ("reset" (print "🧹") nil nil :ascent center)
        ("review" (print "✍") nil nil :ascent center)
        ("finances" (print "💵") nil nil :ascent center)
        ("accounting" (print "💰") nil nil :ascent center)
        ("investing" (print "📈") nil nil :ascent center)
        ("groceries" (print "🛒") nil nil :ascent center)
        ("health" (print "❤") nil nil :ascent center)
        ("cardio" (print "🏃") nil nil :ascent center)
        ("strength" (print "🏋") nil nil :ascent center)
        ("sports" (print "🏅") nil nil :ascent center)
        ("newcon" (print "🟢") nil nil :ascent center)
        ("bus+" (print "🚌") nil nil :ascent center)
        ("shortbreak" (print "⏸") nil nil :ascent center)
        ("project" (print "🛠") nil nil :ascent center)
        ("selfdev" (print "🚀") nil nil :ascent center)
        ("shopping" (print "🛒") nil nil :ascent center)
        ("social" (print "👥") nil nil :ascent center)
        ("random" (print "❔") nil nil :ascent center)
        ("MLGS" (print "🤖") nil nil :ascent center)
        ("TN" (print "🌐") nil nil :ascent center)
        ("MLRG" (print "🧬") nil nil :ascent center)
        ("ES" (print "🇪🇸") nil nil :ascent center)
        ("PPI" (print "🔬") nil nil :ascent center)
        ("BDL" (print "🧠") nil nil :ascent center)
        ("ADLM" (print "🩻") nil nil :ascent center)
        ("erasmus" (print "✈") nil nil :ascent center)
        ("uni" (print "🎓") nil nil :ascent center)
        ))

Define a custom super agenda view:

(setq org-agenda-skip-scheduled-if-done t
      org-agenda-skip-deadline-prewarning-if-scheduled t
      org-agenda-skip-deadline-if-done t
      org-agenda-include-deadlines t
      org-agenda-block-separator nil
      org-agenda-compact-blocks t
      org-agenda-start-day nil ;; i.e. today
      org-agenda-span 1
      org-agenda-start-on-weekday nil)
(setq org-agenda-custom-commands
      '(("d" "Daily Super Agenda"
         ((agenda "" ((org-agenda-overriding-header "")
                      (org-super-agenda-groups
                       '((:name "☑ Daily"
                          :time-grid t
                          :order 0)
                         ;;(:discard (:scheduled future))
                         (:name "☑ Routines"
                          ;;:category ("orga" "reset" "review")
                          :file-path ("routines.org")
                          :order 10)
                         (:name "🏅 Sports"
                          :category ("sports" "cardio" "strength")
                          :order 20)
                         (:name "🎓 Uni"
                          ;;:category ("uni" "erasmus" "MLGS" "MLRG" "TN" "ES" "PPI" "BDL" "ADLM")
                          :file-path ("uni.org")
                          :order 30)
                         (:name "🟢 newcon GmbH"
                          ;;:category ("newcon" "bus+" "shortbreak")
                          :file-path ("newcon.org")
                          :order 40)
                         (:name "👥 Social"
                          ;;:category "social"
                          :file-path ("social.org")
                          :order 50)
                         (:name "🚀 Selfdev"
                          :file-path ("selfdev.org")
                          :order 60)
                         (:name "💵 Finances"
                          ;;:category ("finances" "accounting" "investing")
                          :file-path ("finances.org")
                          :order 70)
                         (:name "🛠 Projects"
                          :category "projects"
                          :order 80)
                         (:name "❔ Random"
                          :anything t
                          :order 99)
                         ;;(:name "❔ Random"
                         ;;:category "random"
                         ;;:order 99)
                         ;;(:auto-category t
                         ;;:order 100)
                         ))
                      (org-agenda-list)))))
        ("u" "Unscheduled TODOs"
         ((alltodo "" ((org-agenda-overriding-header "")
                       (org-super-agenda-groups
                        '((:discard (:scheduled past
                                     :scheduled today
                                     :scheduled future
                                     :deadline past
                                     :deadline today
                                     :deadline future))
                          (:discard (:todo "PROJ"
                                     :category "groceries"
                                     :category "routines"))
                          (:name "⏱ WAIT: Review"
                           :todo "WAIT"
                           :order 0)
                          (:name "🖐 HOLD: Review"
                           :todo "HOLD"
                           :order 10)
                         (:name "🏅 Sports"
                          :category ("sports" "cardio" "strength")
                          :order 20)
                         (:name "🎓 Uni"
                          ;;:category ("uni" "erasmus" "MLGS" "MLRG" "TN" "ES" "PPI" "BDL" "ADLM")
                          :file-path ("uni.org")
                          :order 30)
                         (:name "🟢 newcon GmbH"
                          ;;:category ("newcon" "bus+" "shortbreak")
                          :file-path ("newcon.org")
                          :order 40)
                         (:name "👥 Social"
                          ;;:category "social"
                          :file-path ("social.org")
                          :order 50)
                         (:name "🚀 Selfdev"
                          :file-path ("selfdev.org")
                          :order 60)
                         (:name "💵 Finances"
                          ;;:category ("finances" "accounting" "investing")
                          :file-path ("finances.org")
                          :order 70)
                         (:name "🛠 Projects"
                          :category "projects"
                          :order 80)
                         (:name "🛒 Shopping"
                          :category "shopping"
                          :order 90)
                         (:name "❔ Random: Refile"
                          :anything t
                          :order 15)
                          )
                        )
                       (org-todo-list)))))
        ))

Make the daily view quickly accessible:

(map! :leader :desc "Open daily super agenda" "a" #'(lambda () (interactive) (org-agenda nil "d")))

Roam

Configure roam2:

(use-package! org-roam
  :init
  (map! :leader
        :prefix ("r" . "roam")
        :desc "Find Node" "f" #'org-roam-node-find
        :desc "Insert Node" "i" #'org-roam-node-insert
        :desc "Find Ref" "r" #'org-roam-ref-find
        :desc "Show Graph" "g" #'org-roam-graph
        :desc "Capture" "c" #'org-roam-capture)
  (setq org-roam-directory "~/org/roam"
        ;;org-roam-database-connector 'sqlite-builtin
        ;;org-roam-db-gc-threshold most-positive-fixnum
        org-id-link-to-org-use-id nil)
  :config
  (org-roam-db-autosync-mode +1)
  (add-hook 'org-roam-mode-hook #'turn-on-visual-line-mode))

Website

Define the org project to be published:

(setq org-publish-project-alist
      '(("org-notes"
        :base-directory "~/org/website/src"
        :base-extension "org"
        :publishing-directory "~/org/website/pub/"
        :recursive t
        :publishing-function org-html-publish-to-html
        :headline-levels 4
        :with-author nil
        :time-stamp-file nil
        :with-creator nil
        :with-toc nil
        :section-numbers nil
        :auto-preamble t)
        ("org-static"
        :base-directory "~/org/website/src"
        :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|ico"
        :publishing-directory "~/org/website/pub/"
        :recursive t
        :publishing-function org-publish-attachment)
        ("org" :components ("org-notes" "org-static"))
        ))

Reading

Configure calibredb:

(use-package calibredb
  :defer t
  :config
  (setq calibredb-root-dir "~/Library")
  (setq calibredb-db-dir (expand-file-name "metadata.db" calibredb-root-dir))
  (setq calibredb-library-alist '(("~/Library"))))

Supply surrogate for required string-replace function:

;; backport for older emacs versions
(if (not (fboundp 'string-replace))
    (progn
      (defun string-replace (from to in)
        (replace-regexp-in-string (regexp-quote from) to in nil 'literal))
      (declare-function string-replace "nov")
    ))

Automatically open ebooks in nov-mode:

(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))

Shoplist

Configure the org-shoplist package:

(setq org-shoplist-default-format 'org-shoplist-shoplist-as-todo-list)
(setq org-shoplist-buffer-name "🛍 Groceries")
(setq org-shoplist-auto-add-unit 't)
(setq org-shoplist-keyword "BUY")

Fixes

Empty src field for quick experiments:

  • Hit C-c C-c to apply code

TODO

  1. [ ] Disable crossref information (slows down)?
  2. [ ] Show Dashboard on startup