#+TITLE: Doom Emacs Configuration #+AUTHOR: tecosaur #+PROPERTY: header-args:emacs-lisp :tangle yes :cache yes :results silent :comments link #+HTML_HEAD: #+BEGIN_QUOTE Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do. --- Donald Knuth #+END_QUOTE * Intro Customising an editor can be very rewarding ... until you have to leave it. For years I have been looking for ways to avoid this pain. Then I discovered [[https://github.com/cknadler/vim-anywhere][vim-anywhere]], and found that it had an emacs companion,[[https://github.com/zachcurry/emacs-anywhere][ emacs-anywhere]]. To me, this looked most attractive. Separately, online I have seen the following statement enough times I think it's a catchphrase #+BEGIN_QUOTE Redditor1: I just discovered this thing, isn't it cool. Redditor2: Oh, there's an emacs mode for that. #+END_QUOTE I tried out the =spacemacs= distribution a bit, but it wasn't quite to my liking. Then I heard about =doom emacs= and thought I may as well give that a try. TLDR; it's great. Now I've discovered the wonders of literate programming, and am becoming more settled by the day. This is my config. * Rudimentary configuration Make this file run (slightly) faster with lexical binding (see [[https://nullprogram.com/blog/2016/12/22/][this blog post]] for more info). #+BEGIN_SRC emacs-lisp ;;; config.el -*- lexical-binding: t; -*- #+END_SRC ** Personal Information It's useful to have some basic personal information #+BEGIN_SRC emacs-lisp (setq user-full-name "tecosaur" user-mail-address "tecosaur@gmail.com") #+END_SRC Apparently this is used by ~GPG~, and all sorts of other things. ** Better defaults *** Simple settings Browsing the web and seeing [[https://github.com/angrybacon/dotemacs/blob/master/dotemacs.org#use-better-defaults][angrybacon/dotemacs]] and comparing with the values shown by =SPC h v= and selecting what I thought looks good, I've ended up adding the following: #+BEGIN_SRC emacs-lisp (setq-default delete-by-moving-to-trash t ; Delete files to trash tab-width 4 ; Set width for tabs uniquify-buffer-name-style 'forward ; Uniquify buffer names window-combination-resize t ; take new window space from all other windows (not just current) x-stretch-cursor t) ; Stretch cursor to the glyph width (setq undo-limit 80000000 ; Raise undo-limit to 80Mb evil-want-fine-undo t ; By default while in insert all changes are one big blob. Be more granular auto-save-default t ; Nobody likes to loose work, I certainly don't inhibit-compacting-font-caches t) ; When there are lots of glyphs, keep them in memory (delete-selection-mode 1) ; Replace selection when inserting text (display-time-mode 1) ; Enable time in the mode-line (display-battery-mode 1) ; On laptops it's nice to know how much power you have (global-subword-mode 1) ; Iterate through CamelCase words #+END_SRC *** Fullscreen I also like the idea of fullscreen-ing when opened by ~emacs~ or the ~.desktop~ file. #+BEGIN_SRC emacs-lisp (if (eq initial-window-system 'x) ; if started by emacs command or desktop file (toggle-frame-maximized) (toggle-frame-fullscreen)) #+END_SRC *** Auto-customisations By default changes made via a customisation interface are added to =init.el=. I prefer the idea of using a separate file for this. We just need to change a setting, and load it if it exists. #+BEGIN_SRC emacs-lisp (setq-default custom-file (expand-file-name ".custom.el" doom-private-dir)) (when (file-exists-p custom-file) (load custom-file)) #+END_SRC *** Windows I find it rather handy to be asked which buffer I want to see after splitting the window. Let's make that happen. First, we'll enter the new window #+BEGIN_SRC emacs-lisp (setq evil-vsplit-window-right t evil-split-window-below t) #+END_SRC Then, we'll pull up ~ivy~ #+BEGIN_SRC emacs-lisp (defadvice! prompt-for-buffer (&rest _) :after '(evil-window-split evil-window-vsplit) (+ivy/switch-buffer)) #+END_SRC *** Buffer defaults I'd much rather have my new buffers in ~org-mode~ than ~fundamental-mode~, hence #+BEGIN_SRC emacs-lisp ;; (setq-default major-mode 'org-mode) #+END_SRC For some reason this + the mixed pitch hook causes issues with hydra and so I'll just need to resort to =SPC b o= for now. ** Doom configuration *** Visual Settings **** Font Face 'Fira Code' is nice, and 'Overpass' makes for a nice sans companion. We just need to fiddle with the font sizes a tad so that they visually match. #+BEGIN_SRC emacs-lisp (setq doom-font (font-spec :family "Fira Code" :size 22) doom-big-font (font-spec :family "Fira Code" :size 36) doom-variable-pitch-font (font-spec :family "Overpass" :size 24)) #+END_SRC **** Theme ~doom-one~ is nice and all, but I find the ~vibrant~ variant nicer. #+BEGIN_SRC emacs-lisp (setq doom-theme 'doom-vibrant) #+END_SRC However, by default ~red~ text is used in the ~modeline~, so let's make that orange so I don't feel like something's gone /wrong/ when editing files. #+BEGIN_SRC emacs-lisp (custom-set-faces! '(doom-modeline-buffer-modified :foreground "orange")) #+END_SRC **** Miscellaneous Relative line numbers are fantastic for knowing how far away line numbers are, then =ESC 12 = gets you exactly where you think. #+BEGIN_SRC emacs-lisp (setq display-line-numbers-type 'relative) #+END_SRC I'd like some slightly nicer default buffer names #+BEGIN_SRC emacs-lisp (setq doom-fallback-buffer-name "► Doom" +doom-dashboard-name "► Doom") #+END_SRC There's a bug with the modeline in insert mode for org documents ([[https://github.com/seagle0128/doom-modeline/issues/300][issue]]), so #+BEGIN_SRC emacs-lisp (custom-set-faces! '(doom-modeline-evil-insert-state :weight bold :foreground "#339CDB")) #+END_SRC *** Some helper macros There are a few handy macros added by doom, namely - ~load!~ for loading external ~.el~ files relative to this one - ~use-package~ for configuring packages - ~add-load-path!~ for adding directories to the ~load-path~ where ~emacs~ looks when you load packages with ~require~ or ~use-package~ - ~map~ for binding new keys To find more, ** Other things *** Editor interaction **** Mouse buttons #+BEGIN_SRC emacs-lisp (map! :n [mouse-8] #'better-jumper-jump-backward :n [mouse-9] #'better-jumper-jump-forward) #+END_SRC *** Window title I'd like to have just the buffer name, then if applicable the project folder #+BEGIN_SRC emacs-lisp (setq frame-title-format '("" "%b" (:eval (let ((project-name (projectile-project-name))) (unless (string= "-" project-name) (format (if (buffer-modified-p) " ◉ %s" "  ●  %s") project-name)))))) #+END_SRC *** Splash screen Emacs can render an image as the splash screen, and [[https://github.com/MarioRicalde][@MarioRicalde]] came up with a cracker! He's also provided me with a nice emacs-style /E/, which is good for smaller windows. *@MarioRicalde* you have my sincere thanks, you're great! [[file:misc/splash-images/blackhole-lines.svg]] By incrementally stripping away the outer layers of the logo one can obtain quite a nice resizing effect. #+BEGIN_SRC emacs-lisp (defvar fancy-splash-image-template (expand-file-name "misc/splash-images/blackhole-lines-template.svg" doom-private-dir) "Default template svg used for the splash image, with substitutions from ") (defvar fancy-splash-image-nil (expand-file-name "misc/splash-images/transparent-pixel.png" doom-private-dir) "An image to use at minimum size, usually a transparent pixel") (setq fancy-splash-sizes `((:height 500 :min-height 50 :padding (0 . 2) :template ,(expand-file-name "misc/splash-images/blackhole-lines-0.svg" doom-private-dir)) (:height 440 :min-height 42 :padding (1 . 4) :template ,(expand-file-name "misc/splash-images/blackhole-lines-0.svg" doom-private-dir)) (:height 400 :min-height 38 :padding (1 . 4) :template ,(expand-file-name "misc/splash-images/blackhole-lines-1.svg" doom-private-dir)) (:height 350 :min-height 36 :padding (1 . 3) :template ,(expand-file-name "misc/splash-images/blackhole-lines-2.svg" doom-private-dir)) (:height 300 :min-height 34 :padding (1 . 3) :template ,(expand-file-name "misc/splash-images/blackhole-lines-3.svg" doom-private-dir)) (:height 250 :min-height 32 :padding (1 . 2) :template ,(expand-file-name "misc/splash-images/blackhole-lines-4.svg" doom-private-dir)) (:height 200 :min-height 30 :padding (1 . 2) :template ,(expand-file-name "misc/splash-images/blackhole-lines-5.svg" doom-private-dir)) (:height 100 :min-height 24 :padding (1 . 2) :template ,(expand-file-name "misc/splash-images/emacs-e-template.svg" doom-private-dir)) (:height 0 :min-height 0 :padding (0 . 0) :file ,fancy-splash-image-nil))) (defvar fancy-splash-sizes `((:height 500 :min-height 50 :padding (0 . 2)) (:height 440 :min-height 42 :padding (1 . 4)) (:height 330 :min-height 35 :padding (1 . 3)) (:height 200 :min-height 30 :padding (1 . 2)) (:height 0 :min-height 0 :padding (0 . 0) :file ,fancy-splash-image-nil)) "list of plists with the following properties :height the height of the image :min-height minimum `frame-height' for image :padding `+doom-dashboard-banner-padding' to apply :template non-default template file :file file to use instead of template") (defvar fancy-splash-template-colours '(("$colour1" . keywords) ("$colour2" . type) ("$colour3" . base5) ("$colour4" . base8)) "list of colour-replacement alists of the form (\"$placeholder\" . 'theme-colour) which applied the template") (unless (file-exists-p (expand-file-name "theme-splashes" doom-cache-dir)) (make-directory (expand-file-name "theme-splashes" doom-cache-dir) t)) (defun fancy-splash-filename (theme-name height) (expand-file-name (concat (file-name-as-directory "theme-splashes") (symbol-name doom-theme) "-" (number-to-string height) ".svg") doom-cache-dir)) (defun fancy-splash-clear-cache () "Delete all cached fancy splash images" (interactive) (delete-directory (expand-file-name "theme-splashes" doom-cache-dir) t) (message "Cache cleared!")) (defun fancy-splash-generate-image (template height) "Read TEMPLATE and create an image if HEIGHT with colour substitutions as ;described by `fancy-splash-template-colours' for the current theme" (with-temp-buffer (insert-file-contents template) (re-search-forward "$height" nil t) (replace-match (number-to-string height) nil nil) (dolist (substitution fancy-splash-template-colours) (beginning-of-buffer) (while (re-search-forward (car substitution) nil t) (replace-match (doom-color (cdr substitution)) nil nil))) (write-region nil nil (fancy-splash-filename (symbol-name doom-theme) height) nil nil))) (defun fancy-splash-generate-images () "Perform `fancy-splash-generate-image' in bulk" (dolist (size fancy-splash-sizes) (unless (plist-get size :file) (fancy-splash-generate-image (or (plist-get size :file) (plist-get size :template) fancy-splash-image-template) (plist-get size :height))))) (defun ensure-theme-splash-images-exist (&optional height) (unless (file-exists-p (fancy-splash-filename (symbol-name doom-theme) (or height (plist-get (car fancy-splash-sizes) :height)))) (fancy-splash-generate-images))) (defun get-appropriate-splash () (let ((height (frame-height))) (cl-some (lambda (size) (when (>= height (plist-get size :min-height)) size)) fancy-splash-sizes))) (setq fancy-splash-last-size nil) (setq fancy-splash-last-theme nil) (defun set-appropriate-splash (&optional frame) (let ((appropriate-image (get-appropriate-splash))) (unless (and (equal appropriate-image fancy-splash-last-size) (equal doom-theme fancy-splash-last-theme))) (unless (plist-get appropriate-image :file) (ensure-theme-splash-images-exist (plist-get appropriate-image :height))) (setq fancy-splash-image (or (plist-get appropriate-image :file) (fancy-splash-filename (symbol-name doom-theme) (plist-get appropriate-image :height)))) (setq +doom-dashboard-banner-padding (plist-get appropriate-image :padding)) (setq fancy-splash-last-size appropriate-image) (setq fancy-splash-last-theme doom-theme) (+doom-dashboard-reload))) (add-hook 'window-size-change-functions #'set-appropriate-splash) (add-hook 'doom-load-theme-hook #'set-appropriate-splash) #+END_SRC *** Systemd daemon For running a systemd service for a emacs server I have the following #+BEGIN_SRC systemd :tangle ~/.config/systemd/user/emacs.service [Unit] Description=Emacs server daemon Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/ [Service] Type=forking ExecStart=/usr/bin/emacs --daemon ExecStop=/usr/bin/emacsclient --eval "(progn (setq kill-emacs-hook nil) (kill emacs))" Environment=SSH_AUTH_SOCK=%t/keyring/ssh Restart=on-failure [Install] WantedBy=default.target #+END_SRC which is then enabled by #+BEGIN_SRC sh :tangle no systemctl --user enable emacs.service #+END_SRC * Package loading :PROPERTIES: :header-args:emacs-lisp: :tangle "packages.el" :END: This file shouldn't be byte compiled. #+BEGIN_SRC emacs-lisp :tangle "packages.el" ;; -*- no-byte-compile: t; -*- #+END_SRC ** Loading instructions :PROPERTIES: :header-args:emacs-lisp: :tangle no :END: This is where you install packages, by declaring them with the ~package!~ macro, then running ~doom refresh~ on the command line. You'll need to restart Emacs for your changes to take effect! Or at least, run =M-x doom/reload=. WARNING: Don't disable core packages listed in ~~/.emacs.d/core/packages.el~. Doom requires these, and disabling them may have terrible side effects. *** Packages in MELPA/ELPA/emacsmirror To install ~some-package~ from MELPA, ELPA or emacsmirror: #+BEGIN_SRC emacs-lisp (package! some-package) #+END_SRC *** Packages from git repositories To install a package directly from a particular repo, you'll need to specify a ~:recipe~. You'll find documentation on what ~:recipe~ accepts [[https://github.com/raxod502/straight.el#the-recipe-format][here]]: #+BEGIN_SRC emacs-lisp (package! another-package :recipe (:host github :repo "username/repo")) #+END_SRC If the package you are trying to install does not contain a ~PACKAGENAME.el~ file, or is located in a subdirectory of the repo, you'll need to specify ~:files~ in the ~:recipe~: #+BEGIN_SRC emacs-lisp (package! this-package :recipe (:host github :repo "username/repo" :files ("some-file.el" "src/lisp/*.el"))) #+END_SRC *** Disabling built-in packages If you'd like to disable a package included with Doom, for whatever reason, you can do so here with the ~:disable~ property: #+BEGIN_SRC emacs-lisp (package! builtin-package :disable t) #+END_SRC You can override the recipe of a built in package without having to specify all the properties for ~:recipe~. These will inherit the rest of its recipe from Doom or MELPA/ELPA/Emacsmirror: #+BEGIN_SRC emacs-lisp (package! builtin-package :recipe (:nonrecursive t)) (package! builtin-package-2 :recipe (:repo "myfork/package")) #+END_SRC Specify a ~:branch~ to install a package from a particular branch or tag. This is required for some packages whose default branch isn't 'master' (which our package manager can't deal with; see [[https://github.com/raxod502/straight.el/issues/279][raxod502/straight.el#279]]) #+BEGIN_SRC emacs-lisp (package! builtin-package :recipe (:branch "develop")) #+END_SRC ** General packages *** Auto-complete #+BEGIN_SRC emacs-lisp (package! company-tabnine ; tab9 autocomplete :recipe (:host github :repo "TommyX12/company-tabnine" :files ("company-tabnine.el" "fetch-binaries.sh"))) #+END_SRC *** Prettification ~prettify-mode~ is nice and all, but adding substitutions is a little verbose. This helps with that. #+BEGIN_SRC emacs-lisp (package! prettify-utils ; simplify messing with prettify-mode :recipe (:host github :repo "Ilazki/prettify-utils.el")) #+END_SRC *** Window management #+BEGIN_SRC emacs-lisp (package! rotate) #+END_SRC *** Fun Sometimes one just wants a little fun. XKCD comics are fun. #+BEGIN_SRC emacs-lisp (package! xkcd) #+END_SRC Every so often, you want everyone else to /know/ that you're typing, or just to amuse oneself. Introducing: typewriter sounds! #+BEGIN_SRC emacs-lisp (package! selectric-mode) #+END_SRC Hey, let's get the weather in here while we're at it. #+BEGIN_SRC emacs-lisp (package! wttrin) #+END_SRC Why not flash words on the screen. Why not --- hey, it could be fun. #+BEGIN_SRC emacs-lisp (package! spray) #+END_SRC With all our fancy emacs themes, my terminal is missing out! #+BEGIN_SRC emacs-lisp (package! theme-magic) #+END_SRC *** Other **** Flyspell-lazy To alleviate some [[Flyspell][issues with flyspell]] #+BEGIN_SRC emacs-lisp (package! flyspell-lazy) #+END_SRC **** CalcTeX This is a nice extension to ~calc~ #+BEGIN_SRC emacs-lisp (package! calctex :recipe (:host github :repo "johnbcoughlin/calctex" :files ("*.el"))) #+END_SRC **** ESS View dataframes better with #+BEGIN_SRC emacs-lisp (package! ess-view) #+END_SRC ** Language packages *** Systemd For editing systemd unit files #+BEGIN_SRC emacs-lisp (package! systemd) #+END_SRC *** Org Mode Org tables aren't the prettiest thing to look at. This package is supposed to redraw them in the buffer with box-drawing characters. Sounds like an improvement to me! Just need to get it working... #+BEGIN_SRC emacs-lisp (package! org-pretty-table-mode :recipe (:host github :repo "Fuco1/org-pretty-table")) #+END_SRC Because of the /[[https://github.com/commonmark/commonmark-spec/wiki/markdown-flavors][lovely variety in markdown implementations]]/ there isn't actually such a thing a standard table spec ... or standard anything really. Because ~org-md~ is a goody-two-shoes, it just uses HTML for all these non-standardised elements (a lot of them). So ~ox-gfm~ is handy for exporting markdown with all the features that GitHub has. Initialised in [[Exporting to GFM]]. #+BEGIN_SRC emacs-lisp (package! ox-gfm) #+END_SRC Now and then citations need to happen #+BEGIN_SRC emacs-lisp (package! org-ref) #+END_SRC For automatically toggling LaTeX fragment previews there's this nice package #+BEGIN_SRC emacs-lisp (package! org-fragtog) #+END_SRC Came across this and ... it's cool #+BEGIN_SRC emacs-lisp (package! org-graph-view :recipe (:host github :repo "alphapapa/org-graph-view")) #+END_SRC I *need* this in my life. It take a URL to a recipe from a common site, and inserts an org-ified version at point. Isn't that just great. #+BEGIN_SRC emacs-lisp (package! org-chef) #+END_SRC * Package configuration ** Abbrev mode Thanks to [[https://emacs.stackexchange.com/questions/45462/use-a-single-abbrev-table-for-multiple-modes/45476#45476][use a single abbrev-table for multiple modes? - Emacs Stack Exchange]] I have the following. #+BEGIN_SRC emacs-lisp (use-package abbrev :init (setq-default abbrev-mode t) ;; a hook funtion that sets the abbrev-table to org-mode-abbrev-table ;; whenever the major mode is a text mode (defun tec/set-text-mode-abbrev-table () (if (derived-mode-p 'text-mode) (setq local-abbrev-table org-mode-abbrev-table))) :commands abbrev-mode :hook (abbrev-mode . tec/set-text-mode-abbrev-table) :config (setq abbrev-file-name (expand-file-name "abbrev.el" doom-private-dir)) (setq save-abbrevs 'silently)) #+END_SRC ** Centaur Tabs We want to make the tabs a nice, comfy size (~36~), with icons. The modifier marker is nice, but the particular default Unicode one causes a lag spike, so let's just switch to an ~o~, which still looks decent but doesn't cause any issues. A 'active-bar' is nice, so let's have one of those. If we have it ~under~ needs us to turn on ~x-underline-at-decent~ though. For some reason this didn't seem to work inside the ~(after! ... )~ block ¯\_(ツ)_/¯. Then let's change the font to a sans serif, but the default one doesn't fit too well somehow, so let's switch to 'P22 Underground Book'; it looks much nicer. #+BEGIN_SRC emacs-lisp (after! centaur-tabs (setq centaur-tabs-height 36 centaur-tabs-set-icons t centaur-tabs-modified-marker "o" centaur-tabs-close-button "×" centaur-tabs-set-bar 'above) centaur-tabs-gray-out-icons 'buffer (centaur-tabs-change-fonts "P22 Underground Book" 160)) ;; (setq x-underline-at-descent-line t) #+END_SRC ** Company It's nice to have completions almost all the time, in my opinion. Key strokes are just waiting to be saved! #+BEGIN_SRC emacs-lisp (after! company (setq company-idle-delay 0.5 company-minimum-prefix-length 2) (setq company-show-numbers t) (add-hook 'evil-normal-state-entry-hook #'company-abort)) ;; make aborting less annoying. #+END_SRC Now, the improvements from ~precident~ are mostly from remembering history, so let's improve that memory. #+BEGIN_SRC emacs-lisp (setq-default history-length 1000) (setq-default prescient-history-length 1000) #+END_SRC *** Plain Text ~ispell~ is nice, let's have it in ~text~, ~markdown~, and ~GFM~. #+BEGIN_SRC emacs-lisp (set-company-backend! '(text-mode markdown-mode gfm-mode) '(:seperate company-ispell company-files company-yasnippet)) #+END_SRC We then configure the dictionary we're using in [[*ispell][ispell]]. *** ESS ~company-dabbrev-code~ is nice. Let's have it. #+BEGIN_SRC emacs-lisp (set-company-backend! 'ess-r-mode '(company-R-args company-R-objects company-dabbrev-code :separate)) #+END_SRC ** [[https://github.com/zachcurry/emacs-anywhere][Emacs Anywhere]] configuration # NB: install with curl -fsSL https://raw.github.com/zachcurry/emacs-anywhere/master/install | bash It's nice to recognise GitHub (so we can use ~GFM~), and other apps which we know take markdown #+BEGIN_SRC emacs-lisp (defun markdown-window-p (window-title) "Judges from WINDOW-TITLE whether the current window likes markdown" (string-match-p (rx (or "Stack Exchange" "Stack Overflow" "Pull Request" "Issue" "Discord")) window-title)) #+END_SRC When the window opens, we generally want text so let's use a nice sans serif font, a position the window below and to the left. Oh, and don't forget about checking for ~GFM~, otherwise let's just use ~markdown~. #+BEGIN_SRC emacs-lisp (define-minor-mode emacs-anywhere-mode "To tweak the current buffer for some emacs-anywhere considerations" :init-value nil :keymap (list ;; Finish edit, but be smart in org mode (cons (kbd "C-c C-c") (lambda! (if (and (eq major-mode 'org-mode) (org-in-src-block-p)) (org-ctrl-c-ctrl-c) (delete-frame)))) ;; Abort edit. emacs-anywhere saves the current edit for next time. (cons (kbd "C-c C-k") (lambda! (setq ea-on nil) (delete-frame)))) (when emacs-anywhere-mode ;; line breaking (turn-off-auto-fill) (visual-line-mode t) ;; DEL/C-SPC to clear (first keystroke only) (set-transient-map (let ((keymap (make-sparse-keymap))) (define-key keymap (kbd "DEL") (lambda! (delete-region (point-min) (point-max)))) (define-key keymap (kbd "C-SPC") (lambda! (delete-region (point-min) (point-max)))) keymap)) ;; disable tabs (when (bound-and-true-p centaur-tabs-mode) (centaur-tabs-local-mode t)))) (defun ea-popup-handler (app-name window-title x y w h) (interactive) (set-frame-size (selected-frame) 80 12) ;; position the frame near the mouse (let* ((mousepos (split-string (shell-command-to-string "xdotool getmouselocation | sed -E \"s/ screen:0 window:[^ ]*|x:|y://g\""))) (mouse-x (- (string-to-number (nth 0 mousepos)) 100)) (mouse-y (- (string-to-number (nth 1 mousepos)) 50))) (set-frame-position (selected-frame) mouse-x mouse-y)) (set-frame-name (concat "Quick Edit ∷ " ea-app-name " — " (truncate-string-to-width (string-trim (string-trim-right window-title (format "-[A-Za-z0-9 ]*%s" ea-app-name)) "[\s-]+" "[\s-]+") 45 nil nil "…"))) (message "window-title: %s" window-title) ;; set major mode (cond ((markdown-window-p window-title) (gfm-mode)) (t (org-mode)) ; default major mode ) (when (gui-get-selection 'PRIMARY) (insert (gui-get-selection 'PRIMARY))) (evil-insert-state) ; start in insert (emacs-anywhere-mode 1)) (add-hook 'ea-popup-hook 'ea-popup-handler) #+END_SRC ** Flyspell At one point, typing became noticably laggy, Profiling revealed ~flyspell-post-command-hook~ was responsible for 47% of CPU cycles by itself! So I'm going to make use of ~flyspell-lazy~ #+BEGIN_SRC emacs-lisp (after! flyspell (require 'flyspell-lazy) (flyspell-lazy-mode 1)) #+END_SRC ** Tramp Let's try to make tramp handle prompts better #+BEGIN_SRC emacs-lisp (after! tramp (setenv "SHELL" "/bin/bash") (setq tramp-shell-prompt-pattern "\\(?:^\\| \\)[^]#$%>\n]*#?[]#$%>] *\\(\\[[0-9;]*[a-zA-Z] *\\)*")) ;; defult +  #+END_SRC **** Troubleshooting In case the remote shell is misbehaving, here are some things to try ***** Zsh There are some escape code you don't want, let's make it behave more considerately. #+BEGIN_SRC shell :eval no :tangle no if [[ "$TERM" == "dumb" ]]; then unset zle_bracketed_paste unset zle PS1='$ ' return fi #+END_SRC ** Treemacs Quite often there are superfluous files I'm not that interested in. There's no good reason for them to take up space. Let's add a mechanism to ignore them. #+BEGIN_SRC emacs-lisp (after! treemacs (defvar treemacs-file-ignore-extensions '() "File extension which `treemacs-ignore-filter' will ensure are ignored") (defvar treemacs-file-ignore-globs '() "Globs which will are transformed to `treemacs-file-ignore-regexps' which `treemacs-ignore-filter' will ensure are ignored") (defvar treemacs-file-ignore-regexps '() "RegExps to be tested to ignore files, generated from `treeemacs-file-ignore-globs'") (defun treemacs-file-ignore-generate-regexps () "Generate `treemacs-file-ignore-regexps' from `treemacs-file-ignore-globs'" (setq treemacs-file-ignore-regexps (mapcar 'dired-glob-regexp treemacs-file-ignore-globs))) (if (equal treemacs-file-ignore-globs '()) nil (treemacs-file-ignore-generate-regexps)) (defun treemacs-ignore-filter (file full-path) "Ignore files specified by `treemacs-file-ignore-extensions', and `treemacs-file-ignore-regexps'" (or (member (file-name-extension file) treemacs-file-ignore-extensions) (let ((ignore-file nil)) (dolist (regexp treemacs-file-ignore-regexps ignore-file) (setq ignore-file (or ignore-file (if (string-match-p regexp full-path) t nil))))))) (add-to-list 'treemacs-ignored-file-predicates #'treemacs-ignore-filter)) #+END_SRC Now, we just identify the files in question. #+BEGIN_SRC emacs-lisp (setq treemacs-file-ignore-extensions '(;; LaTeX "aux" "ptc" "fdb_latexmk" "fls" "synctex.gz" "toc" ;; LaTeX - glossary "glg" "glo" "gls" "glsdefs" "ist" "acn" "acr" "alg" ;; LaTeX - pgfplots "mw" ;; LaTeX - pdfx "pdfa.xmpi" )) (setq treemacs-file-ignore-globs '(;; LaTeX "*/_minted-*" ;; AucTeX "*/.auctex-auto" "*/_region_.log" "*/_region_.tex")) #+END_SRC ** Miscellaneous *** calc Radians are just better #+BEGIN_SRC emacs-lisp (setq calc-angle-mode 'rad) #+END_SRC *** electric pair mode We want this everywhere #+BEGIN_SRC emacs-lisp (electric-pair-mode t) #+END_SRC *** ispell Let's get a nice big dictionary from [[http://app.aspell.net/create][SCOWL Custom List/Dictionary Creator]] with the following configuration - size :: 80 (huge) - spellings :: British(-ise) and Australian - spellling variants level :: 0 - diacritics :: keep - extra lists :: hacker, roman numerals #+BEGIN_SRC emacs-lisp (setq ispell-dictionary "en_GBs_au_SCOWL_80_0_k_hr") #+END_SRC Oh, and by the way, if ~company-ispell-dictionary~ is ~nil~, then ~ispell-complete-word-dict~ is used instead, which once again when ~nil~ is ~ispell-alternate-dictionary~, which at the moment maps to a plaintext version of the above. It seems reasonable to want to keep an eye on my personal dict, let's have it nearby (also means that if I change the 'main' dictionary I keep my addition). #+BEGIN_SRC emacs-lisp (setq ispell-personal-dictionary (expand-file-name ".hunspell_personal" doom-private-dir)) #+END_SRC *** spray Let's make this suit me slightly better. #+BEGIN_SRC emacs-lisp (setq spray-wpm 500 spray-height 700) #+END_SRC *** theme magic Let's automatically update terminals on theme change #+BEGIN_SRC emacs-lisp (add-hook 'doom-load-theme-hook 'theme-magic-from-emacs) #+END_SRC *** wttrin Set the default city. It's initially ~Taipei~ but I find the IP-locating that's done perfectly acceptable, so let's make that happen. #+BEGIN_SRC emacs-lisp (setq wttrin-default-cities '("")) #+END_SRC * Language configuration *** File Templates For some file types, we overwrite defaults in the [[file:./snippets][snippets]] directory, others need to have a template assigned. #+BEGIN_SRC emacs-lisp (set-file-template! "\\.tex$" :trigger "__" :mode 'latex-mode) #+END_SRC ** Org Mode *** System config Org mode isn't recognised as it's own mime type by default, but that can easily be changed with the following file. For system-wide changes try ~~/usr/share/mime/packages/org.xml~. #+BEGIN_SRC xml :tangle ~/.local/share/mime/packages/org.xml Emacs Org-mode File #+END_SRC What's nice is that Papirus [[https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/commit/a10fb7f2423d5e30b9c4477416ccdc93c4f3849d][now]] has an icon for =text/org=. One simply needs to refresh their mime database #+BEGIN_SRC shell :results silent update-mime-database ~/.local/share/mime #+END_SRC Then set emacs as the default editor #+BEGIN_SRC shell :results silent xdg-mime default emacs.desktop text/org #+END_SRC *** Behaviour **** Tweaking defaults #+BEGIN_SRC emacs-lisp (setq org-directory "~/.org" ; let's put files here org-use-property-inheritance t ; it's convenient to have properties inherited org-log-done 'time ; having the time a item is done sounds convininet org-list-allow-alphabetical t ; have a. A. a) A) list bullets org-export-in-background t ; run export processes in external emacs process org-catch-invisible-edits 'smart) ; try not to accidently do weird stuff in invisible regions #+END_SRC I also like the ~:comments~ header-argument, so let's make that a default. #+BEGIN_SRC emacs-lisp (setq org-babel-default-header-args '((:session . "none") (:results . "replace") (:exports . "code") (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no") (:comments . "link"))) #+END_SRC **** Extra functionality Let's also make creating an org buffer just that little bit easier. #+BEGIN_SRC emacs-lisp (evil-define-command evil-buffer-org-new (count file) "Creates a new ORG buffer replacing the current window, optionally editing a certain FILE" :repeat nil (interactive "P") (if file (evil-edit file) (let ((buffer (generate-new-buffer "*new org*"))) (set-window-buffer nil buffer) (with-current-buffer buffer (org-mode))))) (map! :leader (:prefix "b" :desc "New empty ORG buffer" "o" #'evil-buffer-org-new)) #+END_SRC I think it makes sense to have list bullets change with depth #+BEGIN_SRC emacs-lisp (setq org-list-demote-modify-bullet '(("+" . "-") ("-" . "+") ("*" . "+"))) #+END_SRC Occasionally I want to cite something. #+BEGIN_SRC emacs-lisp (def-package! org-ref :after org :config (setq org-ref-completion-library 'org-ref-ivy-cite)) #+END_SRC It's also nice to be able to use ~cdlatex~. #+BEGIN_SRC emacs-lisp (after! org (add-hook 'org-mode-hook 'turn-on-org-cdlatex)) #+END_SRC At some point in the future it could be good to investigate [[https://scripter.co/splitting-an-org-block-into-two/][splitting org blocks]]. Likewise [[https://archive.casouri.cat/note/2020/insert-math-symbol-in-emacs/][this]] looks good for symbols. My spelling is atrocious, so let's get flycheck going. #+BEGIN_SRC emacs-lisp (after! org (add-hook 'org-mode-hook 'turn-on-flyspell)) #+END_SRC **** Nicer headings Thanks to alphapapa's [[https://github.com/alphapapa/unpackaged.el#export-to-html-with-useful-anchors][unpackaged.el]]. Unfortunately this currently seems to break some of the other modifications I've made. #+BEGIN_SRC emacs-lisp :tangle no (define-minor-mode unpackaged/org-export-html-with-useful-ids-mode "Attempt to export Org as HTML with useful link IDs. Instead of random IDs like \"#orga1b2c3\", use heading titles, made unique when necessary." :global t (if unpackaged/org-export-html-with-useful-ids-mode (advice-add #'org-export-get-reference :override #'unpackaged/org-export-get-reference) (advice-remove #'org-export-get-reference #'unpackaged/org-export-get-reference))) (defun unpackaged/org-export-get-reference (datum info) "Like `org-export-get-reference', except uses heading titles instead of random numbers." (let ((cache (plist-get info :internal-references))) (or (car (rassq datum cache)) (let* ((crossrefs (plist-get info :crossrefs)) (cells (org-export-search-cells datum)) ;; Preserve any pre-existing association between ;; a search cell and a reference, i.e., when some ;; previously published document referenced a location ;; within current file (see ;; `org-publish-resolve-external-link'). ;; ;; However, there is no guarantee that search cells are ;; unique, e.g., there might be duplicate custom ID or ;; two headings with the same title in the file. ;; ;; As a consequence, before re-using any reference to ;; an element or object, we check that it doesn't refer ;; to a previous element or object. (new (or (cl-some (lambda (cell) (let ((stored (cdr (assoc cell crossrefs)))) (when stored (let ((old (org-export-format-reference stored))) (and (not (assoc old cache)) stored))))) cells) (when (org-element-property :raw-value datum) ;; Heading with a title (unpackaged/org-export-new-title-reference datum cache)) ;; NOTE: This probably breaks some Org Export ;; feature, but if it does what I need, fine. (org-export-format-reference (org-export-new-reference cache)))) (reference-string new)) ;; Cache contains both data already associated to ;; a reference and in-use internal references, so as to make ;; unique references. (dolist (cell cells) (push (cons cell new) cache)) ;; Retain a direct association between reference string and ;; DATUM since (1) not every object or element can be given ;; a search cell (2) it permits quick lookup. (push (cons reference-string datum) cache) (plist-put info :internal-references cache) reference-string)))) (defun unpackaged/org-export-new-title-reference (datum cache) "Return new reference for DATUM that is unique in CACHE." (cl-macrolet ((inc-suffixf (place) `(progn (string-match (rx bos (minimal-match (group (1+ anything))) (optional "--" (group (1+ digit))) eos) ,place) ;; HACK: `s1' instead of a gensym. (-let* (((s1 suffix) (list (match-string 1 ,place) (match-string 2 ,place))) (suffix (if suffix (string-to-number suffix) 0))) (setf ,place (format "%s--%s" s1 (cl-incf suffix))))))) (let* ((title (org-element-property :raw-value datum)) (ref (url-hexify-string (substring-no-properties title))) (parent (org-element-property :parent datum))) (while (--any (equal ref (car it)) cache) ;; Title not unique: make it so. (if parent ;; Append ancestor title. (setf title (concat (org-element-property :raw-value parent) "--" title) ref (url-hexify-string (substring-no-properties title)) parent (org-element-property :parent parent)) ;; No more ancestors: add and increment a number. (inc-suffixf ref))) ref))) #+END_SRC **** Nicer ~org-return~ Once again, from [[https://github.com/alphapapa/unpackaged.el#org-return-dwim][unpackaged.el]] #+BEGIN_SRC emacs-lisp (after! org (defun unpackaged/org-element-descendant-of (type element) "Return non-nil if ELEMENT is a descendant of TYPE. TYPE should be an element type, like `item' or `paragraph'. ELEMENT should be a list like that returned by `org-element-context'." ;; MAYBE: Use `org-element-lineage'. (when-let* ((parent (org-element-property :parent element))) (or (eq type (car parent)) (unpackaged/org-element-descendant-of type parent)))) ;;;###autoload (defun unpackaged/org-return-dwim (&optional default) "A helpful replacement for `org-return-indent'. With prefix, call `org-return-indent'. On headings, move point to position after entry content. In lists, insert a new item or end the list, with checkbox if appropriate. In tables, insert a new row or end the table." ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ (interactive "P") (if default (org-return t) (cond ;; Act depending on context around point. ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be ;; followed. ;; ((eq 'link (car (org-element-context))) ;; ;; Link: Open it. ;; (org-open-at-point-global)) ((org-at-heading-p) ;; Heading: Move to position after entry content. ;; NOTE: This is probably the most interesting feature of this function. (let ((heading-start (org-entry-beginning-position))) (goto-char (org-entry-end-position)) (cond ((and (org-at-heading-p) (= heading-start (org-entry-beginning-position))) ;; Entry ends on its heading; add newline after (end-of-line) (insert "\n\n")) (t ;; Entry ends after its heading; back up (forward-line -1) (end-of-line) (when (org-at-heading-p) ;; At the same heading (forward-line) (insert "\n") (forward-line -1)) ;; FIXME: looking-back is supposed to be called with more arguments. (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))))) (insert "\n")) (forward-line -1))))) ((org-at-item-checkbox-p) ;; Checkbox: Insert new item with checkbox. (org-insert-todo-heading nil)) ((org-in-item-p) ;; Plain list. Yes, this gets a little complicated... (let ((context (org-element-context))) (if (or (eq 'plain-list (car context)) ; First item in list (and (eq 'item (car context)) (not (eq (org-element-property :contents-begin context) (org-element-property :contents-end context)))) (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link ;; Non-empty item: Add new item. (org-insert-item) ;; Empty item: Close the list. ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. (delete-region (line-beginning-position) (line-end-position)) (insert "\n")))) ((when (fboundp 'org-inlinetask-in-task-p) (org-inlinetask-in-task-p)) ;; Inline task: Don't insert a new heading. (org-return t)) ((org-at-table-p) (cond ((save-excursion (beginning-of-line) ;; See `org-table-next-field'. (cl-loop with end = (line-end-position) for cell = (org-element-table-cell-parser) always (equal (org-element-property :contents-begin cell) (org-element-property :contents-end cell)) while (re-search-forward "|" end t))) ;; Empty row: end the table. (delete-region (line-beginning-position) (line-end-position)) (org-return t)) (t ;; Non-empty row: call `org-return-indent'. (org-return t)))) (t ;; All other cases: call `org-return-indent'. (org-return t))))) (advice-add #'org-return-indent :override #'unpackaged/org-return-dwim)) #+END_SRC *** Visuals **** In editor ***** Font Display Mixed pitch is great. As is ~+org-pretty-mode~, let's use them. #+BEGIN_SRC emacs-lisp (add-hook! 'org-mode-hook #'+org-pretty-mode #'mixed-pitch-mode) #+END_SRC Earlier I loaded the ~org-pretty-table~ package, let's enable it everywhere! #+BEGIN_SRC emacs-lisp (setq global-org-pretty-table-mode t) #+END_SRC Let's make headings a bit bigger #+BEGIN_SRC emacs-lisp (custom-set-faces! '(outline-1 :weight extra-bold :height 1.2) '(outline-2 :weight bold :height 1.12) '(outline-3 :weight bold :height 1.1) '(outline-4 :weight semi-bold :height 1.08) '(outline-5 :weight semi-bold :height 1.05) '(outline-6 :weight semi-bold :height 1.02) '(outline-8 :weight semi-bold) '(outline-9 :weight semi-bold)) #+END_SRC ***** Symbols It's also nice to change the character used for collapsed items (by default ~…~), I think ~▾~ is better for indicating 'collapsed section'. and add an extra ~org-bullet~ to the default list of four. I've also added some fun alternatives, just commented out. #+BEGIN_SRC emacs-lisp (setq org-ellipsis " ▾ " org-bullets-bullet-list '("◉" "○" "✸" "✿" "✤") ;; org-bullets-bullet-list '("Ⅰ" "Ⅱ" "Ⅲ" "Ⅳ" "Ⅴ" "Ⅵ" "Ⅶ" "Ⅷ" "Ⅸ" "Ⅹ") ) #+END_SRC It's also nice to make use of the unicode characters for check boxes, and other commands. #+BEGIN_SRC emacs-lisp (after! org (appendq! +pretty-code-symbols '(:checkbox "☐" :pending "◼" :checkedbox "☑" :results "🠶" :property "☸" :properties "⚙" :end "∎" :options "⌥" :title "𝙏" :author "𝘼" :date "𝘿" :begin_quote "❮" :end_quote "❯" :em_dash "—")) (set-pretty-symbols! 'org-mode :merge t :checkbox "[ ]" :pending "[-]" :checkedbox "[X]" :results "#+RESULTS:" :property "#+PROPERTY:" :property ":PROPERTIES:" :end ":END:" :options "#+OPTIONS:" :title "#+TITLE:" :author "#+AUTHOR:" :date "#+DATE:" :begin_quote "#+BEGIN_QUOTE" :end_quote "#+END_QUOTE" :em_dash "---") ) (plist-put +pretty-code-symbols :name "⁍") ; or › could be good? #+END_SRC We also like ~org-fragtog~, and that wants a hook. #+BEGIN_SRC emacs-lisp (add-hook 'org-mode-hook 'org-fragtog-mode) #+END_SRC ***** LaTeX Fragments It's nice to customise the look of LaTeX fragments so they fit better in the text --- like this \(\sqrt{\beta^2+3}-\sum_{\phi=1}^\infty \frac{x^\phi-1}{\Gamma(a)}\). Let's start by adding a sans font. #+BEGIN_SRC emacs-lisp (setq org-format-latex-header "\\documentclass{article} \\usepackage[usenames]{color} \\usepackage[T1]{fontenc} \\usepackage{mathtools} \\usepackage{textcomp,amssymb} \\usepackage[makeroom]{cancel} \\pagestyle{empty} % do not remove % The settings below are copied from fullpage.sty \\setlength{\\textwidth}{\\paperwidth} \\addtolength{\\textwidth}{-3cm} \\setlength{\\oddsidemargin}{1.5cm} \\addtolength{\\oddsidemargin}{-2.54cm} \\setlength{\\evensidemargin}{\\oddsidemargin} \\setlength{\\textheight}{\\paperheight} \\addtolength{\\textheight}{-\\headheight} \\addtolength{\\textheight}{-\\headsep} \\addtolength{\\textheight}{-\\footskip} \\addtolength{\\textheight}{-3cm} \\setlength{\\topmargin}{1.5cm} \\addtolength{\\topmargin}{-2.54cm} % my custom stuff \\usepackage{arev} \\usepackage{arevmath}") #+END_SRC We can either render from a ~dvi~ or ~pdf~ file, so let's benchmark ~latex~ and ~pdflatex~. | ~latex~ time | ~pdflatex~ time | |------------+---------------| | 135±2 ms | 215±3 ms | On the rendering side, there are two ~.dvi~-to-image convertors which I am interested in: ~dvipng~ and ~dvisvgm~. Then with the a ~.pdf~ we have ~pdf2svg~. For inline preview we care about speed, while for exporting we care about file size and preffer a vector graphic. Using the above latex expression and benchmarking lead to the following results: | ~dvipng~ time | ~dvisvgm~ time | ~pdf2svg~ time | |-------------+--------------+--------------| | 89±2 ms | 178±2 ms | 12±2 ms | Now let's combine this to see what's best | Tool chain | Total time | Resultant file size | |--------------------+------------+---------------------| | ~latex~ + ~dvipng~ | 226±2 ms | 7 KiB | | ~latex~ + ~dvisvgm~ | 392±4 ms | 8 KiB | | ~pdflatex~ + ~pdf2svg~ | 230±2 ms | 16 KiB | So, let's use ~dvipng~ for previewing LaTeX fragments in-emacs, but ~dvisvgm~ for [[ Exporting to HTML][LaTeX Rendering]]. /Unfortunately: it seems that svg sizing is annoying ATM, so let's actually not do this right now./ As well as having a sans font, there are a few other tweaks which can make them look better. Namely making sure that the colours switch when the theme does. #+BEGIN_SRC emacs-lisp (after! org ;; make background of fragments transparent ;; (let ((dvipng--plist (alist-get 'dvipng org-preview-latex-process-alist))) ;; (plist-put dvipng--plist :use-xcolor t) ;; (plist-put dvipng--plist :image-converter '("dvipng -D %D -bg 'transparent' -T tight -o %O %f"))) (add-hook! 'doom-load-theme-hook (defun +org-refresh-latex-background () (plist-put! org-format-latex-options :background (face-attribute (or (cadr (assq 'default face-remapping-alist)) 'default) :background nil t)))) ) #+END_SRC It'd be nice to make ~mhchem~ equations able to be rendered. NB: This doesn't work at the moment. #+BEGIN_SRC emacs-lisp (after! org (add-to-list 'org-latex-regexps '("\\ce" "^\\\\ce{\\(?:[^\000{}]\\|{[^\000}]+?}\\)}" 0 nil))) #+END_SRC ***** Stolen from [[https://github.com/jkitchin/scimax][scimax]] (semi-working right now) I want fragment justification #+BEGIN_SRC emacs-lisp (after! org (defun scimax-org-latex-fragment-justify (justification) "Justify the latex fragment at point with JUSTIFICATION. JUSTIFICATION is a symbol for 'left, 'center or 'right." (interactive (list (intern-soft (completing-read "Justification (left): " '(left center right) nil t nil nil 'left)))) (let* ((ov (ov-at)) (beg (ov-beg ov)) (end (ov-end ov)) (shift (- beg (line-beginning-position))) (img (overlay-get ov 'display)) (img (and (and img (consp img) (eq (car img) 'image) (image-type-available-p (plist-get (cdr img) :type))) img)) space-left offset) (when (and img ;; This means the equation is at the start of the line (= beg (line-beginning-position)) (or (string= "" (s-trim (buffer-substring end (line-end-position)))) (eq 'latex-environment (car (org-element-context))))) (setq space-left (- (window-max-chars-per-line) (car (image-size img))) offset (floor (cond ((eq justification 'center) (- (/ space-left 2) shift)) ((eq justification 'right) (- space-left shift)) (t 0)))) (when (>= offset 0) (overlay-put ov 'before-string (make-string offset ?\ )))))) (defun scimax-org-latex-fragment-justify-advice (beg end image imagetype) "After advice function to justify fragments." (scimax-org-latex-fragment-justify (or (plist-get org-format-latex-options :justify) 'left))) (defun scimax-toggle-latex-fragment-justification () "Toggle if LaTeX fragment justification options can be used." (interactive) (if (not (get 'scimax-org-latex-fragment-justify-advice 'enabled)) (progn (advice-add 'org--format-latex-make-overlay :after 'scimax-org-latex-fragment-justify-advice) (put 'scimax-org-latex-fragment-justify-advice 'enabled t) (message "Latex fragment justification enabled")) (advice-remove 'org--format-latex-make-overlay 'scimax-org-latex-fragment-justify-advice) (put 'scimax-org-latex-fragment-justify-advice 'enabled nil) (message "Latex fragment justification disabled")))) #+END_SRC There's also this lovely equation numbering stuff I'll nick #+BEGIN_SRC emacs-lisp ;; Numbered equations all have (1) as the number for fragments with vanilla ;; org-mode. This code injects the correct numbers into the previews so they ;; look good. (after! org (defun scimax-org-renumber-environment (orig-func &rest args) "A function to inject numbers in LaTeX fragment previews." (let ((results '()) (counter -1) (numberp)) (setq results (loop for (begin . env) in (org-element-map (org-element-parse-buffer) 'latex-environment (lambda (env) (cons (org-element-property :begin env) (org-element-property :value env)))) collect (cond ((and (string-match "\\\\begin{equation}" env) (not (string-match "\\\\tag{" env))) (incf counter) (cons begin counter)) ((string-match "\\\\begin{align}" env) (prog2 (incf counter) (cons begin counter) (with-temp-buffer (insert env) (goto-char (point-min)) ;; \\ is used for a new line. Each one leads to a number (incf counter (count-matches "\\\\$")) ;; unless there are nonumbers. (goto-char (point-min)) (decf counter (count-matches "\\nonumber"))))) (t (cons begin nil))))) (when (setq numberp (cdr (assoc (point) results))) (setf (car args) (concat (format "\\setcounter{equation}{%s}\n" numberp) (car args))))) (apply orig-func args)) (defun scimax-toggle-latex-equation-numbering () "Toggle whether LaTeX fragments are numbered." (interactive) (if (not (get 'scimax-org-renumber-environment 'enabled)) (progn (advice-add 'org-create-formula-image :around #'scimax-org-renumber-environment) (put 'scimax-org-renumber-environment 'enabled t) (message "Latex numbering enabled")) (advice-remove 'org-create-formula-image #'scimax-org-renumber-environment) (put 'scimax-org-renumber-environment 'enabled nil) (message "Latex numbering disabled."))) (advice-add 'org-create-formula-image :around #'scimax-org-renumber-environment) (put 'scimax-org-renumber-environment 'enabled t)) #+END_SRC **** Exporting (general) #+BEGIN_SRC emacs-lisp (after! org (setq org-export-headline-levels 5)) ; I like nesting #+END_SRC **** Exporting to HTML ***** Custom CSS/JS There is a fantastic exporter config ([[https://github.com/fniessen/org-html-themes][fniessen/org-html-themes]]) which we can setup to be used with all our org files. Since most of the syntax highlighting colours from our [[Theme]] gets used, we benefit from customising the code block style. #+NAME: orgHtmlStyle #+BEGIN_SRC web :exports none :tangle no #+END_SRC #+NAME: orgHtmlScript We also want to make the background and foreground colours of the ~
~ blocks
match out theme (they don't by default), so I scraped some code from ~emacs.stackexchange~.
#+BEGIN_SRC emacs-lisp :noweb yes
(defun my-org-inline-css-hook (exporter)
  "Insert custom inline css to automatically set the
   background of code to whatever theme I'm using's background"
  (when (eq exporter 'html)
      (setq
       org-html-head-extra
       (concat
        org-html-head-extra
        (format "
"
       (doom-color 'bg)
       (doom-color 'bg-alt)
       (doom-color 'base0)
       (doom-color 'base1)
       (doom-color 'base2)
       (doom-color 'base3)
       (doom-color 'base4)
       (doom-color 'base5)
       (doom-color 'base6)
       (doom-color 'base7)
       (doom-color 'base8)
       (doom-color 'fg)
       (doom-color 'fg-alt)
       (doom-color 'grey)
       (doom-color 'red)
       (doom-color 'orange)
       (doom-color 'green)
       (doom-color 'teal)
       (doom-color 'yellow)
       (doom-color 'blue)
       (doom-color 'dark-blue)
       (doom-color 'magenta)
       (doom-color 'violet)
       (doom-color 'cyan)
       (doom-color 'dark-cyan))
        "
<>
"
        ))))

(add-hook 'org-export-before-processing-hook 'my-org-inline-css-hook)
#+END_SRC
***** Make verbatim different to code
Since we have =verbatim= and ~code~, let's use =verbatim= for key strokes.
#+BEGIN_SRC emacs-lisp
(setq org-html-text-markup-alist
      '((bold . "%s")
        (code . "%s")
        (italic . "%s")
        (strike-through . "%s")
        (underline . "%s")
        (verbatim . "%s")))
#+END_SRC
***** Change checkbox type
We also want to use HTML checkboxes, however we want to get a bit fancier than default
#+BEGIN_SRC emacs-lisp
(after! org
(appendq! org-html-checkbox-types '((html-span .
	  ((on . "")
	  (off . "")
	  (trans . "")))))
(setq org-html-checkbox-type 'html-span))
#+END_SRC
- [ ] I'm yet to do this
- [-] Work in progress
- [X] This is done
***** LaTeX Rendering
On the maths side of things, I consider ~dvisvgm~ to be a rather compelling
option. However this isn't sized very well at the moment.
#+BEGIN_SRC emacs-lisp
;; (setq-default org-html-with-latex `dvisvgm)
#+END_SRC
**** Exporting to LaTeX
I like automatically using spaced small caps for acronyms. For strings I want to
be unaffected lest's use ~;~ as a prefix to prevent the transformation --- i.e.
~;JFK~ (as one would want for two-letter geographic locations and names).
#+BEGIN_SRC emacs-lisp
;; TODO make this /only/ apply to text (i.e. not URL)
(after! org
  (defun tec/org-export-latex-filter-acronym (text backend info)
    (when (org-export-derived-backend-p backend 'latex)
      (let ((case-fold-search nil))
        (replace-regexp-in-string
         ";?\\b[A-Z][A-Z]+s?"
         (lambda (all-caps-str)
           ; only \acr if str doesn't start with ";"
           (if (equal (aref all-caps-str 0) 59) (substring all-caps-str 1)
             (if (equal (aref all-caps-str (- (length all-caps-str) 1)) ?s)
                 (concat "\\textls*[70]{\\textsc{" (s-downcase (substring all-caps-str 0 -1)) "}\\protect\\scalebox{.91}[.84]{s}}")
               (concat "\\textls*[70]{\\textsc{" (s-downcase all-caps-str) "}}"))))
         text t t))))

  (add-to-list 'org-export-filter-plain-text-functions
               'tec/org-export-latex-filter-acronym)
  (add-to-list 'org-export-filter-headline-functions
               'tec/org-export-latex-filter-acronym))
#+END_SRC

Now for a few more adjustments.
#+BEGIN_SRC emacs-lisp
(after! ox-latex
  (add-to-list 'org-latex-classes
               '("fancy-article"
               "\\documentclass{scrartcl}\n\
\\usepackage[T1]{fontenc}\n\
\\usepackage[osf,largesc,helvratio=0.9]{newpxtext}\n\
\\usepackage[scale=0.92]{sourcecodepro}\n\
\\usepackage[varbb]{newpxmath}\n\
\\usepackage[activate={true,nocompatibility},final,tracking=true,kerning=true,spacing=true,factor=2000]{microtype}\n\
\\usepackage{xcolor}\n\
\\setlength{\\parskip}{\\baselineskip}\n\
\\setlength{\\parindent}{0pt}"
               ("\\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
               '("blank"
               "[NO-DEFAULT-PACKAGES]
               [NO-PACKAGES]
               [EXTRA]"
               ("\\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
               '("bmc-article"
               "\\documentclass[article,code,maths]{bmc}
               [NO-DEFAULT-PACKAGES]
               [NO-PACKAGES]
               [EXTRA]"
               ("\\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
               '("bmc"
               "\\documentclass[code,maths]{bmc}
               [NO-DEFAULT-PACKAGES]
               [NO-PACKAGES]
               [EXTRA]"
               ("\\chapter{%s}" . "\\chapter*{%s}")
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\paragraph{%s}" . "\\paragraph*{%s}")
               ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
  (setq org-latex-default-class "fancy-article")

  (add-to-list 'org-latex-packages-alist '("" "minted"))
  (setq org-latex-listings 'minted)
  (setq org-latex-minted-options
        '(("frame" "lines")
          ("fontsize" "\\scriptsize")
          ("linenos" "")
          ("breakanywhere" "true")
          ("breakautoindent" "true")
          ("breaklines" "true")
          ("autogobble" "true")
          ("obeytabs" "true")
          ("python3" "true")
          ("breakbefore" "\\\\\\.+")
          ("breakafter" "\\,")
          ("style" "autumn")
          ("breaksymbol" "\\tiny\\ensuremath{\\hookrightarrow}")
          ("breakanywheresymbolpre" "\\,\\footnotesize\\ensuremath{{}_{\\rfloor}}")
          ("breakbeforesymbolpre" "\\,\\footnotesize\\ensuremath{{}_{\\rfloor}}")
          ("breakaftersymbolpre" "\\,\\footnotesize\\ensuremath{{}_{\\rfloor}}")))

  (setq org-latex-hyperref-template "\\hypersetup{
  pdfauthor={%a},
  pdftitle={%t},
  pdfkeywords={%k},
  pdfsubject={%d},
  pdfcreator={%c},
  pdflang={%L},
  breaklinks=true,
  colorlinks=true,
  linkcolor=,
  urlcolor=blue!70!green,
  citecolor=green!60!blue\n}
\\urlstyle{same}\n")
  (setq org-latex-pdf-process
        '("latexmk -shell-escape -interaction=nonstopmode -f -pdf -output-directory=%o %f")))
#+END_SRC
**** Exporting to Beamer
It's nice to use a different theme
#+BEGIN_SRC emacs-lisp
(setq org-beamer-theme "[progressbar=foot]metropolis")
#+END_SRC
Then customise it a bit
#+BEGIN_SRC emacs-lisp

#+END_SRC
And I think that it's natural to divide a presentation into sections, e.g.
Introduction, Overview... so let's set bump up the headline level that becomes a
frame from ~1~ to ~2~.
#+BEGIN_SRC emacs-lisp
(setq org-beamer-frame-level 2)
#+END_SRC
**** Exporting to GFM
We just need to load ~ox-gfm~ for org-mode documents
#+BEGIN_SRC emacs-lisp
(eval-after-load "org"
  '(require 'ox-gfm nil t))
#+END_SRC
*** Babel
Doom lazy-loads babel languages, with is lovely.

We need to tell babel to use python3. Who uses python2 anymore anyway? And why
doesn't ~python~ refer to the latest version!?
#+BEGIN_SRC emacs-lisp
(setq org-babel-python-command "python3")
#+END_SRC
We also like autocompletion here
#+BEGIN_SRC emacs-lisp
(defun tec-org-python ()
  (if (eq major-mode 'python-mode)
   (progn (anaconda-mode t)
          (company-mode t)))
  )
(add-hook 'org-src-mode-hook 'tec-org-python)
#+END_SRC
*** ESS
We don't want ~R~ evaluation to hang the editor, hence
#+BEGIN_SRC emacs-lisp
(setq ess-eval-visibly 'nowait)
#+END_SRC
Syntax highlighting is nice, so let's turn all of that on
#+BEGIN_SRC emacs-lisp
(setq ess-R-font-lock-keywords '((ess-R-fl-keyword:keywords . t)
 (ess-R-fl-keyword:constants . t)
 (ess-R-fl-keyword:modifiers . t)
 (ess-R-fl-keyword:fun-defs . t)
 (ess-R-fl-keyword:assign-ops . t)
 (ess-R-fl-keyword:%op% . t)
 (ess-fl-keyword:fun-calls . t)
 (ess-fl-keyword:numbers . t)
 (ess-fl-keyword:operators . t)
 (ess-fl-keyword:delimiters . t)
 (ess-fl-keyword:= . t)
 (ess-R-fl-keyword:F&T . t)))
#+END_SRC
** LaTeX
*** To-be-implemented ideas
- Paste image from clipboard
  + Determine first folder in ~graphicspath~ if applicable
  + Ask for file name
  + Use ~xclip~ to save file to graphics folder, or current directory (whichever applies)
    #+BEGIN_SRC shell :eval no
command -v xclip >/dev/null 2>&1 || { echo >&1 "no xclip"; exit 1; }

if
xclip -selection clipboard -target image/png -o >/dev/null 2>&1
then
xclip -selection clipboard -target image/png -o >$1 2>/dev/null
echo $1
else
echo "no image"
fi
#+END_SRC
  + Insert figure, with filled in details as a result (activate =yasnippet= with
    filename as variable maybe?)
*** Snippet value
For use in the new-file template, let's set out a nice preamble we may want to use.
#+NAME: latex-nice-preable
#+BEGIN_SRC latex :tangle no
\\usepackage[pdfa,unicode=true,hidelinks]{hyperref}

\\usepackage[dvipsnames,svgnames,table,hyperref]{xcolor}
\\renewcommand{\\UrlFont}{\\ttfamily\\small}

\\usepackage[a-2b]{pdfx} % why not be archival

\\usepackage[T1]{fontenc}
\\usepackage[osf,helvratio=0.9]{newpxtext} % pallatino
\\usepackage[scale=0.92]{sourcecodepro}

\\usepackage[varbb]{newpxmath}
\\usepackage{mathtools}
\\usepackage{amssymb}

\\usepackage[activate={true,nocompatibility},final,tracking=true,kerning=true,spacing=true,factor=2000]{microtype}
% microtype makes text look nicer

\\usepackage{graphicx} % include graphics
\\usepackage{grffile} % fix allowed graphicx filenames

\\usepackage{booktabs} % nice table rules
#+END_SRC
Then let's bind the content to a function, and define some nice helpers.
#+BEGIN_SRC emacs-lisp :noweb yes
(setq tec/yas-latex-template-preamble "
<>
")

(defun tec/yas-latex-get-class-choice ()
  "Prompt user for LaTeX class choice"
  (setq tec/yas-latex-class-choice (ivy-read "Select document class: " '("article" "scrartcl" "bmc") :def "bmc")))

(defun tec/yas-latex-preamble-if ()
  "Based on class choice prompt for insertion of default preamble"
    (if (equal tec/yas-latex-class-choice "bmc") 'nil
             (eq (read-char-choice "Include default preamble? [Type y/n]" '(?y ?n)) ?y)))
#+END_SRC
*** Editor visuals
Once again, /all hail mixed pitch mode!/
#+BEGIN_SRC emacs-lisp
(add-hook 'LaTeX-mode-hook #'mixed-pitch-mode)
#+END_SRC

Let's enhance ~TeX-fold-math~ a bit
#+BEGIN_SRC emacs-lisp
(setq TeX-fold-math-spec-list
      '(;; missing/better symbols
        ("≤" ("le"))
        ("≥" ("ge"))
        ("≠" ("ne"))
        ("★" ("star"))
        ;; conviniance shorts
        ("‹" ("left"))
        ("›" ("right"))
        ;; private macros
        ("ℝ" ("RR"))
        ("ℕ" ("NN"))
        ("ℤ" ("ZZ"))
        ("ℚ" ("QQ"))
        ("ℂ" ("CC"))
        ("ℙ" ("PP"))
        ("ℍ" ("HH"))
        ("𝔼" ("EE"))
        ("𝑑" ("dd"))
        ;; known commands
        ("" ("phantom"))
        ("({1}⧸{2})" ("frac"))
        ("‘{1}’" ("text"))
        ;; private commands
        ("|{1}|" ("abs"))
        ("‖{1}‖" ("norm"))
        ("⌊{1}⌋" ("floor"))
        ("⌈{1}⌉" ("ceil"))
        ("⌊{1}⌉" ("round"))
        ("⭡{1}" ("vec"))
        ("𝑑{1}⧸𝑑{2}" ("dv"))
        ("∂{1}⧸∂{2}" ("pdv"))
        )
      TeX-fold-macro-spec-list
      '(
        ;; as the defaults
        ("[f]" ("footnote" "marginpar"))
        ("[c]" ("cite"))
        ("[l]" ("label"))
        ("[r]" ("ref" "pageref" "eqref"))
        ("[i]" ("index" "glossary"))
        ("..." ("dots"))
        ("{1}" ("emph" "textit" "textsl" "textmd" "textrm" "textsf" "texttt"
            "textbf" "textsc" "textup"))
        ;; tweaked defaults
        ("©" ("copyright"))
        ("®" ("textregistered"))
        ("™"  ("texttrademark"))
        ("[1]:||►" ("item"))
        ("❡❡ {1}" ("part" "part*"))
        ("❡ {1}" ("chapter" "chapter*"))
        ("§ {1}" ("section" "section*"))
        ("§§ {1}" ("subsection" "subsection*"))
        ("§§§ {1}" ("subsubsection" "subsubsection*"))
        ("¶ {1}" ("paragraph" "paragraph*"))
        ("¶¶ {1}" ("subparagraph" "subparagraph*"))
        ;; extra
        ("⬖ {1}" ("begin"))
        ("⬗ {1}" ("end"))
        ))
#+END_SRC

Let's just make the folding a little less manual
#+BEGIN_SRC emacs-lisp
(defvar +latex-use-TeX-fold t
  "Use TeX fold in TeX-mode.
When set to non-nil, this adds a few hooks/advices to fold stuff.")

;; Fold after cdlatex and snippets.
(defun +TeX-fold-line-ah (&rest _)
  "Auto-fold LaTeX macros after functions that typically insert them."
  (TeX-fold-region (line-beginning-position) (line-end-position)))

(when +latex-use-TeX-fold
  (advice-add #'cdlatex-math-symbol :after #'+TeX-fold-line-ah)
  (advice-add #'cdlatex-math-modify :after #'+TeX-fold-line-ah)
  ;; local after-snippet hook for folding
  (add-hook! 'TeX-mode-hook
    (add-hook 'yas-after-exit-snippet-hook #'+TeX-fold-line-ah nil t))
  ;; TeX-fold messes up the font face a bit too much, so
  (add-hook! 'mixed-pitch-mode-hook
    (when mixed-pitch-mode
      (let ((var-pitch (face-attribute 'variable-pitch :family))
            (var-height (face-attribute 'variable-pitch :height)))
        (add-to-list 'mixed-pitch-fixed-cookie
                     (face-remap-add-relative
                      'TeX-fold-folded-face :family var-pitch :height var-height))))))
#+END_SRC

Some local keybindings to make life a bit easier
#+BEGIN_SRC emacs-lisp
(after! tex
  (map!
   :map LaTeX-mode-map
   :ei [C-return] #'LaTeX-insert-item

   ;; normal stuff here
   :localleader
   :desc "View" "v" #'TeX-view
   (:when +latex-use-TeX-fold
     :desc "Fold paragraph"     "f"   #'TeX-fold-paragraph
     :desc "Unfold paragraph"   "C-f" #'TeX-fold-clearout-paragraph
     :desc "Fold buffer"        "F"   #'TeX-fold-buffer
     :desc "Unfold buffer"      "C-F" #'TeX-fold-clearout-buffer))
  (setq TeX-electric-math '("\\(" . "")))
#+END_SRC

Maths deliminators can be de-emphasised a bit
#+BEGIN_SRC emacs-lisp
;; Making \( \) less visible
(defface unimportant-latex-face
  '((t
     :inherit font-lock-comment-face :family "Overpass" :weight light))
  "Face used to make \\(\\), \\[\\] less visible."
  :group 'LaTeX-math)

(font-lock-add-keywords
 'latex-mode
 `((,(rx (and "\\" (any "()[]"))) 0 'unimportant-latex-face prepend))
 'end)

(font-lock-add-keywords
 'latex-mode
 `((,"\\\\[[:word:]]+" 0 'font-lock-keyword-face prepend))
 'end)
#+END_SRC

And enable shell escape for the preview
#+BEGIN_SRC emacs-lisp
(setq preview-LaTeX-command '("%`%l \"\\nonstopmode\\nofiles\
\\PassOptionsToPackage{" ("," . preview-required-option-list) "}{preview}\
\\AtBeginDocument{\\ifx\\ifPreview\\undefined"
preview-default-preamble "\\fi}\"%' \"\\detokenize{\" %t \"}\""))
#+END_SRC
*** CDLaTeX
The symbols and modifies are very nice by default, but could do with a bit of
fleshing out. Let's change the prefix to a key which is similarly rarely used,
but more convinient, like =;=.
#+BEGIN_SRC emacs-lisp
(after! cdlatex
  (setq ;; cdlatex-math-symbol-prefix ?\; ;; doesn't work at the moment :(
   cdlatex-math-symbol-alist
   '( ;; adding missing functions to 3rd level symbols
     (?_    ("\\downarrow"  ""           "\\inf"))
     (?^    ("\\uparrow"    ""           "\\sup"))
     (?k    ("\\kappa"      ""           "\\ker"))
     (?m    ("\\mu"         ""           "\\lim"))
     (?c    (""             "\\circ"     "\\cos"))
     (?d    ("\\delta"      "\\partial"  "\\dim"))
     (?D    ("\\Delta"      "\\nabla"    "\\deg"))
     ;; no idea why \Phi isnt on 'F' in first place, \phi is on 'f'.
     (?F    ("\\Phi"))
     ;; now just conveniance
     (?.    ("\\cdot" "\\dots"))
     (?:    ("\\vdots" "\\ddots"))
     (?*    ("\\times" "\\star" "\\ast")))
   cdlatex-math-modify-alist
   '( ;; my own stuff
     (?B    "\\mathbb"        nil          t    nil  nil)
     (?a    "\\abs"           nil          t    nil  nil))))
#+END_SRC
** R
*** Editor Visuals
#+BEGIN_SRC emacs-lisp
(after! ess-r-mode
  (appendq! +pretty-code-symbols
            '(:assign "⟵"
              :multiply "×"))
  (set-pretty-symbols! 'ess-r-mode
    ;; Functional
    :def "function"
    ;; Types
    :null "NULL"
    :true "TRUE"
    :false "FALSE"
    :int "int"
    :floar "float"
    :bool "bool"
    ;; Flow
    :not "!"
    :and "&&" :or "||"
    :for "for"
    :in "%in%"
    :return "return"
    ;; Other
    :assign "<-"
    :multiply "%*%"))
#+END_SRC
** hledger
~ledger-mode~ is great and all, but ~hledger~ seems to be more actively maintained.
For example, from 2018--2020, the most prolific contributor to ~ledger~ produced
31 commits. For ~hledger~ this statistic is 1800 commits. In addition, over the
last decade, ~ledger~ seems to have lost steam, while ~hledger~ seems as actively
developed as ever. From this basic comparison ~hledger~ looks to have a more
promising outlook. It also has a few extra nicities that ~ledger~ doesn't, but is
a little slower (~haskell~ vs. ~c++~).
Since this uses the same format, and ~ledger-mode~ is well integrated into emacs,
and produced by John Wiegley --- author of ~ledger~ and current Emacs maintainer
--- using this seems like a good idea. Thankfully we can, with a little modification.
#+BEGIN_SRC emacs-lisp
(setq ledger-mode-should-check-version nil
      ledger-report-links-in-register nil
      ledger-binary-path "hledger")
#+END_SRC
** Markdown
Let's use mixed pitch, because it's great
#+BEGIN_SRC emacs-lisp
(add-hook! (gfm-mode markdown-mode) #'mixed-pitch-mode)
#+END_SRC
Most of the time when I write markdown, it's going into some app/website which
will do it's own line wrapping, hence we /only/ want to use visual line wrapping. No hard stuff.
#+BEGIN_SRC emacs-lisp
(add-hook! (gfm-mode markdown-mode) #'visual-line-mode #'turn-off-auto-fill)
#+END_SRC
** Beancount
The [[https://bitbucket.org/blais/beancount/src/tip/editors/emacs/beancount.el][beancount package]] online has been put into ~./lisp~, we just need to load and
enable it for ~.beancount~ files.
#+BEGIN_SRC emacs-lisp
(use-package! beancount
  :load-path "~/.config/doom/lisp"
  :mode ("\\.beancount\\'" . beancount-mode)
  :config
  (setq beancount-electric-currency t)
  (defun beancount-bal ()
    "Run bean-report bal."
    (interactive)
    (let ((compilation-read-command nil))
      (beancount--run "bean-report"
                      (file-relative-name buffer-file-name) "bal")))
  ;; TODO make the following *work*
  :bind (:map beancount-mode-map ("S-RET" . #'beancount-align-to-previous-number)))
#+END_SRC

# Local variables:
# eval: (add-hook 'after-save-hook 'org-html-export-to-html t t)
# end: