#+TITLE: Doom Emacs Configuration
#+AUTHOR: tecosaur
#+PROPERTY: header-args:emacs-lisp :tangle yes :cache yes :results silent
#+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
*** 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"
(or (string-match-p "Pull Request" window-title)
(string-match-p "Issue" window-title)
(string-match-p "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
;; I'm used to C-c C-c for 'completed the thing' so add that
;; :keymap '((kbd "C-c C-c") . (lambda () (interactive) (if (equal major-mode "org-mode") (if (org-in-src-block-p) (org-ctrl-c-ctrl-c) (delete-frame)) (delete-frame))))
(when emacs-anywhere-mode
;; line breaking
(turn-off-auto-fill)
(visual-line-mode t)
;; disable tabs
(when (centaur-tabs-mode) (centaur-tabs-local-mode t)))
)
(defun ea-popup-handler (app-name window-title x y w h)
(set-frame-size (selected-frame) 80 12)
(interactive)
;; position
(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 "…")))
;; 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")
(setq company-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.
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 automaticly 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
*** LSP
For some reason I'de been having an issue with loading ~lsp-ui~. I don't know why
this is needed, but it seems to fix things.
#+BEGIN_SRC emacs-lisp
(defvar lsp-ui-doc-winum-ignore nil)
#+END_SRC
Without this I get ~File mode specification error: (void-variable lsp-ui-doc-winum-ignore)~.
* 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
*** 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 ~