64 KiB
DOOM Emacs Configuration
- Look And Feel
- UX Improvements
- Writing
- BibTeX
- Org
- Override DOOM indentation behavior
- Disable DOOM's centralized attachment system
- Agenda
- LaTeX Export
- Capture templates
- Custom ID generation
- Publish projects
- Enhanced Confluence export
- Reload images on source execution
- Sticky headers
- Library of Babel
- Nicer looking timestamps
- Tufte HTML
- Journal
- Cookbook
- Ref
- Roam
- Sidebar
- Transclusion
- Ditaa
- DND
- ReStructuredText
- Unfill
- Reading
- Coding
- Applications
- Operating Systems
- Miscellaneous Nonsense
Look And Feel
Theme
It's Catppuccin time!
(use-package! catppuccin-theme
:init (setq catppuccin-flavor 'macchiato)
:config (setq doom-theme 'catppuccin))
Default font
(setq doom-font (font-spec :family "Inconsolata" :size 16))
Dashboard
Customize the Doom Emacs dashboard with commissioned artwork, chosen randomly.
The dashboard can be reloaded with the g
key.
(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)
Frame transparency
Set the background transparency to 90% so my wallpaper shows through.
(set-frame-parameter (selected-frame) 'alpha '(98 . 98))
(add-to-list 'default-frame-alist '(alpha . (98 . 98)))
Comic Sans Mono
Why not, it's fun 🤣
Adds a command to switch to the monospaced Comic Sans font, and make it the default font for PHP buffers.
https://dtinth.github.io/comic-mono-font/
(defun my/buffer-face-comic-mono ()
(interactive)
(setq buffer-face-mode-face '(:family "Comic Mono"))
(buffer-face-mode))
(after! php-mode
(add-hook 'php-mode-hook #'my/buffer-face-comic-mono))
Emoji Support
Use the Noto Emoji font.
(setf use-default-font-for-symbols nil)
(set-fontset-font t 'unicode "Noto Emoji" nil 'append)
Mixed Pitch
Facilitates mixing monospace and proportional fonts. I'm using an MIT-licensed version of the Edward Tufte book font because it's gorgeous.
(use-package! mixed-pitch
:hook ((org-mode markdown-mode rst-mode) . mixed-pitch-mode))
(setq doom-variable-pitch-font (font-spec :family "ETBookOT"))
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.
(if (display-graphic-p)
(global-unset-key (kbd "C-z")))
Custom settings
Store options set via customize-*
in a separate file (Emacs stores
them in init.el
by default).
(setq custom-file "~/.emacs.d/custom.el")
(if (file-exists-p custom-file)
(load custom-file))
(setf custom-safe-themes t)
Eval and Replace
Replace an s-expression with its value. Taken from Emacs Redux.
(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)
Prompt for unsafe local variables
Doom sets this to :safe
, logging unsafe variables for later addressing. I'd
rather continue to be prompted.
(setq enable-local-variables t)
Writing
BibTeX
Tell Emacs where to find my bibliography files. I keep most everything in my Calibre library, which I regularly export and keep synced via Nextcloud.
(setq my/bibliographies
'("~/Documents/bibliography/references.bib"
"~/Documents/bibliography/calibre.bib"))
Helm BibTeX
(setq bibtex-completion-bibliography my/bibliographies
bibtex-completion-pdf-field "File"
bibtex-completion-notes-path "~/org/roam")
Citar
(setq citar-bibliography my/bibliographies
citar-notes-paths '("~/org/roam"))
Org
Configure a variety of options and tools for Org Mode, the markup I use for everything from simple notes to task management.
(after! org
<<org>>)
Override DOOM indentation behavior
(defun my/org-init-babel ()
(setq org-src-preserve-indentation nil))
(add-hook! 'org-mode-hook #'my/org-init-babel)
Disable DOOM's centralized attachment system
It's incompatible with all of the org files I already have using the standard setup.
(setq org-attach-directory "data/")
(remove-hook! 'org-load-hook
#'(+org-init-centralized-attachments-h))
Agenda
Set up my agenda view. I use separate files for my personal TODOs and my work TODOs, synced externally using Nextcloud.
(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) "s1326.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-overriding-header "Unscheduled")
(org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled 'deadline)))))
((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")))
LaTeX Export
Document Classes
Tell Emacs about all of the LaTeX classes I use to export documents.
(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}"))
("thermal-paper"
"\\documentclass{paper}
\\usepackage[paperwidth=52mm]{geometry}"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}")))))
DnD
This adds an additional LaTeX export option that outputs documents resembling a Dungeons and Dragons manual.
(use-package! ox-dnd
:after ox)
Capture templates
Set up my capture templates for making new notes and journal entries.
(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%?")
("c" "Cookbook Recipe" entry
(file "~/org/cookbook/index.org")
"%(org-chef-get-recipe-from-url)"
:empty-lines 1)
;; 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)))
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.
(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))))))
Publish projects
Tell Emacs how to build the document collections I export to HTML.
(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
;; "<link rel=\"stylesheet\" href=\"" my/org-base-url "styles/tufte-css/tufte.css\"/>"
;; "<link rel=\"stylesheet\" href=\"" my/org-base-url "styles/tufte-css/latex.css\"/>"
;; Org-Spec
;; "<link href=\"https://fonts.googleapis.com/css?family=Roboto+Slab:400,700|Inconsolata:400,700\" rel=\"stylesheet\" type=\"text/css\" />"
;; "<link rel=\"stylesheet\" href=\"" my/org-base-url "styles/org-spec/style.css\"/>"
;; "<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "css/info.css\" />"
;; ReadTheOrg
"<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "styles/readtheorg/css/htmlize.css\"/>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "styles/readtheorg/css/readtheorg.css\"/>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "css/info.css\" />"
"<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js\"></script>"
"<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js\"></script>"
"<script type=\"text/javascript\" src=\"" my/org-base-url "styles/lib/js/jquery.stickytableheaders.min.js\"></script>"
"<script type=\"text/javascript\" src=\"" my/org-base-url "styles/readtheorg/js/readtheorg.js\"></script>"
;; Bigblow
;; "<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "styles/bigblow/css/htmlize.css\"/>"
;; "<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "styles/bigblow/css/bigblow.css\"/>"
;; "<link rel=\"stylesheet\" type=\"text/css\" href=\"" my/org-base-url "styles/bigblow/css/hideshow.css\"/>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/jquery-1.11.0.min.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/jquery-ui-1.10.2.min.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/jquery.localscroll-min.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/jquery.scrollTo-1.4.3.1-min.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/jquery.zclip.min.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/bigblow.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/bigblow/js/hideshow.js\"></script>"
;; "<script type=\"text/javascript\" src=\"" my/org-base-url "styles/lib/js/jquery.stickytableheaders.min.js\"></script>"
)))
(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-roam-html"
:base-directory "~/roam/aweber"
:base-extension "org"
:publishing-directory "~/Public/roam-aweber"
:recursive t
:with-toc nil
:section-numbers nil
:auto-sitemap nil
:sitemap-title "Correl Roush's Org Roam Notes"
:publishing-function org-html-publish-to-html
:html-head "<link rel=\"stylesheet\" href=\"https://gongzhitaao.org/orgcss/org.css\"/>")
("work-roam-assets"
:base-directory "~/roam/aweber"
:base-extension "css\\|gif\\|jpe?g\\|png\\|svg\\|pdf\\|ogv\\|py\\|html\\|js\\|json\\|ya?ml"
:include (".gitlab-ci.yml")
:publishing-directory "~/Public/roam-aweber"
:publishing-function org-publish-attachment
:display-custom-times t
:recursive t)
("work-roam" :components ("work-roam-html" "work-roam-assets" "work-themes"))
("cookbook-html"
:base-directory "~/org/cookbook"
:base-extension "org"
:publishing-directory "~/Public/cookbook"
:recursive t
:with-toc nil
:section-numbers nil
:auto-sitemap nil
:publishing-function org-html-publish-to-html
:html-head "<link rel=\"stylesheet\" href=\"https://gongzhitaao.org/orgcss/org.css\"/>")
("cookbook-assets"
:base-directory "~/org/cookbook"
:base-extension "css\\|js\\|json\\|gif\\|jpe?g\\|png\\|svg\\|pdf"
:publishing-directory "~/Public/cookbook"
:publishing-function org-publish-attachment
:recursive t)
("cookbook" :components ("cookbook-html" "cookbook-assets"))
("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 "<link rel=\"stylesheet\" type=\"text/css\" href=\"http://thomasf.github.io/solarized-css/solarized-dark.min.css\" />"
:html-head-extra "<link rel=\"stylesheet\" type=\"text/css\" href=\"/~croush/org/css/org.css\" />"
: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
"<link rel=\"stylesheet\" href=\"" my/org-base-url "styles/tufte-css/tufte.css\"/>"
"<link rel=\"stylesheet\" href=\"" my/org-base-url "styles/tufte-css/latex.css\"/>"))
;; Org-Spec
;; "<link href=\"http://fonts.googleapis.com/css?family=Roboto+Slab:400,700|Inconsolata:400,700\" rel=\"stylesheet\" type=\"text/css\" />"
;; "<link href=\"http://demo.thi.ng/org-spec/css/style.css\" rel=\"stylesheet\" type=\"text/css\" />"
("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 "~/roam/personal"
: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 "<link rel=\"stylesheet\" href=\"https://gongzhitaao.org/orgcss/org.css\"/>")
("roam-assets"
:base-directory "~/roam/personal"
:base-extension "css\\|js\\|json\\|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 "<link rel=\"stylesheet\" type=\"text/css\" href=\"http://thomasf.github.io/solarized-css/solarized-light.min.css\" />"
: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)
Enhanced Confluence export
Adds my own package that extends the built-in Confluence wiki markup exporter with better formatting and macro support.
(use-package! ox-confluence-en
:after ox
:commands ox-confluence-en-export-as-confluence)
Reload images on source execution
Force images to redisplay after executing a source code block, so I can immediately see the result of regenerating graphs and diagrams.
(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)
Sticky headers
Keeps the current heading visible at the top of the Emacs window.
(use-package! org-sticky-header
:hook (org-mode . org-sticky-header-mode)
:config (setq org-sticky-header-full-path 'full))
Library of Babel
Load shared code snippets to be used in org documents.
(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))))
Nicer looking timestamps
(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))
Tufte HTML
Gorgeous HTML exports.
;; (use-package! ox-tufte
;; :after ox)
Journal
(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"))
Cookbook
(use-package! org-chef
:commands (org-chef-get-recipe-from-url))
Ref
Tools for linking and taking notes on books and papers.
(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/"))
Roam
Powerful cross-linked note-taking.
Capture templates
(setq org-roam-capture-templates
'(("d" "default" plain "%?" :target
(file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}
")
:unnarrowed t)))
Add backlinks to org-roam exports
(use-package! org-roam-export-backlinks
:commands org-roam-export-backlinks-preprocessor
:init
(add-to-list 'org-export-before-processing-hook
#'org-roam-export-backlinks-preprocessor))
Org Roam Bibtex
Make it easy to take notes on books and papers that I'm reading.
(use-package! org-roam-bibtex
:after org-roam
:hook (org-roam . org-roam-bibtex-mode)
:bind (:map org-mode-map
(("C-c n r b" . orb-note-actions))))
Org Roam UI
Provides a fun way to browse through a collection of notes.
(use-package! org-roam-ui
:after org-roam
:commands org-roam-ui-mode
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t))
Use writeroom in org-roam buffers
Makes for a much nicer note-taking experience.
(defun my/org-roam-writeroom ()
;; Use a buffer-local local variables hook to ensure the org-roam-directory is
;; set properly
(add-hook 'hack-local-variables-hook
(lambda ()
(when (and org-roam-directory
(f-ancestor-of?
(expand-file-name org-roam-directory)
(or (buffer-file-name) default-directory)))
(writeroom-mode t)))
nil t))
(add-hook! 'org-mode-hook #'my/org-roam-writeroom)
Provide seamless switching between org-roam slipboxes
I keep multiple slipboxes under a common directory, some of which may or may not be available on different machines (e.g. a work slipbox vs a personal one). This gives me an interface for easily switching between them and resyncing their databases. The databases are kept out of the slipbox directories themselves to keep them tidy.
(defvar my/org-roam-slipbox-directory (expand-file-name "~/roam"))
(defun my/slipbox-directories ()
(f-directories my/org-roam-slipbox-directory))
(defun my/slipbox-names ()
(-map #'f-filename (my/slipbox-directories)))
(defun my/slipbox--directory (slipbox)
(f-join my/org-roam-slipbox-directory slipbox))
(defun my/slipbox--database (slipbox)
(f-join my/org-roam-slipbox-directory (s-concat slipbox ".db")))
(defmacro my/with-slipbox (slipbox &rest body)
`(let ((org-roam-directory (my/slipbox--directory ,slipbox))
(org-roam-db-location (my/slipbox--database ,slipbox)))
,@body))
(defun my/org-roam-slipbox-node-find ()
(interactive)
(let ((slipbox (completing-read "Slipbox: " (my/slipbox-names))))
(setq org-roam-directory (my/slipbox--directory slipbox)
org-roam-db-location (my/slipbox--database slipbox))
(unless (f-exists? org-roam-db-location)
(org-roam-db-sync))
(org-roam-node-find)))
(defun my/org-roam-slipbox-db-sync ()
(interactive)
(--each (my/slipbox-names)
(my/with-slipbox it (org-roam-db-sync))))
(defun my/slipbox-select (slipbox)
(interactive
(list (completing-read "Slipbox: " (my/slipbox-names))))
(unless (member slipbox (my/slipbox-names))
(error "Unknown slipbox %s" slipbox))
(setq org-roam-directory (my/slipbox--directory slipbox)
org-roam-db-location (my/slipbox--database slipbox))
(unless (f-exists? org-roam-db-location)
(org-roam-db-sync))
slipbox)
(map! :leader
(:prefix-map ("n" . "notes")
(:prefix ("r" . "roam")
:desc "Select slipbox" "c" #'my/slipbox-select)))
(map! :leader
(:prefix-map ("n" . "notes")
(:prefix ("r" . "roam")
:desc "Find file in slipbox" "F" #'my/org-roam-slipbox-node-find)))
(map! :leader
(:prefix-map ("n" . "notes")
(:prefix ("r" . "roam")
:desc "Synchronize all slipbox databases" "S" #'my/org-roam-slipbox-db-sync)))
;; Set a default org-roam directory from the available slipboxes
(if-let (slipbox (-first-item (my/slipbox-names)))
(my/slipbox-select slipbox))
Sidebar
Display a sidebar with file-local todos and scheduling.
(use-package! org-sidebar
:bind (:map org-mode-map
(("C-c l v s" . org-sidebar-toggle)
("C-c l v S" . org-sidebar-tree-toggle)))
:commands (org-sidebar
org-sidebar-toggle
org-sidebar-tree
org-sidebar-tree-toggle))
Transclusion
Show linked org document sections inline.
(use-package! org-transclusion
:after org
:init
(map!
:map global-map "<f12>" #'org-transclusion-add
:leader
:prefix "n"
:desc "Org Transclusion Mode" "t" #'org-transclusion-mode))
Ditaa
Download and use a recent version of ditaa for rendering ASCII diagrams.
(after! ob-ditaa
(let ((jar-url "https://github.com/stathissideris/ditaa/releases/download/v0.11.0/ditaa-0.11.0-standalone.jar")
(jar-path (concat doom-etc-dir "ditaa.jar")))
(unless (f-exists? jar-path)
(url-copy-file jar-url jar-path))
(setq org-ditaa-jar-path jar-path
org-ditaa-eps-jar-path jar-path)))
DND
(use-package! org-d20
:after org)
ReStructuredText
(use-package! polymode
:defer t)
(use-package! poly-rst
:mode ("\\.rst\\'" . poly-rst-mode))
Unfill
Does the opposite of fill (M-q)
, removing line breaks from a paragraph or
region.
(use-package! unfill
:commands (unfill-paragraph
unfill-region)
:bind ("M-Q" . unfill-paragraph))
Reading
Epub reader
A major mode for reading and navigating .epub
files.
(use-package! nov
:mode ("\\.epub\\'" . nov-mode)
:config
(setq nov-save-place-file (concat doom-cache-dir "nov-places")))
Kanji Mode
Minor mode for displaying Japanese characters' stroke orders.
(use-package! kanji-mode
:commands kanji-mode)
Kanji Glasses Mode
Study kanji by overlaying hiragana readings.
(use-package! kanji-glasses-mode
:commands kanji-glasses-mode)
Coding
Arduino
(use-package! arduino-mode
:mode "\\.ino\\'")
(use-package! arduino-cli-mode
:hook arduino-mode
:custom
(arduino-cli-warnings 'all)
(arduino-cli-verify t))
Erlang
Kerl
Select the active erlang installation managed with kerl.
(use-package! kerl
:commands (kerl-use))
Lisp
Paredit
Adds shortcuts to edit the structure of lisp code.
(use-package! paredit
:hook ((emacs-lisp-mode . enable-paredit-mode)))
OpenSCAD
Mode for editing OpenSCAD 3D modeling files. Files can be opened externally for
live-updated previews within OpenSCAD itself using C-c C-o
.
(use-package! scad-mode
:mode "\\.scad\\'")
Applications
Configure MU4E to read email synced from my personal and work accounts.
(use-package! mu4e
:bind (("<f9>" . mu4e))
:config
(require 'f)
(setq mu4e-maildir "~/Mail")
(setq user-full-name "Correl Roush")
(setq mu4e-contexts nil)
(when (f-exists?
(f-join mu4e-maildir "Work"))
(add-to-list
'mu4e-contexts
(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
(when (f-exists? "~/.signature-aweber")
(insert-file-contents "~/.signature-aweber"))
(buffer-string)))
(smtpmail-smtp-user . "correlr@aweber.com")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 465)
(smtpmail-stream-type . ssl)))))
(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
(when (f-exists? "~/.signature")
(insert-file-contents "~/.signature"))
(buffer-string)))
(smtpmail-smtp-user . "correl@gmail.com")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 465)
(smtpmail-stream-type . ssl)))))
(setq mu4e-context-policy 'pick-first)
(setq mu4e-compose-context-policy 'ask)
(setq mu4e-compose-dont-reply-to-self t)
(setq mu4e-index-lazy-check nil)
(setq mu4e-headers-include-related nil)
(setq mu4e-headers-skip-duplicates t)
(setq mu4e-user-mail-address-list '("correlr@aweber.com"
"correl@gmail.com")))
Prefer sending HTML-formatted messages with plain text as a fallback option (alternative formats should be specified in increasing level of preference per RFC-1341).
(use-package! org-msg
:after mu4e
:config
(setq org-msg-default-alternatives '(text html)
org-msg-options "html-postamble:nil toc:nil author:nil email:nil ^:nil"))
Chat
Circe
(after! circe
(set-irc-server!
"liberachat"
`(:tls nil
:host "znc.phoenixinquis.is-a-geek.org"
:port 8667
:nick "correl"
:user "correl/liberachat"
:pass (lambda (&rest _) (+pass-get-secret "Social/znc.phoenixinquis.is-a-geek.org/correl"))))
(set-irc-server!
"twitch"
`(:tls nil
:host "znc.phoenixinquis.is-a-geek.org"
:port 8667
:nick "correl"
:user "correl/twitch"
:pass (lambda (&rest _) (+pass-get-secret "Social/znc.phoenixinquis.is-a-geek.org/correl")))))
Matrix
(use-package! ement
:commands 'ement-connect
:init
(defvar +matrix-workspace-name "*Matrix*")
(defvar +matrix--old-wconf nil)
(defun +matrix ()
(interactive)
(if (modulep! :ui workspaces)
(+workspace/new +matrix-workspace-name)
(setq +matrix--old-wconf (current-window-configuration))
(delete-other-windows)
(switch-to-buffer (doom-fallback-buffer)))
(call-interactively #'ement-connect))
(defun +matrix/quit ()
(interactive)
(ement-disconnect (mapcar #'cdr ement-sessions))
(when (modulep! :ui workspaces)
(+workspace/delete +matrix-workspace-name))
(when +matrix--old-wconf
(set-window-configuration +matrix--old-wconf)
(setq +matrix--old-wconf nil)))
:config
(when (modulep! :ui popup)
(set-popup-rule! "\\*Ement " :ignore t))
:custom
(ement-room-prism 'both)
(ement-save-sessions t) ;; Beware, this stores your token to disk in plain text!
)
Music
Configure EMMS for playing music files on my computer.
(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))
News Aggregation
Read blogs and articles from the RSS feeds I follow.
(use-package! elfeed
:commands (elfeed my/elfeed my/elfeed-emacs my/elfeed-blogs)
:bind
(("<f2>" . 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"))
(add-hook! 'elfeed-show-mode-hook #'mixed-pitch-mode))
Kubernetes
Manage a Kubernetes cluster and set up remote shell/file access via TRAMP.
(use-package! kubernetes
:commands (kubernetes-overview)
:config)
(set-popup-rule! "^\\*kubernetes" :ignore t)
(use-package! kubernetes-tramp
:commands (eshell find-file)
:config
(setq tramp-remote-shell-executable "sh"))
Project Management
Projectile
Pre-load Projectile with projects in my usual code directories.
(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))
Jira
Add some commands for interacting with Jira within org documents.
(after! org
(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))
Source Control
(after! forge
(add-to-list
'forge-alist
'("gitlab.aweber.io" "gitlab.aweber.io/api/v4" "gitlab.aweber.io" forge-gitlab-repository)))
Eshell
Change directory in the context of a remote host
Add an lcd
command that functions similarly to cd
, but is scoped to the
remote host being accessed. Basically means I can use lcd /
and other absolute
paths and not worry about being bounced back to my local filesystem.
(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)))
Disable company autocompletion on remote hosts
(defun my/toggle-shell-autocomplete ()
(when (fboundp 'company-mode)
(company-mode (if (file-remote-p default-directory) -1 1))))
(add-hook! 'eshell-directory-change-hook #'my/toggle-shell-autocomplete)
Background Processes
Manage background services
(use-package! prodigy
:defer 2
:config
(global-set-key (kbd "<f7>") '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))
(when (f-exists? (expand-file-name "~/Public/roam"))
(prodigy-define-service
:name "Org Roam Documents"
:command "python"
:args '("-m" "http.server" "3002")
:cwd (expand-file-name "~/Public/roam")
:tags '(personal autostart)
:kill-signal 'sigkill))
(when (f-exists? (expand-file-name "~/code/correl.github.io"))
(prodigy-define-service
:name "Blog"
:command "hugo"
:args '("serve" "-D")
:cwd (expand-file-name "~/code/correl.github.io")
:rags '(personal)
:kill-signal 'sigkill))
(mapcar
#'prodigy-start-service
(-concat (prodigy-services-tagged-with 'autostart))))
Screen Sharing
Showing keypresses
(use-package! keypression
:commands (keypression-mode)
:bind (("C-c t k" . keypression-mode))
:config
(setq keypression-fade-out-delay 2.0
keypression-cast-command-name t
keypression-combine-same-keystrokes t
keypression-combine-format "%s (%d times)"
keypression-y-offset 100
keypression-font-face-attribute '(:height 400 :weight bold)))
Swagger API documentation
Make calls to swagger-documented APIs with completion support.
(use-package! swagg
:commands (swagg-request swagg-request-with-rest-block)
:config (setq swagg-rest-block-prelude "#+BEGIN_SRC http
"
swagg-rest-block-postlude "#+END_SRC"))
UUID Generation
(use-package! uuidgen
:commands (uuidgen))
Wordle
(use-package! wordle
:commands (wordel wordel-marathon))
Operating Systems
Linux
EXWM
Set Emacs + EXWM as the default X window manager
[Desktop]
session=~/.doom.d/start-exwm.sh
emacs -mm -l ~/.doom.d/exwm.el
Configure EXWM
- Sets the desktop background
- Starts a bar/system tray and various applets
- Sets up workspaces
- Names X window buffers based on which application is running
(defun my/exwm-update-class ()
(exwm-workspace-rename-buffer exwm-class-name))
(defun my/run-in-background (command)
(let ((command-parts (split-string command "[ ]+")))
(apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts)))))
(defun my/set-desktop-background ()
(interactive)
(start-process-shell-command "feh" nil "feh --bg-scale ~/Pictures/Wallpapers/1520742811045.png"))
(defun my/exwm-init-hook ()
;; Start tint2 bar
(my/run-in-background "tint2")
;; Start system tray applets
(my/run-in-background "nm-applet")
(my/run-in-background "pasystray")
(my/run-in-background "blueman-applet")
(my/run-in-background "nextcloud --background")
(my/run-in-background "compton"))
(use-package! exwm
:config
(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 1)))))
(number-sequence 1 9))
([?\s-&] . (lambda (command)
(interactive (list (read-shell-command "$ ")))
(start-process-shell-command command nil command)))))
(setq exwm-workspace-number 4)
(exwm-input-set-key (kbd "s-SPC") #'counsel-linux-app)
(add-hook! 'exwm-update-class-hook #'my/exwm-update-class)
(add-hook! 'exwm-init-hook #'my/exwm-init-hook)
(my/set-desktop-background)
(exwm-enable))
(use-package! exwm-config
:after exwm)
(use-package! desktop-environment
:after exwm
:config
(desktop-environment-mode))
Application launcher
Use counsel as an application launcher. Scans for .desktop
files in all the
usual places.
(use-package! counsel
:custom (counsel-linux-app-format-function #'counsel-linux-app-format-function-name-only)
:config (counsel-mode 1))
OSX
Editing binary-compressed plist files
From https://www.emacswiki.org/emacs/MacOSXPlist#toc1
;; 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)
Miscellaneous Nonsense
BRING ON THE …
A silly interactive method for generating horizontal and vertical text.
(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)))))
OwO Mode
Make reading an open buffer an exercise in insanity.
(use-package! owo-mode
:commands owo-mode)
Elcord
Emits rich presence to Discord.
(use-package! elcord
:config
(setq elcord-display-buffer-details nil
elcord-quiet t
elcord-editor-icon "emacs_material_icon"
elcord-use-major-mode-as-main-icon t)
(elcord-mode t))
MTG Mode
(use-package! mtg
:commands (mtg-mode))