#+TITLE: Doom-emacs config #+AUTHOR: Judah Sotomayor #+STARTUP: overview Welcome to my configuration! I hope you enjoy your time here :) It's not all very well documented, but I have done my best to split everything into a logical order. Perhaps in 10 years I will still be using it, but who knows. * Task List ** TODO Reorganize into use-cases rather than packages * Setting Basics #+BEGIN_SRC emacs-lisp (setq tab-always-indent t) (setq org-roam-directory "~/org/") (setq user-full-name "Judah Sotomayor" user-mail-address "") #+END_SRC * Theme and Font Set the theme. /Use something dark/ +I also prefer relative line numbers because of *evil* mode+ Relative line numbers don't work, because of foling in Org-mode. #+BEGIN_SRC emacs-lisp (setq doom-theme 'doom-dark+) (setq doom-font (font-spec :family "Hack Nerd Font Mono" :size 16 :weight 'medium)) (setq auto-save-default nil) ;I don't like autosaving. Let me do it myself. #+END_SRC ** Doom splash screen I really don't like the widgets, and I think it takes longer to load so I get rid of them #+BEGIN_SRC emacs-lisp (remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-shortmenu) (remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-footer) #+END_SRC Set a sweet splash image #+BEGIN_SRC emacs-lisp (setq fancy-splash-image (concat doom-private-dir "emacs.png")) #+END_SRC * Evil configuration I want a non-ESC way to get back to normal mode: #+BEGIN_SRC emacs-lisp (map! :desc "Switch to normal mode" :i "C-c" #'evil-normal-state) #+END_SRC * Terminal setup I like to just use bash: #+BEGIN_SRC emacs-lisp (defun bash () (interactive) (term "/bin/bash")) (map! :desc "Start a bash shell" "" #'bash) #+END_SRC I don't like the modeline in the terminal. #+BEGIN_SRC emacs-lisp (add-hook 'term-mode-hook 'hide-mode-line-mode) #+END_SRC * Sage-math #+BEGIN_SRC emacs-lisp (use-package! sage-shell-mode :defer) ;; Ob-sagemath supports only evaluating with a session. (setq org-babel-default-header-args:sage '((:session . t) (:results . "output"))) ;; C-c c for asynchronous evaluating (only for SageMath code blocks). (with-eval-after-load "org" (define-key org-mode-map (kbd "C-c c") 'ob-sagemath-execute-async)) #+END_SRC I like to have a shortcut for calc as well, for simpler calculations #+BEGIN_SRC emacs-lisp (map! :leader :desc "The Emacs Calculator" "C" #'calc) #+END_SRC * Beancount ** FavaGTK :outdated: I want to launch favagtk more easily. Let's do that here: #+BEGIN_SRC emacs-lisp (defun fava () (interactive) (async-shell-command "flatpak run org.gnome.gitlab.johannesjh.favagtk")) (map! :leader :desc "Start favagtk" "r b" #'fava) #+END_SRC * Graphing Applications ** Mermaid.js Mermaid.js requires the mmcli executable from npm. Mermaid is a tool that allows you to graph many kinds of entities: - Relationship diagrams - Flowcharts - Gantt diagrams There's other kinds too. The package on ELPA is perfect. Needs no config, fits right in with Babel. #+BEGIN_SRC emacs-lisp (use-package! ob-mermaid) #+END_SRC ** SVGBob SVGBob is an ASCII art renderer. It can do all kinds of nifty things with just a few basic .------./\ #+BEGIN_SRC emacs-lisp (use-package! ob-svgbob) (setq org-svgbob-executable "svgbob_cli") (setq org-babel-svgbob--parameters '(:background transparent )) #+END_SRC * Fun Stuff * Useful Functions ** Get the date from the shell I want to grab the current date and put it in the buffer. Because I sometimes use Fish or another shell, it's good to make sure bash is running the command using the src_bash{`-c`} flag. #+BEGIN_SRC emacs-lisp (defun current-date () (interactive) (shell-command-to-string " bash -c 'echo -n $(date +%Y-%m-%d)'")) (defun insert-current-date () (interactive) (insert (current-date))) (map! :leader :desc "Insert the current date into the buffer" "i d" #'insert-current-date) #+END_SRC * Creating Diagrams and graphs ** TODO set default values using org-babel's features for that. * Securing Sensitive Files ** Age.el #+BEGIN_SRC emacs-lisp (use-package! age :ensure t :demand t :custom (age-program "age") (age-default-identity "~/.age/personal") (age-default-recipient "~/.age/personal.pub") :config (age-file-enable)) #+END_SRC ** Org-mode security settings These mostly concern limiting evaluation of code-blocks without confirmation. The final two lines concern local variables, which may try to evaluate code. Emacs has better default values now, so it will ask to evaluate local variables. #+BEGIN_SRC emacs-lisp (setq org-confirm-babel-evaluate t) (setq org-link-shell-confirm-function t) (setq org-link-elisp-confirm-function t) (setq enable-local-variables t) (setq enable-local-eval 'maybe) #+END_SRC * Org-mode for a great todo list ** Making the Agenda features more efficient #+BEGIN_SRC emacs-lisp :tangle no (dolist (file (org-roam-list-files)) (message "processing %s" file) (with-current-buffer (or (find-buffer-visiting file) (find-file-noselect file)) (vulpea-project-update-tag) (save-buffer))) #+END_SRC #+RESULTS: Only include files with the hastodos tag. This section is contributed from [[https://gist.github.com/d12frosted/a60e8ccb9aceba031af243dff0d19b2e][d12frosted]] and use his vulpea.el package's functions. I ripped out the essentials because I don't want all the other stuff in that package. #+BEGIN_SRC emacs-lisp (after! org-roam (defun vulpea-project-p () "Return non-nil if current buffer has any todo entry. TODO entries marked as done are ignored, meaning the this function returns nil if current buffer contains only completed tasks." (seq-find ; (3) (lambda (type) (eq type 'todo)) (org-element-map ; (2) (org-element-parse-buffer 'headline) ; (1) 'headline (lambda (h) (org-element-property :todo-type h))))) (defun vulpea-project-update-tag () "Update PROJECT tag in the current buffer." (when (and (not (active-minibuffer-window)) (vulpea-buffer-p)) (save-excursion (goto-char (point-min)) (let* ((tags (vulpea-buffer-tags-get)) (original-tags tags)) (if (vulpea-project-p) (setq tags (cons "hastodos" tags)) (setq tags (remove "hastodos" tags))) ;; cleanup duplicates (setq tags (seq-uniq tags)) ;; update tags if changed (when (or (seq-difference tags original-tags) (seq-difference original-tags tags)) (apply #'vulpea-buffer-tags-set tags)))))) (defun vulpea-buffer-p () "Return non-nil if the currently visited buffer is a note." (and buffer-file-name (string-prefix-p (expand-file-name (file-name-as-directory org-roam-directory)) (file-name-directory buffer-file-name)))) (defun vulpea-project-files () "Return a list of note files containing 'hastodos' tag." ; (seq-uniq (seq-map #'car (org-roam-db-query [:select [nodes:file] :from tags :left-join nodes :on (= tags:node-id nodes:id) :where (like tag (quote "%\"hastodos\"%"))])))) (defun vulpea-agenda-files-update (&rest _) "Update the value of `org-agenda-files'." (setq org-agenda-files (vulpea-project-files))) (add-hook 'find-file-hook #'vulpea-project-update-tag) (add-hook 'before-save-hook #'vulpea-project-update-tag) (advice-add 'org-agenda :before #'vulpea-agenda-files-update) (advice-add 'org-todo-list :before #'vulpea-agenda-files-update) ;; functions borrowed from `vulpea' library ;; https://github.com/d12frosted/vulpea/blob/6a735c34f1f64e1f70da77989e9ce8da7864e5ff/vulpea-buffer.el (defun vulpea-buffer-tags-get () "Return filetags value in current buffer." (vulpea-buffer-prop-get-list "filetags" "[ :]")) (defun vulpea-buffer-tags-set (&rest tags) "Set TAGS in current buffer. If filetags value is already set, replace it." (if tags (vulpea-buffer-prop-set "filetags" (concat ":" (string-join tags ":") ":")) (vulpea-buffer-prop-remove "filetags"))) (defun vulpea-buffer-tags-add (tag) "Add a TAG to filetags in current buffer." (let* ((tags (vulpea-buffer-tags-get)) (tags (append tags (list tag)))) (apply #'vulpea-buffer-tags-set tags))) (defun vulpea-buffer-tags-remove (tag) "Remove a TAG from filetags in current buffer." (let* ((tags (vulpea-buffer-tags-get)) (tags (delete tag tags))) (apply #'vulpea-buffer-tags-set tags))) (defun vulpea-buffer-prop-set (name value) "Set a file property called NAME to VALUE in buffer file. If the property is already set, replace its value." (setq name (downcase name)) (org-with-point-at 1 (let ((case-fold-search t)) (if (re-search-forward (concat "^#\\+" name ":\\(.*\\)") (point-max) t) (replace-match (concat "#+" name ": " value) 'fixedcase) (while (and (not (eobp)) (looking-at "^[#:]")) (if (save-excursion (end-of-line) (eobp)) (progn (end-of-line) (insert "\n")) (forward-line) (beginning-of-line))) (insert "#+" name ": " value "\n"))))) (defun vulpea-buffer-prop-set-list (name values &optional separators) "Set a file property called NAME to VALUES in current buffer. VALUES are quoted and combined into single string using `combine-and-quote-strings'. If SEPARATORS is non-nil, it should be a regular expression matching text that separates, but is not part of, the substrings. If nil it defaults to `split-string-default-separators', normally \"[ \f\t\n\r\v]+\", and OMIT-NULLS is forced to t. If the property is already set, replace its value." (vulpea-buffer-prop-set name (combine-and-quote-strings values separators))) (defun vulpea-buffer-prop-get (name) "Get a buffer property called NAME as a string." (org-with-point-at 1 (when (re-search-forward (concat "^#\\+" name ": \\(.*\\)") (point-max) t) (buffer-substring-no-properties (match-beginning 1) (match-end 1))))) (defun vulpea-buffer-prop-get-list (name &optional separators) "Get a buffer property NAME as a list using SEPARATORS. If SEPARATORS is non-nil, it should be a regular expression matching text that separates, but is not part of, the substrings. If nil it defaults to `split-string-default-separators', normally \"[ \f\t\n\r\v]+\", and OMIT-NULLS is forced to t." (let ((value (vulpea-buffer-prop-get name))) (when (and value (not (string-empty-p value))) (split-string-and-unquote value separators)))) (defun vulpea-buffer-prop-remove (name) "Remove a buffer property called NAME." (org-with-point-at 1 (when (re-search-forward (concat "\\(^#\\+" name ":.*\n?\\)") (point-max) t) (replace-match "")))) (setq org-agenda-files (vulpea-project-files))) #+END_SRC ** Basic settings #+BEGIN_SRC emacs-lisp (setq org-log-done 'time) (after! org-mode (setq org-log-done 'time) (add-to-list 'org-tags-exclude-from-inheritance "hastodos") (setq org-hide-emphasis-markers nil)) (setq org-directory "~/org/") (setq org-roam-directory org-directory) #+END_SRC ** Appearances *** Deprecated: TODO Keywords I like to have a few different todo keywords for different sorts of activities. Purchasing todos: #+BEGIN_SRC emacs-lisp :tangle no (setq org-todo-keywords '((sequence "PROJ(p)" "|" "COMPLETE") (type "TICKLE") (type "DELE") (type "WAIT") (sequence "TODO(t)" "|" "DONE") (sequence "DELEGATED" "VERIFY" "|" "DONE") (sequence "BUY(b)" "|" "BOUGHT") )) (setq org-todo-keyword-faces '(("TODO" . "salmon1") ("START" . "spring green" ) ("DELE" . "gold") ("TICKLE" . "magenta") ("WAIT" . "gold") ("PROJ" . "deep sky blue"))) #+END_SRC These settings aren't very useful for me, as I like default colors and keywords. *** Deprecated: Settings for org-modern #+BEGIN_SRC emacs-lisp :tangle no (setq org-modern-todo-faces '(("START" :background "spring green" :foreground "black") ("DELE" :background "gold" :foreground "black") ("WAIT" :background "gold" :foreground "black") ("PROJ" :background "deep sky blue" :foreground "black") ("TICKLE" :background "magenta" :foreground "black"))) (setq org-modern-priority-faces '((?A :background "salmon1" :foreground "black") (?B :background "gold" :foreground "black") (?C :background "spring green" :foreground "black"))) #+END_SRC I currently have org-modern stripped out of my config. I probably won't need this again, but in case I do I want all my preferences to be there. *** Images Preferences We want to show images when loading a file, and also after evaluating code blocks. #+BEGIN_SRC emacs-lisp (setq org-startup-with-inline-images t) ;; Show images after evaluating code blocks. (add-hook 'org-babel-after-execute-hook 'org-display-inline-images) #+END_SRC ** Org-drill Set some good keybinds for quick access: #+BEGIN_SRC emacs-lisp (use-package! org-drill :defer nil ) (map! :leader :desc "Start org-drill" "d d" #'org-drill-directory) (map! :leader :desc "Start org-drill in cram mode" "d c" #'org-drill-cram) #+END_SRC ** ob-lilypond #+BEGIN_SRC emacs-lisp (use-package! ob-lilypond) (setq ly-arrange-mode t) #+END_SRC ** org-edna Edna allows better dependency handling for todos and the like. #+BEGIN_SRC emacs-lisp (use-package! org-edna) (org-edna-mode) #+END_SRC ** Org-habit #+BEGIN_SRC emacs-lisp (use-package org-habit :custom (org-habit-graph-column 1) (org-habit-preceding-days 10) (org-habit-following-days 1) (org-habit-show-habits-only-for-today nil)) (use-package! org-heatmap :after (org) :config (org-heatmap-mode)) #+END_SRC ** Org-archive with structure Many thanks to Mark Edigmar's [[https://gist.github.com/edgimar/072d99d8650abe81a9fe7c8687c0c993][Gist]] *** Keymaps #+BEGIN_SRC emacs-lisp (map! :leader :desc "Export to html and diplay with eww" :n "m A" #'org-archive-subtree-default) #+END_SRC *** Source #+BEGIN_SRC emacs-lisp (require 'org-archive) ; Set the function to use for org-archive-default (C-c C-x C-a) ; (setq org-archive-location (concat org-directory "/archive/%s_archive::")) ; (setq org-archive-location "archive/archived_%s::") (setq org-archive-location "~/org/archive.org") (setq org-archive-location (concat org-directory "/archive.org::")) ; unmap org-archive-subtree ; select command to execute via org-archive-subtree-default (C-c C-x C-a) (setq org-archive-default-command 'org-archive-subtree-hierarchical) (defun line-content-as-string () "Returns the content of the current line as a string" (save-excursion (beginning-of-line) (buffer-substring-no-properties (line-beginning-position) (line-end-position)))) (defun org-child-list (&optional top-level) "This function returns all children of a heading as a list. " (interactive) (save-excursion ;; this only works with org-version > 8.0, since in previous ;; org-mode versions the function (org-outline-level) returns ;; gargabe when the point is not on a heading. (unless top-level (if (= (org-outline-level) 0) (outline-next-visible-heading 1) (org-goto-first-child))) (let ((child-list (list (line-content-as-string)))) (while (org-goto-sibling) (setq child-list (cons (line-content-as-string) child-list))) child-list))) (defun fa/org-struct-subtree () "This function returns the tree structure in which a subtree belongs as a list." (interactive) (let ((archive-tree nil)) (save-excursion (while (org-up-heading-safe) (let ((heading (buffer-substring-no-properties (line-beginning-position) (line-end-position)))) (if (eq archive-tree nil) (setq archive-tree (list heading)) (setq archive-tree (cons heading archive-tree)))))) archive-tree)) (defun org-archive-subtree-hierarchical () "This function archives a subtree hierarchical" (interactive) (let ((org-tree (fa/org-struct-subtree)) (source-buffer (current-buffer)) (file (abbreviate-file-name (or (buffer-file-name (buffer-base-buffer)) (error "No file associated to buffer"))))) (save-excursion (setq location (org-archive--compute-location (or (org-entry-get nil "ARCHIVE" 'inherit) org-archive-location)) afile (car location) heading (cdr location) infile-p (equal file (abbreviate-file-name (or afile "")))) (unless afile (error "Invalid `org-archive-location'")) (if (not (equal heading "")) (progn (setq org-tree (cons heading (mapcar (lambda (s) (concat "*" s)) org-tree))) (org-demote-subtree))) (if (> (length afile) 0) (progn (setq newfile-p (not (file-exists-p afile)) visiting (find-buffer-visiting afile) target-buffer (or visiting (find-file-noselect afile)))) (progn (setq target-buffer (current-buffer)))) (unless target-buffer (error "Cannot access file \"%s\"" afile)) (org-cut-subtree) (set-buffer target-buffer) (setq ind-target-buffer (clone-indirect-buffer nil nil)) (set-buffer ind-target-buffer) (org-mode) (goto-char (point-min)) ; simplified version of org-complex-heading-regexp-format (setq my-org-complex-heading-regexp-format (concat "^" "\\(%s\\)" "\\(?: *\\[[0-9%%/]+\\]\\)*" "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?" "[ \t]*$")) (setq top-level-p t) (while (not (equal org-tree nil)) (let ((child-list (org-child-list top-level-p)) (re (format my-org-complex-heading-regexp-format (regexp-quote (car org-tree)))) ) (if (member "______FOUND_MATCH" (mapcar (lambda (s) (replace-regexp-in-string re "______FOUND_MATCH" s)) child-list)) (progn (re-search-forward re nil t) (setq org-tree (cdr org-tree))) (progn (if (not top-level-p) (newline)) (org-insert-struct org-tree) (setq org-tree nil)))) (setq top-level-p nil)) (end-of-buffer) (newline) (org-yank) ;; Kill the indirect buffer, returning the current buffer to the direct target buffer (kill-buffer ind-target-buffer) ;; Save and kill the target buffer, if it is not the source buffer. (when (not (eq source-buffer target-buffer)) (save-buffer target-buffer) (kill-buffer target-buffer)) ;; ensure font-lock and indentation are normal (set-buffer source-buffer) (org-restart-font-lock) (org-indent-mode t) (message "Subtree archived %s" (concat "in file: " (abbreviate-file-name afile)))))) (defun org-insert-struct (struct) "TODO" (interactive) (when struct (insert (car struct)) (if (not (equal (length struct) 1)) (newline)) (org-insert-struct (cdr struct)))) #+END_SRC * Website ** Capture template #+BEGIN_SRC emacs-lisp (setq templates/post-capture-props "#+date: [%<%Y-%m-%d %a>]\n#+lastmod:\n#+categories[]:\n#+tags[]:\n#+images[]: ") (setq templates/post-capture-title "#+TITLE: ${title}\n") (setq templates/post-capture-template (concat templates/post-capture-title templates/post-capture-props)) #+END_SRC ** Images #+BEGIN_SRC emacs-lisp (setq org-preview-latex-image-directory (concat org-directory (file-name-as-directory "images/latexsnip"))) #+END_SRC ** Hugo base directory #+BEGIN_SRC emacs-lisp (setq org-hugo-base-dir (concat org-directory (file-name-as-directory "website"))) (setq org-hugo-default-section-directory "posts") #+END_SRC * Crafting a Writing Environment For writing I like to automate as much as possible. This means creating an environment that does citations and such /for/ me. ** Export settings *** Ox-latex #+BEGIN_SRC emacs-lisp (after! org ;; Import ox-latex to get org-latex-classes and other funcitonality ;; for exporting to LaTeX from org (use-package! ox-latex :init :config (setq org-latex-with-hyperref nil) ;; stop org adding hypersetup{author..} to latex export ;; (setq org-latex-prefer-user-labels t) (setq org-latex-logfiles-extensions (quote ("lof" "lot" "tex~" "aux" "idx" "log" "out" "toc" "nav" "snm" "vrb" "dvi" "fdb_latexmk" "blg" "brf" "fls" "entoc" "ps" "spl" "bbl" "xmpi" "run.xml" "bcf" "acn" "acr" "alg" "glg" "gls" "ist"))) (unless (boundp 'org-latex-classes) (setq org-latex-classes nil)))) #+END_SRC *** Lualatex as PDF Processor I've found that lualatex does a good job processing PDFs. $hi$ #+BEGIN_SRC emacs-lisp (after! ox-latex (setq org-latex-pdf-process '("lualatex --output-directory=/home/user/Documents -shell-escape -interaction nonstopmode %f" "lualatex --output-directory=/home/user/Documents -shell-escape -interaction nonstopmode %f")) ) #+END_SRC $x + 1 = 3$ #+RESULTS: : luamagick #+BEGIN_SRC emacs-lisp (setq-default org-html-with-latex `dvisvgm) (unless (boundp 'org-latex-classes) (setq org-latex-classes nil)) (add-to-list 'org-latex-classes '("ethz" "\\documentclass[a4paper,11pt,titlepage]{memoir} \\usepackage[utf8]{inputenc} \\usepackage[T1]{fontenc} \\usepackage{fixltx2e} \\usepackage{graphicx} \\usepackage{longtable} \\usepackage{float} \\usepackage{wrapfig} \\usepackage{rotating} \\usepackage[normalem]{ulem} \\usepackage{amsmath} \\usepackage{textcomp} \\usepackage{marvosym} \\usepackage{wasysym} \\usepackage{amssymb} \\usepackage[hidelinks]{hyperref} \\usepackage{mathpazo} \\usepackage{color} \\usepackage{enumerate} \\definecolor{bg}{rgb}{0.95,0.95,0.95} \\tolerance=1000 [NO-DEFAULT-PACKAGES] [PACKAGES] [EXTRA] \\linespread{1.1} \\hypersetup{pdfborder=0 0 0}" ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))) (add-to-list 'org-latex-classes '("article" "\\documentclass[11pt,a4paper]{article} \\usepackage[utf8]{inputenc} \\usepackage[T1]{fontenc} \\usepackage{fixltx2e} \\usepackage{graphicx} \\usepackage{longtable} \\usepackage{float} \\usepackage{wrapfig} \\usepackage{rotating} \\usepackage[normalem]{ulem} \\usepackage{amsmath} \\usepackage{textcomp} \\usepackage{marvosym} \\usepackage{wasysym} \\usepackage{amssymb} \\usepackage[hidelinks]{hyperref} \\usepackage{mathpazo} \\usepackage{color} \\usepackage{enumerate} \\definecolor{bg}{rgb}{0.95,0.95,0.95} \\tolerance=1000 [NO-DEFAULT-PACKAGES] [PACKAGES] [EXTRA] \\linespread{1.1} \\hypersetup{pdfborder=0 0 0}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}"))) (add-to-list 'org-latex-classes '("ebook" "\\documentclass[11pt, oneside]{memoir} \\setstocksize{9in}{6in} \\settrimmedsize{\\stockheight}{\\stockwidth}{*} \\setlrmarginsandblock{2cm}{2cm}{*} % Left and right margin \\setulmarginsandblock{2cm}{2cm}{*} % Upper and lower margin \\checkandfixthelayout % Much more laTeX code omitted " ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}"))) #+END_SRC #+RESULTS: | mcdowellcv | \documentclass[]{mcdowellcv} | *** Fixing dvipng image handling #+BEGIN_SRC emacs-lisp (defun +org-refresh-latex-images-previews-h () (dolist (buffer (doom-buffers-in-mode 'org-mode (buffer-list))) (with-current-buffer buffer (+org--toggle-inline-images-in-subtree (point-min) (point-max) 'refresh) (unless (eq org-latex-preview-default-process 'dvisvgm) (org-clear-latex-preview (point-min) (point-max)) (org--latex-preview-region (point-min) (point-max)))))) (add-hook 'doom-load-theme-hook #'+org-refresh-latex-images-previews-h) #+END_SRC *** Export Function #+BEGIN_SRC emacs-lisp (defun eww-open-this-file () (set 'directory (file-name-directory buffer-file-name)) (set 'filename (file-name-sans-extension buffer-file-name)) (set 'extension ".html") (set 'filename (format "%s%s" filename extension)) (eww-open-file filename) (evil-window-left) ) (defun org-export-and-open-eww () "Export current file to html and open in eww" (interactive) (org-html-export-to-html) (if (equal (file-name-extension buffer-file-name) "org") (eww-open-this-file) (message "Failed") ) ) #+END_SRC We'll want a handy shortcut for this. #+BEGIN_SRC emacs-lisp (map! :leader :desc "Export to html and diplay with eww" "r E" #'org-export-and-open-eww) #+END_SRC #+RESULTS: : /home/user/.config/doom/ ** Automatic Citations with *citar* and *org-cite* Citar is a sweet little package for managing citations. We want a general [[file:~/org/references.bib][bibliography file]], a [[file:~/Zotero/styles/][styles directory]], and a default set of styles. *** Citar setup First of all, we must configure citar. #+BEGIN_SRC emacs-lisp (use-package! citar :ensure t :demand t :custom (citar-bibliography "~/org/references.bib") (citar-file-note-extensions '(".org")) (org-cite-global-bibliography '("~/org/references.bib")) (org-cite-csl-styles-dir (expand-file-name "~/Zotero/styles/")) (org-cite-export-processors '((t . (csl "apa.csl")) )) :hook (LaTeX-mode . citar-capf-setup) (org-mode . citar-capf-setup)) (citar-org-roam-mode) #+END_SRC *** Icons for some prettification #+BEGIN_SRC emacs-lisp (after! citar (setq citar-indicator-files-icons (citar-indicator-create :symbol (nerd-icons-faicon "nf-fa-file_o" :face 'nerd-icons-green :v-adjust -0.1) :function #'citar-has-files :padding " " ; need this because the default padding is too low for these icons :tag "has:files")) (setq citar-indicator-links-icons (citar-indicator-create :symbol (nerd-icons-faicon "nf-fa-link" :face 'nerd-icons-orange :v-adjust 0.01) :function #'citar-has-links :padding " " :tag "has:links")) (setq citar-indicator-notes-icons (citar-indicator-create :symbol (nerd-icons-codicon "nf-cod-note" :face 'nerd-icons-blue :v-adjust -0.3) :function #'citar-has-notes :padding " " :tag "has:notes")) (setq citar-indicator-cited-icons (citar-indicator-create :symbol (nerd-icons-faicon "nf-fa-circle_o" :face 'nerd-icon-green) :function #'citar-is-cited :padding " " :tag "is:cited")) (setq citar-indicators (list citar-indicator-files-icons citar-indicator-links-icons citar-indicator-notes-icons citar-indicator-cited-icons))) #+END_SRC ** Scholarly Writing Environment with Org-mode I like to keep formatting simple. Let's use a package or two to set a decent document class: #+BEGIN_SRC emacs-lisp (add-to-list 'org-latex-classes '("student-apa7" "\\documentclass[stu]{apa7} \\hypersetup{hidelinks} \\usepackage{times} [NO-DEFAULT-PACKAGES] [NO-PACKAGES]" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))) (add-to-list 'org-latex-classes '("student-turabian" "\\documentclass{turabian-researchpaper} [NO-DEFAULT-PACKAGES] [NO-PACKAGES]" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}") ("\\usepackage{biblatex-chicago}"))) (add-to-list 'org-latex-classes '("student-mla" "\\documentclass[stu]{mla} \\hypersetup{hidelinks} \\usepackage{times} " ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))) ;; org-latex-compilers = ("pdflatex" "xelatex" "lualatex"), which are the possible values for %latex #+END_SRC *** Engraving Faces Ever wonder why code export sucks with \LaTeX ? Me neither! Let's fix it! #+BEGIN_SRC emacs-lisp (use-package! engrave-faces) (setq org-latex-listings 'engraved) #+END_SRC ** Zettelkasten environment with org-roam Org-roam enables features essential to a Zettelkasten such as inter-ID linking. #+BEGIN_SRC emacs-lisp (use-package! org-roam :after md-roam :init (setq org-roam-directory "~/org/")) :custom #+END_SRC #+BEGIN_SRC emacs-lisp (after! org-roam :ensure t :custom (setq org-roam-capture-templates `( ("d" "default" plain "%?" :target (file+head "%<%Y%m%d-%H%M%S>.org" "#+TITLE: ${title}\n#+HTML_HEAD: \n") :unnarrowed t) ("s" "school" plain "%?" :target (file+head "%<%Y%m%d-%H%M%S>.org" "#+TITLE: ${title} ,#+LATEX_CLASS: student-apa7 ,#+LATEX_OPTIONS: stu,hidelinks ,#+LATEX_HEADER: \\course{} ,#+LATEX_HEADER:\\authorsnames{Judah Sotomayor} ,#+LATEX_HEADER: \\authorsaffiliations{} ,#+LATEX_HEADER: \\professor{} ,#+LATEX_HEADER: \\usepackage{times} ,#+LATEX_HEADER: \\duedate{} ,#+OPTIONS: toc:nil\n") :unnarrowed t) ("c" "encrypted" plain "%?" :target (file+head "%<%Y%m%d-%H%M%S>.sec.org.age" "#+TITLE: ${title}\n#+FILETAGS: :secure:noexport:\n#+HTML_HEAD: \n\n\n* No Export Below This Line") :unnarrowed t)))) #+END_SRC *** Keybinds Org-roam has many commands, of which I have bound the most important below. This entails creating a local leader. I use =r= for this purpose. It is close and goes well with *roam*. #+BEGIN_SRC emacs-lisp (after! org-roam (map! :leader (:prefix ("r" . "roam") :desc "Search for a node in org-roam files" "f" #'org-roam-node-find :desc "Insert the link to a node from org-roam" "i" #'org-roam-node-insert :desc "Rescan org-roam files and add new nodes to database" "s" #'org-roam-db-sync :desc "Jump to a random node" "r" #'org-roam-node-random :desc "Capture inside an existing or new node" "c" #'org-roam-capture :desc "Go to or create a daily note" "d" #'org-roam-dailies-goto-today :desc "Seek a daily note" "D" #'org-roam-dailies-goto-date :desc "Extract a subtree to a new file" "e" #'org-roam-extract-subtree)) (map! :desc "Alternative keybind to insert a roam link while in insert mode" :i "M-[" #'org-roam-node-insert)) #+END_SRC *** Nice UI to access Zettelkasten notes with Org-roam ui #+BEGIN_SRC emacs-lisp (use-package! websocket :after org-roam) (use-package! org-roam-ui :after org-roam ;; or :after org ;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have ;; a hookable mode anymore, you're advised to pick something yourself ;; if you don't care about startup time, use ;; :hook (after-init . 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)) (after! org-roam-ui (map! :leader (:prefix ("r" . "roam") (:prefix ("u" . "roam-ui") :desc "Focus the current node in org-roam-ui view" "f" #'org-roam-ui-node-zoom :desc "Focus the current node's local graph in org-roam-ui view" "l" #'org-roam-ui-node-local :desc "Begin org-roam ui mode" "u" #'open-org-roam-ui )))) #+END_SRC **** Function to pull up org-roam ui #+BEGIN_SRC emacs-lisp (defun open-org-roam-ui () (interactive) (org-roam-ui-mode) (async-shell-command "surf http://localhost:35901/")) #+END_SRC *** Default Browser for Export #+BEGIN_SRC emacs-lisp (setq browse-url-browser-function 'browse-url-generic browse-url-generic-program "surf") '(org-file-apps (quote ((auto-mode . emacs) ("\\.mm\\'" . default) ("\\.x?html?\\'" . "surf %s") ("\\.pdf\\'" . default)))) #+END_SRC ** Journal Environment with org-roam #+BEGIN_SRC emacs-lisp (after! org-roam (setq org-roam-dailies-capture-templates `(("d" "default" entry "* %?" :target (file+head "%<%Y-%m-%d>.sec.org.age" "#+title: %<%Y-%m-%d>\n"))))) #+END_SRC *** Property getters and setters These are to fulfill my need to get property values for generating my [[id:6672f401-32a1-49ef-8004-ac77ece67f5b][journal index]]. #+BEGIN_SRC emacs-lisp (require 'cl-lib) (defun org-global-props-key-re (key) "Construct a regular expression matching key and an optional plus and eating the spaces behind. Test for existence of the plus: (match-beginning 1)" (concat "^" (regexp-quote key) "\\(\\+\\)?[[:space:]]+")) (defun org-global-props (&optional buffer) "Get the plists of global org properties of current buffer." (with-current-buffer (or buffer (current-buffer)) (org-element-map (org-element-parse-buffer) 'keyword (lambda (el) (when (string-equal (org-element-property :key el) "PROPERTY") (nth 1 el)))))) (defun org-global-prop-value (key) "Get global org property KEY of current buffer. Adding up values for one key is supported." (let ((key-re (org-global-props-key-re key)) (props (org-global-props)) ret) (cl-loop with val for prop in props when (string-match key-re (setq val (plist-get prop :value))) do (setq val (substring val (match-end 0)) ret (if (match-beginning 1) (concat ret " " val) val))) ret)) #+END_SRC ** Resume setup for a stellar CV *** ox-extra for ":ignore:" tags I want to be able to ignore headings on export. #+BEGIN_SRC emacs-lisp (after! org (use-package! ox-extra :config (ox-extras-activate '(latex-header-blocks ignore-headlines)))) #+END_SRC *** Resume template #+BEGIN_SRC emacs-lisp (add-to-list 'org-latex-classes '("altacv" "\\documentclass[10pt,a4paper,ragged2e,withhyper]{altacv} \\usepackage[rm]{roboto} \\usepackage[defaultsans]{lato} \\usepackage{paracol} [NO-DEFAULT-PACKAGES] [NO-PACKAGES] % Change the page layout if you need to \\geometry{left=1.25cm,right=1.25cm,top=1.5cm,bottom=1.5cm,columnsep=1.2cm} % Use roboto and lato for fonts \\renewcommand{\\familydefault}{\\sfdefault} % Change the colours if you want to \\definecolor{SlateGrey}{HTML}{2E2E2E} \\definecolor{LightGrey}{HTML}{666666} \\definecolor{DarkPastelRed}{HTML}{450808} \\definecolor{PastelRed}{HTML}{8F0D0D} \\definecolor{GoldenEarth}{HTML}{E7D192} \\colorlet{name}{black} \\colorlet{tagline}{black} \\colorlet{heading}{black} \\colorlet{headingrule}{black} \\colorlet{subheading}{black} \\colorlet{accent}{black} \\colorlet{emphasis}{SlateGrey} \\colorlet{body}{LightGrey} % Change some fonts, if necessary \\renewcommand{\\namefont}{\\Huge\\rmfamily\\bfseries} \\renewcommand{\\personalinfofont}{\\footnotesize} \\renewcommand{\\cvsectionfont}{\\LARGE\\rmfamily\\bfseries} \\renewcommand{\\cvsubsectionfont}{\\large\\bfseries} % Change the bullets for itemize and rating marker % for \cvskill if you want to \\renewcommand{\\itemmarker}{{\\small\\textbullet}} \\renewcommand{\\ratingmarker}{\\faCircle} " ("\\cvsection{%s}" . "\\cvsection*{%s}") ("\\cvevent{%s}" . "\\cvevent*{%s}"))) #+END_SRC *** McDowell Resume Template This template is supposed to be the standard. #+BEGIN_SRC emacs-lisp (add-to-list 'org-latex-classes '("mcdowellcv" "\\documentclass[]{mcdowellcv} \\usepackage{amsmath} \\usepackage[]{multicol} [NO-DEFAULT-PACKAGES] [NO-PACKAGES]" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}") ("\\cvsection{%s}" . "\\cvsection*{%s}") ("\\cvevent{%s}" . "\\cvevent*{%s}"))) #+END_SRC ** Better EViL keybinds *** Remapping =g-j/k= #+BEGIN_SRC emacs-lisp (map! :n "g j" #'evil-next-visual-line) (map! :n "g k" #'evil-previous-visual-line) #+END_SRC * Company-mode for great autocompletes I don't really want company-mode to run on timeouts. I want VIM-like autocompletion, where =C-x o= and friends gives me my autocompletion. #+BEGIN_SRC emacs-lisp (after! company-mode (setq company-idle-delay nil)) (setq company-idle-delay nil) #+END_SRC * Initializing shell environment variables #+BEGIN_SRC emacs-lisp (use-package! exec-path-from-shell) (after! exec-path-from-shell (dolist (var '("SSH_AUTH_SOCK" "SSH_AGENT_PID" "GPG_AGENT_INFO")) (add-to-list 'exec-path-from-shell-variables var)) (exec-path-from-shell-initialize) ) #+END_SRC * Md-roam #+BEGIN_SRC emacs-lisp ;; file-truename is optional; it seems required when you use symbolic ;; links, which Org-roam does not resolve (use-package! md-roam :config (setq org-roam-file-extensions '("org" "md")) ; enable Org-roam for a markdown extension (setq md-roam-file-extension "md") ; default "md". Specify an extension such as "markdown" (setq md-roam-file-extension-single "md") (setq md-roam-use-org-extract-ref nil) (org-roam-db-autosync-mode 1)) #+END_SRC ** COMMENT Capture template for documentation #+BEGIN_SRC emacs-lisp (add-to-list 'org-roam-capture-templates '("m" "Markdown" plain "" :target (file+head "training-resources/content/${title}.md" "---\ntitle: ${title}\nid: %<%Y-%m-%dT%H%M%S>\ncategory: \n---\n") :unnarrowed t)) #+END_SRC