#+TITLE: DOOM Emacs Configuration #+STARTUP: indent #+PROPERTY: header-args :tangle yes * Custom settings Store options set via =customize-*= in a separate file (Emacs stores them in =init.el= by default). #+BEGIN_SRC emacs-lisp (setq custom-file "~/.emacs.d/custom.el") (if (file-exists-p custom-file) (load custom-file)) (setf custom-safe-themes t) #+END_SRC * UX Improvements ** Disable C-z backgrounding when the GUI is loaded In OSX, this minimizes the window. It's incredibly annoying, and I accidentally hit it too much. I also can't think of any reason I'd want to background Emacs outside of a terminal session. #+begin_src emacs-lisp (if (display-graphic-p) (global-unset-key (kbd "C-z"))) #+end_src ** Mixed Pitch Facilitates mixing monospace and proportional fonts. I'm using an MIT-licensed version of the [[https://github.com/edwardtufte/et-book][Edward Tufte book font]] because it's /gorgeous/. #+begin_src emacs-lisp (use-package! mixed-pitch :hook ((org-mode markdown-mode rst-mode) . mixed-pitch-mode)) (setq doom-variable-pitch-font (font-spec :family "ETBookOT" :size 19)) #+end_src * Dashboard #+begin_src emacs-lisp (defun my/choice (&rest things) (let ((index (random (length things)))) (nth index things))) (defun my/dashboard-randomize () (interactive) (let ((logo (my/choice "~/Pictures/Patreon/Jon Bliss/phoenix-and-catgirl-500.png" "~/Pictures/Patreon/Jon Bliss/Bassist-final-transparent-500.png" "~/Pictures/Patreon/Jon Bliss/catgirl-final-transparent-500.png" "~/Pictures/Patreon/Jon Bliss/lapiz-final-transparent-500.png" "~/Pictures/Patreon/Jon Bliss/Poledancer-final-transparent-500.png" "~/Pictures/Patreon/Jon Bliss/Hacker-final-transparent-500.png" "~/Pictures/Patreon/Bee/IMG_3666_transparent_500.png"))) (setq fancy-splash-image logo) (if (called-interactively-p) (+doom-dashboard-reload)))) (my/dashboard-randomize) (map! :map +doom-dashboard-mode-map "g" #'my/dashboard-randomize) #+end_src * BibTeX #+begin_src emacs-lisp (setq my/bibliographies '("~/Documents/bibliography/references.bib" "~/Documents/bibliography/calibre.bib")) #+end_src ** Helm BibTeX #+begin_src emacs-lisp (setq bibtex-completion-bibliography my/bibliographies) (setq bibtex-completion-pdf-field "File") #+end_src * Org :PROPERTIES: :header-args: :tangle no :noweb-ref org :END: #+begin_src emacs-lisp :noweb yes :tangle yes :noweb-ref org-all (after! org <>) #+end_src ** Override DOOM indentation behavior #+begin_src emacs-lisp (defun my/org-init-babel () (setq org-src-preserve-indentation nil)) (add-hook! 'org-mode-hook #'my/org-init-babel) #+end_src ** Disable DOOM's centralized attachment system It's incompatible with all of the org files I already have using the standard setup. #+begin_src emacs-lisp (setq org-attach-directory "data/") (remove-hook! 'org-load-hook #'(+org-init-centralized-attachments-h)) #+end_src ** Agenda #+begin_src emacs-lisp (after! org-agenda (require 'f) (setq my/agenda-files '((personal . ("~/Nextcloud/org/personal.org")) (work . ("~/Nextcloud/org/aweber.org"))) org-agenda-files (-filter #'f-exists? (-concat (if (string-equal (system-name) "s1069.ofc.lair") (cdr (assoc 'work my/agenda-files)) (cdr (assoc 'personal my/agenda-files)))))) (setq org-stuck-projects '("+LEVEL=1/-DONE" ("TODO" "NEXT" "NEXTACTION") nil "")) ;; https://www.tompurl.com/2015-12-29-emacs-eisenhower-matrix.html (setq org-tag-alist '(("important" . ?i) ("urgent" . ?u))) (setq org-agenda-custom-commands '(("n" "Agenda and all TODOs" ((agenda "" ((org-agenda-span 'week))) (tags-todo "DEADLINE<=\"<+7d>\"" ((org-agenda-overriding-header "Due soon"))) (todo "")) ((org-agenda-start-with-log-mode t) (org-agenda-start-day nil) (org-agenda-span 'day) (org-agenda-log-mode-items '(clock state closed))) ("~/Public/org/agenda.html" "~/Public/org/agenda.ics")) ("l" "Log" agenda "" ((org-agenda-span 'fortnight) (org-agenda-start-day "-1w") (org-agenda-start-with-log-mode t) (org-agenda-log-mode-items '(clock state closed)) (org-agenda-include-deadlines nil) (org-agenda-skip-scheduled-delay-if-deadline t)) ("~/Public/org/agenda-log.html")) ("e" "Eisenhower Matrix" ((tags-todo "+important+urgent" ((org-agenda-overriding-header "Do"))) (tags-todo "+important-urgent" ((org-agenda-overriding-header "Decide"))) (tags-todo "-important+urgent" ((org-agenda-overriding-header "Delegate"))) (tags-todo "-important-urgent" ((org-agenda-overriding-header "Delete")))) ((org-agenda-start-with-log-mode t) (org-agenda-span 'day) (org-agenda-log-mode-items '(clock state closed)))))) (defun my/org-agenda-timeline () (interactive) (let ((org-agenda-files (list (buffer-file-name)))) (org-agenda))) (setq org-agenda-start-on-weekday nil) (setq org-agenda-span 'fortnight) (setq org-agenda-todo-ignore-scheduled 'future) (setq org-agenda-tags-todo-honor-ignore-options t) (setq org-agenda-skip-deadline-prewarning-if-scheduled t) (add-hook 'org-agenda-finalize-hook (lambda () (hl-line-mode))) (setq org-icalendar-use-scheduled '(todo-start event-if-todo) org-icalendar-combined-agenda-file (expand-file-name "~/Documents/org.ics"))) #+end_src ** LaTeX Export *** Document Classes #+BEGIN_SRC emacs-lisp (use-package! ox-latex :config (seq-map (apply-partially #'add-to-list 'org-latex-classes) '(("koma-letter" "\\documentclass{scrlttr2}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("koma-article" "\\documentclass{scrartcl}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("koma-book" "\\documentclass{scrbook}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("koma-book-chapters" "\\documentclass{scrbook}" ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("koma-report" "\\documentclass{scrreprt}" ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("memoir" "\\documentclass{memoir}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("hitec" "\\documentclass{hitec}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("paper" "\\documentclass{paper}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("letter" "\\documentclass{letter}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("tufte-handout" "\\documentclass{tufte-handout}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("tufte-book" "\\documentclass{tufte-book}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("tufte-book-chapters" "\\documentclass{tufte-book}" ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) ("labbook" "\\documentclass{labbook}" ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\labday{%s}") ("\\subsubsection{%s}" . "\\experiment{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))) #+END_SRC *** DnD #+begin_src emacs-lisp (use-package! ox-dnd :after ox) #+end_src ** Capture templates #+begin_src emacs-lisp (setq org-capture-templates `( ;; Personal ("j" "Journal Entry" plain (file+datetree "~/org/journal.org") "%U\n\n%?" :empty-lines-before 1) ("t" "TODO" entry (file+headline "~/Nextcloud/org/personal.org" "Unsorted") "* TODO %^{Description}\n%?") ("n" "Note" entry (file+headline "~/Nextcloud/org/personal.org" "Notes") "* %^{Description}\n%U\n\n%?") ;; Org-Protocol ("b" "Bookmark" entry (file+headline "~/org/bookmarks.org" "Unsorted") "* %^{Title}\n\n Source: %u, %c\n\n %i") ("p" "Webpage" entry (file "~/org/articles.org") "* %a\n\n%U %?\n\n%:initial") ;; Email ;; https://martinralbrecht.wordpress.com/2016/05/30/handling-email-with-emacs/ ("r" "respond to email (mu4e)" entry (file+headline "~/org/todo.org" "Email") "* REPLY to [[mailto:%:fromaddress][%:fromname]] on %a\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+1d\"))\n%U\n\n" :immediate-finish t :prepend t) ;; Work ("w" "Work") ("wt" "Work TODO" entry (file+headline "~/Nextcloud/org/aweber.org" "Unsorted") "* TODO %^{Description}\n%?") ("wl" "Log Work Task" entry (file+datetree "~/org-aweber/worklog.org") "* %^{Description} %^g\nAdded: %U\n\n%?" :clock-in t :clock-keep t) ("wL" "Log Work Task (no clock)" entry (file+datetree "~/org-aweber/worklog.org") "* %^{Description} %^g\nAdded: %U\n\n%?") ("wj" "Log work on JIRA issue" entry (file+datetree "~/org-aweber/worklog.org") ,(concat "* %?\n" ":PROPERTIES:\n" ":JIRA_ID: %^{JIRA_ID}\n" ":END:\n" "Added: %U\n\n" "[[jira:%\\1][%\\1]]") :clock-in t :clock-keep t) ("wr" "respond to email (mu4e)" entry (file+headline "~/Nextcloud/org/aweber.org" "Unsorted") "* REPLY to [[mailto:%:fromaddress][%:fromname]] on %a\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+1d\"))\n%U\n\n" :immediate-finish t :prepend t))) #+end_src ** Custom ID generation Because I'm all kinds of crazy, I like the custom IDs of my work log entries to be based on their headings. #+begin_src emacs-lisp (use-package! org-id :after org :config ;; https://writequit.org/articles/emacs-org-mode-generate-ids.html#automating-id-creation (defun eos/org-custom-id-get (&optional pom create prefix) "Get the CUSTOM_ID property of the entry at point-or-marker POM. If POM is nil, refer to the entry at point. If the entry does not have an CUSTOM_ID, the function returns nil. However, when CREATE is non nil, create a CUSTOM_ID if none is present already. PREFIX will be passed through to `org-id-new'. In any case, the CUSTOM_ID of the entry is returned." (interactive) (org-with-point-at pom (let ((id (org-entry-get nil "CUSTOM_ID"))) (cond ((and id (stringp id) (string-match "\\S-" id)) id) (create (setq id (org-id-new (concat prefix "h"))) (org-entry-put pom "CUSTOM_ID" id) (org-id-add-location id (buffer-file-name (buffer-base-buffer))) id))))) (defun eos/org-add-ids-to-headlines-in-file () "Add CUSTOM_ID properties to all headlines in the current file which do not already have one. Only adds ids if the `auto-id' option is set to `t' in the file somewhere. ie, ,#+OPTIONS: auto-id:t" (interactive) (save-excursion (widen) (goto-char (point-min)) (when (re-search-forward "^#\\+OPTIONS:.*auto-id:t" (point-max) t) (org-map-entries (lambda () (eos/org-id-get (point) 'create))))) (save-excursion (widen) (goto-char (point-min)) (when (re-search-forward "^#\\+OPTIONS:.*auto-id:worklog" (point-max) t) (let ((my/org-worklog-id-depth 2)) (org-map-entries (lambda () (my/org-worklog-id-get (point) 'create)))))) (save-excursion (widen) (goto-char (point-min)) (when (re-search-forward "^#\\+OPTIONS:.*auto-id:readable" (point-max) t) (let ((my/org-worklog-id-depth 0)) (org-map-entries (lambda () (my/org-worklog-id-get (point) 'create))))))) ;; automatically add ids to saved org-mode headlines (add-hook 'org-mode-hook (lambda () (add-hook 'before-save-hook (lambda () (when (and (eq major-mode 'org-mode) (eq buffer-read-only nil)) (eos/org-add-ids-to-headlines-in-file)))))) (defun my/org-remove-all-ids () (interactive) (save-excursion (widen) (goto-char (point-min)) (org-map-entries (lambda () (org-entry-delete (point) "CUSTOM_ID"))))) (defvar my/org-worklog-id-depth 2) (defun my/org-worklog-id-new (&optional prefix) (let ((path (or (-drop my/org-worklog-id-depth (org-get-outline-path t)) (last (org-get-outline-path t))))) (mapconcat (lambda (s) (->> s (s-downcase) (s-replace-regexp "[^[:alnum:]]+" "-"))) path "-"))) (defun my/org-worklog-id-get (&optional pom create prefix) (interactive) (org-with-point-at pom (let ((id (org-entry-get nil "CUSTOM_ID"))) (cond ((and id (stringp id) (string-match "\\S-" id)) id) (create (setq id (my/org-worklog-id-new prefix)) (org-entry-put pom "CUSTOM_ID" id) id)))))) #+end_src ** Publish projects #+begin_src emacs-lisp (require 'org-attach) (setq org-html-mathjax-options '((path "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-AMS-MML_HTMLorMML"))) (setq org-re-reveal-root "https://cdn.jsdelivr.net/reveal.js/3.0.0/") (defun my/org-work-publish-to-html (plist filename pub-dir) (message "Publishing %s" filename) (cond ((string-match-p "slides.org$" filename) (org-re-reveal-publish-to-reveal plist filename pub-dir)) (t (let ((org-html-head (concat ;; Tufte ;; "" ;; "" ;; Org-Spec ;; "" ;; "" ;; "" ;; ReadTheOrg "" "" "" "" "" "" "" ;; Bigblow ;; "" ;; "" ;; "" ;; "" ;; "" ;; "" ;; "" ;; "" ;; "" ;; "" ;; "" ))) (save-excursion (save-restriction (org-html-publish-to-html plist filename pub-dir))))))) ;; (setq my/org-base-url (concat "/~" (getenv "USER") "/org/")) (setq my/org-base-url "/") (setq my/org-base-url "https://correlr.gitlab.aweber.io/org/") (setq org-publish-project-alist `( ;; ("work-common" ;; :base-directory "~/org/common" ;; :publishing-directory "~/Public/org" ;; :base-extension "css\\|gif\\|jpe?g\\|png\\|svg" ;; :recursive t ;; :publishing-function org-publish-attachment) ("work-themes" :base-directory "~/.emacs.local.d/org-html-themes/styles" :publishing-directory "~/Public/org/styles" :base-extension "js\\|css\\|gif\\|jpe?g\\|png\\|svg\\|ogv" :recursive t :publishing-function org-publish-attachment) ("work-html" :base-directory "~/org-aweber" :base-extension "org" ;; :exclude "\\(^knowledge-transfer.org$\\|-archive.org$\\)" :exclude "\\(^README.org$\\|roam/.*\\)" :publishing-directory "~/Public/org" :publishing-function (my/org-work-publish-to-html org-org-publish-to-org org-babel-tangle-publish) ;; :htmlized-source t ;; :html-head "" ;; :html-head-extra "" ;; :setup-file "~/.emacs.local.d/org-html-themes/setup/theme-readtheorg-local.setup" :html-link-home ,my/org-base-url :html-doctype "html5" :html-html5-fancy t :with-sub-superscript nil :section-numbers nil ;; :infojs-opt "path:http://thomasf.github.io/solarized-css/org-info.min.js view:showall" :auto-sitemap t :sitemap-filename "index.org" :sitemap-title "Correl Roush's Org Documents" :sitemap-sort-folders last :recursive t) ("work-roam-html" :base-directory "~/org-aweber/roam" :base-extension "org" :publishing-directory "~/Public/org/roam" :recursive t :with-toc nil :section-numbers nil :auto-sitemap t :sitemap-filename "index.org" :sitemap-title "Correl Roush's Org Roam Notes" :publishing-function org-html-publish-to-html :html-head "") ("work-assets" :base-directory "~/org-aweber" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg\\|pdf\\|ogv\\|py\\|html\\|ya?ml" :include (".gitlab-ci.yml") :publishing-directory "~/Public/org" :publishing-function org-publish-attachment :display-custom-times t :recursive t) ("work-todo" :base-directory "~/Nextcloud/org" :exclude ".*" :include ("aweber.org") :html-head "" :html-head-extra "" :publishing-directory "~/Public/org" :publishing-function org-html-publish-to-tufte-html) ("work" :components ("work-html" "work-roam-html" "work-todo" "work-assets" "work-themes")) ("dotfiles-common" :base-directory "~/dotfiles" :publishing-directory "~/Public/dotfiles" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg" :recursive t :publishing-function org-publish-attachment) ("dotfiles-html" :base-directory "~/dotfiles" :base-extension "org" :publishing-directory "~/Public/dotfiles" :publishing-function (org-html-publish-to-html org-babel-tangle-publish) :htmlized-source t :html-head "" :html-head-extra "" :html-link-home "/~croush/dotfiles/" :html-doctype "html5" :html-html5-fancy t :with-sub-superscript nil :infojs-opt "path:http://thomasf.github.io/solarized-css/org-info.min.js view:showall" :auto-sitemap t :sitemap-filename "index.org" :sitemap-title "Correl Roush's Dotfiles" :sitemap-sort-folders last :recursive t) ("dotfiles-assets" :base-directory "~/dotfiles" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg" :publishing-directory "~/Public/dotfiles" :publishing-function org-publish-attachment :recursive t) ("dotfiles" :components ("dotfiles-common" "dotfiles-html" "dotfiles-assets")) ("personal-themes" :base-directory "~/.emacs.local.d/org-html-themes/styles" :publishing-directory "~/Public/personal/styles" :base-extension "js\\|css\\|gif\\|jpe?g\\|png\\|svg" :recursive t :publishing-function org-publish-attachment) ("personal-html" :base-directory "~/org" :base-extension "org" :publishing-directory "~/Public/personal" :recursive t :with-toc t :auto-sitemap t :sitemap-title "Correl Roush's Org Files" :sitemap-filename "index.org" :publishing-function org-html-publish-to-tufte-html :html-head ,(concat ;; Tufte "" "")) ;; Org-Spec ;; "" ;; "" ("personal-files" :base-directory "~/org" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg" :publishing-directory "~/Public/personal" :recursive t :publishing-function org-publish-attachment) ("personal-assets" :base-directory "~/org" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg\\|pdf" :publishing-directory "~/Public/personal" :publishing-function org-publish-attachment :recursive t) ("personal" :components ("personal-themes" "personal-html" "personal-files" "personal-assets")) ("journal" :base-directory "~/org" :exclude ".*" :include ("journal.org") :publishing-directory "~/journal" :publishing-function (org-html-publish-to-html org-latex-export-to-pdf)) ("roam-html" :base-directory "~/org/roam" :base-extension "org" :publishing-directory "~/Public/roam" :recursive t :with-toc nil :section-numbers nil :auto-sitemap nil :publishing-function org-html-publish-to-html :html-head "") ("roam-assets" :base-directory "~/org/roam" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg\\|pdf" :publishing-directory "~/Public/roam" :publishing-function org-publish-attachment :recursive t) ("roam" :components ("roam-html" "roam-assets")) ("sicp-html" :base-directory "~/code/sicp" :base-extension "org" :publishing-directory "~/Public/sicp" :publishing-function (org-html-publish-to-html org-org-publish-to-org org-babel-tangle-publish) :htmlized-source t :html-head "" :html-link-home "/" :html-doctype "html5" :html-html5-fancy t :with-sub-superscript nil :auto-sitemap t :sitemap-filename "index.org" :sitemap-title "SICP Exercises and Notes" :sitemap-sort-folders last :recursive t) ("sicp-assets" :base-directory "~/code/sicp" :base-extension "css\\|gif\\|jpe?g\\|png\\|svg\\|scheme\\|pl" :publishing-directory "~/Public/sicp" :publishing-function org-publish-attachment :recursive t) ("sicp" :components ("sicp-html" "sicp-assets")))) ;; Don't prompt for babel evaluation, ever. (setq org-confirm-babel-evaluate nil) (require 'ox-confluence) (defun my/org-publish () (interactive) (org-publish "work") (let ((org-link-abbrev-alist (seq-concatenate 'list org-link-abbrev-alist '(("jira" . "https://jira.aweber.io/browse/") ("gitlab" . "https://gitlab.aweber.io/"))))) (org-store-agenda-views)) (shell-command "org-publish")) (bind-key "C-c o p" #'my/org-publish) #+end_src ** Enhanced Confluence export #+begin_src emacs-lisp (use-package! ox-confluence-en :after ox :commands ox-confluence-en-export-as-confluence) #+end_src ** Reload images on source execution #+begin_src emacs-lisp (defun my/redisplay-org-images () (when org-inline-image-overlays (org-redisplay-inline-images))) (add-hook 'org-babel-after-execute-hook 'my/redisplay-org-images) #+end_src ** Sticky headers #+begin_src emacs-lisp (use-package! org-sticky-header :hook (org-mode . org-sticky-header-mode) :config (setq org-sticky-header-full-path 'full)) #+end_src ** Library of Babel Load shared code snippets to be used in org documents. #+begin_src emacs-lisp (let ((org-dirs '("~/org" "~/org-aweber"))) (seq-map #'org-babel-lob-ingest (seq-filter #'f-exists? (seq-map (lambda (path) (f-join path "library-of-babel.org")) org-dirs)))) #+end_src ** Nicer looking timestamps #+begin_src emacs-lisp (setq org-time-stamp-custom-formats '("<%A, %B %d %Y>" . "<%A, %B %d %Y %H:%M>")) (defun org-export-filter-timestamp-remove-brackets (timestamp backend info) "removes relevant brackets from a timestamp" (cond ((org-export-derived-backend-p backend 'latex) (replace-regexp-in-string "[<>]\\|[][]" "" timestamp)) ((org-export-derived-backend-p backend 'ascii) (replace-regexp-in-string "[<>]\\|[][]" "" timestamp)) ((org-export-derived-backend-p backend 'html) (replace-regexp-in-string "&[lg]t;\\|[][]" "" timestamp)))) (after! ox (add-to-list 'org-export-filter-timestamp-functions 'org-export-filter-timestamp-remove-brackets)) #+end_src ** Tufte HTML #+begin_src emacs-lisp (use-package! ox-tufte :after ox) #+end_src ** Journal #+begin_src emacs-lisp (use-package org-journal :if (f-dir? "~/org-aweber") :custom (org-journal-date-prefix "#+title: ") (org-journal-file-format "%Y-%m-%d.org") (org-journal-dir "~/org-aweber") (org-journal-date-format "%A, %d %B %Y")) #+end_src ** Ref #+begin_src emacs-lisp (use-package! org-ref :config (setq reftex-default-bibliography my/bibliographies) ;; see org-ref for use of these variables (setq org-ref-bibliography-notes "~/Documents/bibliography/notes.org" org-ref-default-bibliography my/bibliographies org-ref-pdf-directory "~/Documents/bibliography/bibtex-pdfs/")) #+end_src ** Roam *** Add backlinks to org-roam exports Adapted from https://org-roam.readthedocs.io/en/master/org_export/. #+begin_src emacs-lisp (defun my/org-roam--rewrite-backlink-content-links (path content) "Re-write the links in backlink CONTENT to be relative to PATH." (with-temp-buffer (insert content) (org-mode) (let ((ast (org-element-parse-buffer))) (org-element-map ast 'link (lambda (link) (when (string= (org-element-property :type link) "file") (org-element-put-property link :path (f-relative (org-element-property :path link) path))))) (org-no-properties (org-element-interpret-data ast))))) (defun my/org-roam--backlinks-list-with-content (file) "Generate a list of backlinks for FILE with content." (when (and (stringp file) (f-file? file)) (with-temp-buffer (cd (f-dirname file)) (hack-dir-local-variables-non-file-buffer) (if-let* ((backlinks (org-roam--get-backlinks file)) (grouped-backlinks (--group-by (nth 0 it) backlinks))) (progn (dolist (group grouped-backlinks) (let ((file-from (car group)) (bls (cdr group))) (insert (format "** [[file:%s][%s]]\n" (f-relative file-from (f-dirname file)) (org-roam--get-title-or-slug file-from))) (dolist (backlink bls) (pcase-let* ((`(,file-from _ ,props) backlink) (content (plist-get props :content))) (when content (let ((rewritten (my/org-roam--rewrite-backlink-content-links (f-dirname file) (plist-get props :content)))) (insert (s-trim (s-replace "\n" " " rewritten))))) (insert "\n\n"))))))) (buffer-string)))) (defun my/org-roam--reference-details () (let* ((citekey (cdr (org-roam--extract-ref))) (bibtex (when citekey (bibtex-completion-get-entry citekey)))) (when citekey (cond (bibtex (my/org-roam--reference-details-bibtex bibtex)) ((s-starts-with? "http" citekey t) (my/org-roam--reference-details-url citekey)) (t (my/org-roam--reference-details-default citekey)))))) (defun my/org-roam--reference-details-default (citekey) (my/org-roam--reference-details-list `(("Key" . ,(concat "=" citekey "="))))) (defun my/org-roam--reference-details-url (url) (my/org-roam--reference-details-list `(("Webpage" . ,(org-link-make-string url))))) (defun my/org-roam--reference-details-bibtex (entry) (concat (cdr (assoc "note" entry)) "\n\n" (my/org-roam--reference-details-list `(("Author" . ,(cdr (assoc "author" entry))) ("Calibre Library" . ,(org-link-make-string (format "https://calibre.phoenixinquis.is-a-geek.org/book/%s" (bibtex-completion-clean-string (cdr (assoc "calibreid" entry)))))))))) (defun my/org-roam--reference-details-list (details-alist) (org-list-to-org (cons 'descriptive (mapcar (lambda (pair) (let ((field (car pair)) (text (cdr pair))) (list (concat field " :: " text)))) details-alist)))) (defun my/org-export-preprocessor (backend) "Append org-roam backlinks with content when applicable before passing to the org export BACKEND." (let ((links (my/org-roam--backlinks-list-with-content (buffer-file-name))) (details (my/org-roam--reference-details))) (unless (or (not (stringp details)) (string= details "")) (save-excursion (goto-char (point-max)) (insert (concat "\n* Reference Details\n") details))) (unless (or (not (stringp links)) (string= links "")) (save-excursion (goto-char (point-max)) (insert (concat "\n* Backlinks\n") links))))) (add-hook 'org-export-before-processing-hook 'my/org-export-preprocessor) #+end_src *** Org Roam Bibtex #+begin_src emacs-lisp (use-package! org-roam-bibtex :after org-roam :hook (org-roam . org-roam-bibtex-mode) :bind (:map org-mode-map (("C-c n r a" . orb-note-actions)))) #+end_src *** Org Roam Server #+begin_src emacs-lisp (use-package! org-roam-server :commands org-roam-server-mode) #+end_src * Eshell ** Change directory in the context of a remote host #+begin_src emacs-lisp (defun eshell/lcd (&optional directory) (interactive) (if (file-remote-p default-directory) (with-parsed-tramp-file-name default-directory nil (eshell/cd (tramp-make-tramp-file-name (tramp-file-name-method v) (tramp-file-name-user v) (tramp-file-name-domain v) (tramp-file-name-host v) (tramp-file-name-port v) (or directory "") (tramp-file-name-hop v)))) (eshell/cd directory))) #+end_src * MU4E #+begin_src emacs-lisp (use-package! mu4e :bind (("" . mu4e)) :config (require 'f) (setq mu4e-maildir "~/Mail") (setq user-full-name "Correl Roush") (setq mu4e-contexts (list (make-mu4e-context :name "work" :vars `((user-mail-address . "correlr@aweber.com") (mu4e-drafts-folder . "/Work/[Gmail].Drafts") (mu4e-sent-folder . "/Work/[Gmail].Sent Mail") (mu4e-trash-folder . "/Work/[Gmail].Trash") (mu4e-maildir-shortcuts . (("/Work/INBOX" . ?i) ("/Work/[Gmail].All Mail" . ?a) ("/Work/[Gmail].Starred" . ?S) ("/Work/[Gmail].Sent Mail" . ?s) ("/Work/[Gmail].Trash" . ?t))) (mu4e-compose-signature . ,(with-temp-buffer (insert-file-contents "~/.signature-aweber") (buffer-string))))))) (when (f-exists? (f-join mu4e-maildir "Personal")) (add-to-list 'mu4e-contexts (make-mu4e-context :name "personal" :vars `((user-mail-address . "correl@gmail.com") (mu4e-drafts-folder . "/Personal/[Gmail].Drafts") (mu4e-sent-folder . "/Personal/[Gmail].Sent Mail") (mu4e-trash-folder . "/Personal/[Gmail].Trash") (mu4e-maildir-shortcuts . (("/Personal/INBOX" . ?i) ("/Personal/[Gmail].All Mail" . ?a) ("/Personal/[Gmail].Starred" . ?S) ("/Personal/[Gmail].Sent Mail" . ?s) ("/Personal/[Gmail].Trash" . ?t))) (mu4e-compose-signature . ,(with-temp-buffer (insert-file-contents "~/.signature") (buffer-string))))))) (setq mu4e-context-policy 'pick-first) (setq mu4e-compose-dont-reply-to-self t) (setq mu4e-user-mail-address-list '("correlr@aweber.com" "correl@gmail.com"))) #+end_src * Prodigy Manage background services #+begin_src emacs-lisp (use-package! prodigy :defer 2 :config (global-set-key (kbd "") 'prodigy) (prodigy-define-tag :name 'work) (prodigy-define-tag :name 'personal) ;; https://martinralbrecht.wordpress.com/2016/05/30/handling-email-with-emacs/ (when (executable-find "imapnotify") (prodigy-define-tag :name 'email :ready-message "Checking Email using IMAP IDLE. Ctrl-C to shutdown.") (prodigy-define-service :name "imapnotify-work" :command "imapnotify" :args (list "-c" (expand-file-name "~/.config/imap_inotify/work.js")) :tags '(email work autostart) :kill-signal 'sigkill) (unless (string-equal "croush" (user-login-name)) (prodigy-define-service :name "imapnotify-personal" :command "imapnotify" :args (list "-c" (expand-file-name "~/.config/imap_inotify/personal.js")) :tags '(email personal autostart) :kill-signal 'sigkill))) (when (f-exists? (expand-file-name "~/code/elm-dashboard")) (prodigy-define-service :name "elm-dashboard" :command "python" :args '("-m" "SimpleHTTPServer" "3000") :cwd (expand-file-name "~/code/elm-dashboard") :tags '(personal elm) :stop-signal 'sigkill :kill-process-buffer-on-stop t)) (when (f-exists? (expand-file-name "~/git/www")) (prodigy-define-service :name "AWeber WWW" :command "npm" :args '("start") :cwd (expand-file-name "~/git/www") :tags '(work))) (when (f-exists? (expand-file-name "~/Public/org")) (prodigy-define-service :name "Org Documents" :command "python" :args '("-m" "http.server" "3001") :cwd (expand-file-name "~/Public/org") :tags '(work autostart) :kill-signal 'sigkill)) (mapcar #'prodigy-start-service (-concat (prodigy-services-tagged-with 'autostart)))) #+end_src * Projectile #+begin_src emacs-lisp (after! projectile (require 'dash) (require 'f) (setq projectile-switch-project-action #'magit-status) (let ((project-directories (-filter #'f-directory? '("~/code" "~/git")))) (-map (lambda (directory) (-map (lambda (project) (-> (concat project "/") ;; Projectile likes trailing slashes (projectile-add-known-project))) (-filter (lambda (f) (and (not (s-ends-with? "." f)) (f-directory? f))) (-map (lambda (f) (concat directory "/" f)) (directory-files directory))))) project-directories)) (projectile-cleanup-known-projects)) #+end_src * Elfeed #+begin_src emacs-lisp (use-package! elfeed :commands (elfeed my/elfeed my/elfeed-emacs my/elfeed-blogs) :bind (("" . elfeed) ("C-c n n" . my/elfeed) ("C-c n a" . my/elfeed-all) ("C-c n e" . my/elfeed-emacs) ("C-c n b" . my/elfeed-blogs)) :init (global-set-key [f2] 'elfeed) :config (use-package! elfeed-org :config (progn (elfeed-org) (setq rmh-elfeed-org-files '("~/org/elfeed.org")))) (defun my/elfeed-with-filters (filters) (elfeed) (setq elfeed-search-filter (if (listp filters) (mapconcat #'identity filters " ") filters)) (elfeed-search-update :force)) (defun my/elfeed () (interactive) (my/elfeed-with-filters "@6-months-ago +unread")) (defun my/elfeed-all () (interactive) (my/elfeed-with-filters "@6-months-ago")) (defun my/elfeed-emacs () (interactive) (my/elfeed-with-filters "@6-months-ago +emacs +unread")) (defun my/elfeed-blogs () (interactive) (my/elfeed-with-filters "@6-months-ago +unread +blog"))) #+end_src * Paredit #+begin_src emacs-lisp (use-package! paredit :hook ((emacs-lisp-mode . enable-paredit-mode))) #+end_src * UUID Generation #+begin_src emacs-lisp (use-package! uuidgen :commands (uuidgen)) #+end_src * Eval and Replace Taken from [[http://emacsredux.com/blog/2013/06/21/eval-and-replace/][Emacs Redux]] #+begin_src emacs-lisp (defun eval-and-replace () "Replace the preceding sexp with its value." (interactive) (backward-kill-sexp) (condition-case nil (prin1 (eval (read (current-kill 0))) (current-buffer)) (error (message "Invalid expression") (insert (current-kill 0))))) (global-set-key (kbd "C-)") 'eval-and-replace) #+end_src * Unfill #+begin_src emacs-lisp (use-package! unfill :commands (unfill-paragraph unfill-region) :bind ("M-Q" . unfill-paragraph)) #+end_src * EMMS #+begin_src emacs-lisp (use-package! emms :commands (emms emms-play-file emms-play-directory emms-smart-browse) :config (let ((emms-player-base-format-list ;; Add some VGM formats to the list for VLC to play (append emms-player-base-format-list '("nsf" "spc" "gym")))) (require 'emms-player-vlc)) (require 'emms-setup) (emms-all) (setq emms-player-list '(emms-player-vlc)) ;; Use the installed VLC app if we're in OSX (if (f-exists? "/Applications/VLC.app/Contents/MacOS/VLC") (setq emms-player-vlc-command-name "/Applications/VLC.app/Contents/MacOS/VLC"))) (map! :leader (:prefix-map ("x" . "EMMS") :desc "Play file" "f" #'emms-play-file :desc "Play directory" "d" #'emms-play-directory :desc "Smart Browser" "b" #'emms-smart-browse)) #+end_src * Kubernetes #+begin_src emacs-lisp (use-package! kubernetes :commands (kubernetes-overview) :config) (set-popup-rule! "^\\*kubernetes" :ignore t) (use-package! kubernetes-tramp :config (setq tramp-remote-shell-executable "sh")) #+end_src * Twitter #+begin_src emacs-lisp (define-key! twittering-mode-map "f" #'twittering-favorite "F" #'twittering-unfavorite) #+end_src * Kerl #+begin_src emacs-lisp (use-package! kerl :commands (kerl-use)) #+end_src * Jira #+begin_src emacs-lisp (use-package jira-api :config (setq jira-api-host "jira.aweber.io" jira-api-user "correlr")) (defun my/org-clock-last-time-in-seconds () (save-excursion (let ((end (save-excursion (org-end-of-subtree)))) (when (re-search-forward (concat org-clock-string ".*\\(\\[[^]]+\\]\\)--\\(\\[[^]]+\\]\\)") end t) (let* ((start (match-string 1)) (end (match-string 2))) (floor (- (org-time-string-to-seconds end) (org-time-string-to-seconds start)))))))) (defun my/org-jira-add-worklog-latest () (interactive) (let ((jira-id (org-entry-get (point) "JIRA_ID")) (seconds (my/org-clock-last-time-in-seconds))) (when (and jira-id seconds) (jira-api-log-work jira-id seconds) (message (format "Logged %d minutes to %s on JIRA" (/ seconds 60) jira-id))))) (defun my/org-jira-add-worklog-total () (interactive) (let ((jira-id (org-entry-get (point) "JIRA_ID")) (seconds (* 60 (org-clock-sum-current-item)))) (when (and jira-id seconds) (jira-api-log-work jira-id seconds) (message (format "Logged %d minutes to %s on JIRA" (/ seconds 60) jira-id))))) (defun my/org-clock-add-jira-worklog-last () "Add a work log entry to a JIRA. To log work to JIRA, set a property named JIRA_ID on the entry to be logged to a JIRA issue ID." (interactive) (save-excursion (save-window-excursion (org-clock-goto) (my/org-jira-add-worklog-latest)))) (defun my/org-jira-browse () (interactive) (-if-let (jira-id (org-entry-get (point) "JIRA_ID")) (let ((protocol (if jira-api-use-ssl "https" "http"))) (browse-url (concat protocol "://" jira-api-host "/browse/" jira-id))))) (defun my/org-jira-list () (interactive) (let ((buffer (generate-new-buffer "*org-jira*"))) (switch-to-buffer buffer) (org-mode) (insert "ohai") (setq-local buffer-read-only t) (display-buffer buffer))) ;; (add-hook 'org-clock-out-hook 'my/org-clock-add-jira-worklog-last) (map! :map org-mode-map "C-c j t" #'my/org-jira-add-worklog-total "C-c j l" #'my/org-jira-add-worklog-latest "C-c j b" #'my/org-jira-browse "C-c j c" #'jira-api-create-issue-from-heading "C-c j u" #'jira-api-update-issue-from-heading) #+end_src * Kanji Mode Minor mode for displaying Japanese characters' stroke orders. #+begin_src emacs-lisp (use-package! kanji-mode :commands kanji-mode) #+end_src * Kanji Glasses Mode Study kanji by overlaying hiragana readings. #+begin_src emacs-lisp (use-package! kanji-glasses-mode :commands kanji-glasses-mode) #+end_src * Nov Epub reader #+begin_src emacs-lisp (use-package! nov :mode ("\\.epub\\'" . nov-mode) :config (setq nov-save-place-file (concat doom-cache-dir "nov-places"))) #+end_src * Editing binary-compressed plist files in OSX From https://www.emacswiki.org/emacs/MacOSXPlist#toc1 #+begin_src emacs-lisp ;; Allow editing of binary .plist files. (add-to-list 'jka-compr-compression-info-list ["\\.plist$" "converting text XML to binary plist" "plutil" ("-convert" "binary1" "-o" "-" "-") "converting binary plist to text XML" "plutil" ("-convert" "xml1" "-o" "-" "-") nil nil "bplist"]) ;;It is necessary to perform an update! (jka-compr-update) #+end_src * Miscellaneous Nonsense ** BRING ON THE ... A silly interactive method for generating horizontal and vertical text. #+CAPTION: M-x bring-on-the RET cats RET #+begin_example B R I N G O N T H E C A T S R I N G O N T H E C A T S #+end_example #+begin_src emacs-lisp (defun bring-on-the (thing) (interactive "sBring on the: ") (let ((upthing (seq-into (s-upcase (s-concat "bring on the " thing)) 'list))) (insert (s-concat (seq-into (-interleave upthing (-repeat (length upthing) 32)) 'string) "\n" (seq-into (-interleave (rest upthing) (-repeat (1- (length upthing)) ?\n)) 'string))))) #+end_src ** OwO Mode Make reading an open buffer an exercise in insanity. #+begin_src emacs-lisp (use-package! owo-mode :commands owo-mode) #+end_src