+++
title = "Emacs config"
author = ["Pavel"]
draft = false
+++
> One day we won't hate one another, no young boy will march to war and I will clean up my Emacs config. But that day isn't today.
My [Emacs](https://www.gnu.org/software/emacs/) configuration.
As with other files in the repo, parts prefixed with (OFF) are not used but kept for historic purposes.
Table of Contents
- [Primary setup](#primary-setup)
- [Measure startup speed](#measure-startup-speed)
- [straight.el](#straight-dot-el)
- [use-package](#use-package)
- [Performance](#performance)
- [Garbage collection](#garbage-collection)
- [Run garbage collection when Emacs is unfocused](#run-garbage-collection-when-emacs-is-unfocused)
- [Misc](#misc)
- [Native compilation](#native-compilation)
- [Anaconda & environment](#anaconda-and-environment)
- [Custom file location](#custom-file-location)
- [Private config](#private-config)
- [No littering](#no-littering)
- [Global editing configuration](#global-editing-configuration)
- [General keybindings stuff](#general-keybindings-stuff)
- [general.el](#general-dot-el)
- [which-key](#which-key)
- [dump keybindings](#dump-keybindings)
- [Evil mode](#evil-mode)
- [evil](#evil)
- [Addons](#addons)
- [evil-collection](#evil-collection)
- [More keybindings](#more-keybindings)
- [Escape key](#escape-key)
- [Home & end](#home-and-end)
- [My leader](#my-leader)
- [Universal argument](#universal-argument)
- [Profiler](#profiler)
- [Buffer switching](#buffer-switching)
- [Buffer management](#buffer-management)
- [xref](#xref)
- [Folding](#folding)
- [Zoom](#zoom)
- [Editing helpers](#editing-helpers)
- [Visual fill column mode](#visual-fill-column-mode)
- [smartparens](#smartparens)
- [Aggressive Indent](#aggressive-indent)
- [Delete trailing whitespace](#delete-trailing-whitespace)
- [Expand region](#expand-region)
- [Various settings](#various-settings)
- [Tabs](#tabs)
- [Scrolling config](#scrolling-config)
- [Clipboard](#clipboard)
- [Backups](#backups)
- [Undo Tree](#undo-tree)
- [Help](#help)
- [Ivy, counsel, swiper](#ivy-counsel-swiper)
- [ivy-rich](#ivy-rich)
- [prescient](#prescient)
- [Keybindings](#keybindings)
- [
OFF (OFF) Helm](#off--helm)
- [Treemacs](#treemacs)
- [Helper functions](#helper-functions)
- [Custom icons](#custom-icons)
- [Projectile](#projectile)
- [Company](#company)
- [Git & Magit](#git-and-magit)
- [Editorconfig](#editorconfig)
- [Snippets](#snippets)
- [Time trackers](#time-trackers)
- [WakaTime](#wakatime)
- [Fixes](#fixes)
- [ActivityWatch](#activitywatch)
- [UI](#ui)
- [General UI & GUI Settings](#general-ui-and-gui-settings)
- [Theme & global stuff](#theme-and-global-stuff)
- [Custom theme](#custom-theme)
- [Font](#font)
- [Custom frame title](#custom-frame-title)
- [perspective.el](#perspective-dot-el)
- [Some functions](#some-functions)
- [
OFF (OFF) Tab bar](#off--tab-bar)
- [Setup](#setup)
- [My title](#my-title)
- [Modeline](#modeline)
- [Font stuff](#font-stuff)
- [Emojis](#emojis)
- [Ligatures](#ligatures)
- [Icons](#icons)
- [Highlight todo](#highlight-todo)
- [Text highlight improvements](#text-highlight-improvements)
- [Dired](#dired)
- [Basic config & keybindings](#basic-config-and-keybindings)
- [Addons](#addons)
- [Subdirectories](#subdirectories)
- [TRAMP](#tramp)
- [Bookmarks](#bookmarks)
- [Shells](#shells)
- [vterm](#vterm)
- [Configuration](#configuration)
- [Subterminal](#subterminal)
- [Dired integration](#dired-integration)
- [Eshell](#eshell)
- [Org Mode](#org-mode)
- [Installation & basic settings](#installation-and-basic-settings)
- [Encryption](#encryption)
- [org-contrib](#org-contrib)
- [Integration with evil](#integration-with-evil)
- [Literate programing](#literate-programing)
- [Python & Jupyter](#python-and-jupyter)
- [Hy](#hy)
- [View HTML in browser](#view-html-in-browser)
- [PlantUML](#plantuml)
- [Setup](#setup)
- [Managing Jupyter kernels](#managing-jupyter-kernels)
- [Do not wrap the output in emacs-jupyter](#do-not-wrap-the-output-in-emacs-jupyter)
- [Wrap source code output](#wrap-source-code-output)
- [Productivity & Knowledge management](#productivity-and-knowledge-management)
- [Capture templates & various settings](#capture-templates-and-various-settings)
- [Custom agendas](#custom-agendas)
- [Org Journal](#org-journal)
- [Org Roam](#org-roam)
- [org-roam-ui](#org-roam-ui)
- [org-roam-protocol](#org-roam-protocol)
- [org-ref](#org-ref)
- [org-roam-bibtex](#org-roam-bibtex)
- [UI](#ui)
- [
OFF (OFF) Instant equations preview](#off--instant-equations-preview)
- [LaTeX fragments](#latex-fragments)
- [Better headers](#better-headers)
- [Org Agenda Icons](#org-agenda-icons)
- [Export](#export)
- [General settings](#general-settings)
- [Hugo](#hugo)
- [Jupyter Notebook](#jupyter-notebook)
- [Html export](#html-export)
- [LaTeX](#latex)
- [Keybindings & stuff](#keybindings-and-stuff)
- [Copy a link](#copy-a-link)
- [Presentations](#presentations)
- [TOC](#toc)
- [System configuration](#system-configuration)
- [Tables for Guix Dependencies](#tables-for-guix-dependencies)
- [Noweb evaluations](#noweb-evaluations)
- [yadm hook](#yadm-hook)
- [
OFF (OFF) EAF](#off--eaf)
- [Installation](#installation)
- [Config](#config)
- [Programming](#programming)
- [General setup](#general-setup)
- [LSP](#lsp)
- [Setup](#setup)
- [Integrations](#integrations)
- [Keybindings](#keybindings)
- [Flycheck](#flycheck)
- [Tree Sitter](#tree-sitter)
- [
OFF (OFF) DAP](#off--dap)
- [
OFF (OFF) TabNine](#off--tabnine)
- [
OFF (OFF) Code Compass](#off--code-compass)
- [Dependencies](#dependencies)
- [Plugin](#plugin)
- [
CHECK (OFF) Format-all](#off--format-all)
- [General additional config](#general-additional-config)
- [Web development](#web-development)
- [Emmet](#emmet)
- [Prettier](#prettier)
- [TypeScript](#typescript)
- [JavaScript](#javascript)
- [Jest](#jest)
- [web-mode](#web-mode)
- [
OFF (OFF) Vue.js](#off--vue-dot-js)
- [mmm-mode fix](#mmm-mode-fix)
- [
OFF (OFF) Svelte](#off--svelte)
- [SCSS](#scss)
- [PHP](#php)
- [LaTeX](#latex)
- [AUCTeX](#auctex)
- [BibTeX](#bibtex)
- [Import \*.sty](#import-dot-sty)
- [Snippets](#snippets)
- [Greek letters](#greek-letters)
- [English letters](#english-letters)
- [Math symbols](#math-symbols)
- [Section snippets](#section-snippets)
- [Other markup languages](#other-markup-languages)
- [Markdown](#markdown)
- [PlantUML](#plantuml)
- [LanguageTool](#languagetool)
- [Lisp](#lisp)
- [Meta Lisp](#meta-lisp)
- [Emacs Lisp](#emacs-lisp)
- [Package Lint](#package-lint)
- [General](#general)
- [Common lisp](#common-lisp)
- [Clojure](#clojure)
- [Hy](#hy)
- [Scheme](#scheme)
- [CLIPS](#clips)
- [Python](#python)
- [pipenv](#pipenv)
- [yapf](#yapf)
- [isort](#isort)
- [sphinx-doc](#sphinx-doc)
- [pytest](#pytest)
- [Fix comint buffer width](#fix-comint-buffer-width)
- [code-cells](#code-cells)
- [tensorboard](#tensorboard)
- [Java](#java)
- [Go](#go)
- [.NET](#dot-net)
- [C#](#c)
- [MSBuild](#msbuild)
- [fish](#fish)
- [sh](#sh)
- [Haskell](#haskell)
- [JSON](#json)
- [YAML](#yaml)
- [.env](#dot-env)
- [CSV](#csv)
- [
OFF (OFF) PDF](#off--pdf)
- [Docker](#docker)
- [Apps & Misc](#apps-and-misc)
- [Managing dotfiles](#managing-dotfiles)
- [Open Emacs config](#open-emacs-config)
- [Open Magit for yadm](#open-magit-for-yadm)
- [Open a dotfile](#open-a-dotfile)
- [Internet & Multimedia](#internet-and-multimedia)
- [Notmuch](#notmuch)
- [Elfeed](#elfeed)
- [Some additions](#some-additions)
- [YouTube](#youtube)
- [EMMS](#emms)
- [MPD](#mpd)
- [MPV](#mpv)
- [Cache cleanup](#cache-cleanup)
- [Fetching lyrics](#fetching-lyrics)
- [Some keybindings](#some-keybindings)
- [EMMS & mpd Fixes](#emms-and-mpd-fixes)
- [EWW](#eww)
- [ERC](#erc)
- [Google Translate](#google-translate)
- [Reading documentation](#reading-documentation)
- [tldr](#tldr)
- [man & info](#man-and-info)
- [devdocs.io](#devdocs-dot-io)
- [Utilities](#utilities)
- [pass](#pass)
- [Docker](#docker)
- [Progidy](#progidy)
- [screenshot.el](#screenshot-dot-el)
- [proced](#proced)
- [Guix](#guix)
- [Productivity](#productivity)
- [Pomidor](#pomidor)
- [Calendar](#calendar)
- [Fun](#fun)
- [Discord integration](#discord-integration)
- [Snow](#snow)
- [Zone](#zone)
- [Guix settings](#guix-settings)
## Primary setup {#primary-setup}
### Measure startup speed {#measure-startup-speed}
A small function to print out the loading time and number of GCs during the loading. Can be useful as a point of data for optimizing Emacs startup time.
```emacs-lisp
(add-hook 'emacs-startup-hook
(lambda ()
(message "*** Emacs loaded in %s with %d garbage collections."
(format "%.2f seconds"
(float-time
(time-subtract after-init-time before-init-time)))
gcs-done)))
;; (setq use-package-verbose t)
```
### straight.el {#straight-dot-el}
Straight.el is my Emacs package manager of choice. Its advantages & disadvantages over other options are listed pretty thoroughly in the README file in the repo.
The following is a straight.el bootstrap script.
References:
- [straight.el repo](https://github.com/raxod502/straight.el)
```emacs-lisp
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
```
### use-package {#use-package}
A macro to simplify package specification & configuration. Integrates with straight.el.
Set `use-package-verbose` to `t` to print out individual package loading time.
References:
- [use-package repo](https://github.com/jwiegley/use-package)
```emacs-lisp
(straight-use-package 'use-package)
(eval-when-compile (require 'use-package))
;; (setq use-package-verbose t)
```
### Performance {#performance}
#### Garbage collection {#garbage-collection}
Just setting `gc-cons-treshold` to a larger value.
```emacs-lisp
(setq gc-cons-threshold 80000000)
(setq read-process-output-max (* 1024 1024))
```
#### Run garbage collection when Emacs is unfocused {#run-garbage-collection-when-emacs-is-unfocused}
Run GC when Emacs loses focus. ~~Time will tell if that's a good idea.~~
Some time has passed, and I still don't know if there is any quantifiable advantage to this, but it doesn't hurt.
```emacs-lisp
(add-hook 'emacs-startup-hook
(lambda ()
(if (boundp 'after-focus-change-function)
(add-function :after after-focus-change-function
(lambda ()
(unless (frame-focus-state)
(garbage-collect))))
(add-hook 'after-focus-change-function 'garbage-collect))))
```
#### Misc {#misc}
The following variable is true when my machine is not powerful enough for some resource-heavy packages.
```emacs-lisp
(setq my/lowpower (string= (system-name) "azure"))
```
The following is true if Emacs is meant to be used with TRAMP over slow ssh.
```emacs-lisp
(setq my/slow-ssh (string= (getenv "IS_TRAMP") "true"))
```
And the following is true if Emacs is run from termux on Android.
```emacs-lisp
(setq my/is-termux (string-match-p (rx (* nonl) "com.termux" (* nonl)) (getenv "HOME")))
```
#### Native compilation {#native-compilation}
Set number of jobs to 1 on low-power machines
```emacs-lisp
(when my/lowpower
(setq comp-async-jobs-number 1))
```
### Anaconda & environment {#anaconda-and-environment}
[Anaconda](https://www.anaconda.com/) is a free package and environment manager. I currently use it to manage multiple versions of Python and Node.js
The following code uses the conda package to activate the base environment on startup if Emacs is launched outside the environment.
Also, some strange things are happening if vterm is launched with conda activated from Emacs, so I advise `conda-env-activate` to set an auxililary environment variable.
References:
- [Anaconda docs](https://docs.anaconda.com/)
- [conda.el repo](https://github.com/necaris/conda.el)
```emacs-lisp
(use-package conda
:straight t
:if (executable-find "conda")
:config
(setq conda-anaconda-home (string-replace "/bin/conda" "" (executable-find "conda")))
(setq conda-env-home-directory (expand-file-name "~/.conda/"))
(setq conda-env-subdirectory "envs")
(setenv "INIT_CONDA" "true")
(advice-add 'conda-env-activate :after
(lambda (&rest _) (setenv "EMACS_CONDA_ENV" conda-env-current-name)))
(unless (getenv "CONDA_DEFAULT_ENV")
(conda-env-activate "general")))
```
Also, I sometimes need to know if a program is running inside Emacs (say, inside a terminal emulator). To do that, I set the following environment variable:
```emacs-lisp
(setenv "IS_EMACS" "true")
```
### Custom file location {#custom-file-location}
By default, custom writes stuff to `init.el`, which is somewhat annoying. The following makes a separate file `custom.el`
```emacs-lisp
(setq custom-file (concat user-emacs-directory "custom.el"))
(load custom-file 'noerror)
```
### Private config {#private-config}
I have some variables which I don't commit to the repo, e.g. my current location. They are stored in `private.el`
```emacs-lisp
(let ((private-file (expand-file-name "private.el" user-emacs-directory)))
(when (file-exists-p private-file)
(load-file private-file)))
```
### No littering {#no-littering}
By default emacs and its packages create a lot files in `.emacs.d` and in other places. [no-littering](https://github.com/emacscollective/no-littering) is a collective effort to redirect all of this to two folders in `user-emacs-directory`.
```emacs-lisp
(use-package no-littering
:straight t)
```
## Global editing configuration {#global-editing-configuration}
### General keybindings stuff {#general-keybindings-stuff}
#### general.el {#general-dot-el}
general.el provides a convenient interface to manage Emacs keybindings.
References:
- [general.el repo](https://github.com/noctuid/general.el)
```emacs-lisp
(use-package general
:straight t
:config
(general-evil-setup))
```
#### which-key {#which-key}
A package that displays the available keybindings in a popup.
Pretty useful, as Emacs seems to have more keybindings than I can remember at any given point.
References:
- [which-key repo](https://github.com/justbur/emacs-which-key)
```emacs-lisp
(use-package which-key
:config
(setq which-key-idle-delay (if my/lowpower 1 0.3))
(setq which-key-popup-type 'frame)
(which-key-mode)
(which-key-setup-side-window-bottom)
(set-face-attribute 'which-key-local-map-description-face nil
:weight 'bold)
:straight t)
```
##### dump keybindings {#dump-keybindings}
A function to dump keybindings starting with a prefix to a buffer in tree-like form.
```emacs-lisp
(defun my/dump-bindings-recursive (prefix &optional level)
(dolist (key (which-key--get-bindings (kbd prefix)))
(when level
(insert (make-string level ? )))
(insert (apply #'format "%s%s%s\n" key))
(when (string-match-p
(rx bos "+" (* nonl))
(substring-no-properties (elt key 2)))
(my/dump-bindings-recursive
(concat prefix " " (substring-no-properties (car key)))
(+ 2 (or level 0))))))
(defun my/dump-bindings (prefix)
"Dump keybindings starting with PREFIX in tree-like form."
(interactive "sPrefix: ")
(with-current-buffer (get-buffer-create "bindings")
(point-max)
(erase-buffer)
(save-excursion
(my/dump-bindings-recursive prefix)))
(switch-to-buffer-other-window "bindings"))
```
### Evil mode {#evil-mode}
A whole ecosystem of packages that emulates the main features of Vim. Probably the best vim emulator out there.
The only problem is that the package name makes it hard to google anything by just typing "evil".
References:
- [evil repo](https://github.com/emacs-evil/evil)
- [(YouTube) Evil Mode: Or, How I Learned to Stop Worrying and Love Emacs](https://www.youtube.com/watch?v=JWD1Fpdd4Pc)
#### evil {#evil}
Basic evil configuration.
```emacs-lisp
(use-package evil
:straight t
:init
(setq evil-want-integration t)
(setq evil-want-C-u-scroll t)
(setq evil-want-keybinding nil)
:config
(evil-mode 1)
(setq evil-search-module 'evil-search)
(setq evil-split-window-below t)
(setq evil-vsplit-window-right t)
;; (setq evil-respect-visual-line-mode t)
(evil-set-undo-system 'undo-tree)
;; (add-to-list 'evil-emacs-state-modes 'dired-mode)
)
```
#### Addons {#addons}
[evil-surround](https://github.com/emacs-evil/evil-surround) emulates one of my favorite vim plugins, surround.vim. Adds a lot of parentheses management options.
```emacs-lisp
(use-package evil-surround
:straight t
:after evil
:config
(global-evil-surround-mode 1))
```
[evil-commentary](https://github.com/linktohack/evil-commentary) emulates commentary.vim.
```emacs-lisp
(use-package evil-commentary
:straight t
:after evil
:config
(evil-commentary-mode))
```
[evil-quickscope](https://github.com/blorbx/evil-quickscope) emulates quickscope.vim. It highlights the important target characters for f, F, t, T keys.
```emacs-lisp
(use-package evil-quickscope
:straight t
:after evil
:config
:hook ((prog-mode . turn-on-evil-quickscope-mode)
(LaTeX-mode . turn-on-evil-quickscope-mode)
(org-mode . turn-on-evil-quickscope-mode)))
```
[evil-numbers](https://github.com/cofi/evil-numbers) allows incrementing and decrementing numbers at the point.
```emacs-lisp
(use-package evil-numbers
:straight t
:commands (evil-numbers/inc-at-pt evil-numbers/dec-at-pt)
:init
(general-nmap
"g+" 'evil-numbers/inc-at-pt
"g-" 'evil-numbers/dec-at-pt))
```
[evil-lion](https://github.com/edkolev/evil-lion) provides alignment operators, somewhat similar to vim-easyalign.
```emacs-lisp
(use-package evil-lion
:straight t
:config
(setq evil-lion-left-align-key (kbd "g a"))
(setq evil-lion-right-align-key (kbd "g A"))
(evil-lion-mode))
```
[evil-matchit](https://github.com/redguardtoo/evil-matchit) makes "%" to match things like tags.
```emacs-lisp
(use-package evil-matchit
:straight t
:config
(global-evil-matchit-mode 1))
```
#### evil-collection {#evil-collection}
[evil-collection](https://github.com/emacs-evil/evil-collection) is a package that provides evil bindings for a lot of different packages. One can see the whole list in the [modes](https://github.com/emacs-evil/evil-collection/tree/master/modes) folder.
I don't enable the entire package, just the modes I need.
```emacs-lisp
(use-package evil-collection
:straight t
:after evil
:config
(evil-collection-init
'(eww
devdocs
proced
emms
pass
calendar
dired
debug
guix
calc
docker
ibuffer
geiser
pdf
info
elfeed
edebug
bookmark
company
vterm
flycheck
profiler
cider
explain-pause-mode
notmuch
custom
xref
eshell
helpful
compile
comint
magit
prodigy)))
```
### More keybindings {#more-keybindings}
The main keybindings setup is positioned after evil mode to take the latter into account.
#### Escape key {#escape-key}
Use the escape key instead of `C-g` whenever possible.
I must have copied it from somewhere, but as I googled to find out the source, I discovered quite a number of variations of the following code over time. I wonder if Richard Dawkins was inspired by something like this a few decades ago.
```emacs-lisp
(defun minibuffer-keyboard-quit ()
"Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
(interactive)
(if (and delete-selection-mode transient-mark-mode mark-active)
(setq deactivate-mark t)
(when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
(abort-recursive-edit)))
(general-define-key
:keymaps '(normal visual global)
[escape] 'keyboard-quit)
(general-define-key
:keymaps '(minibuffer-local-map
minibuffer-local-ns-map
minibuffer-local-completion-map
minibuffer-local-must-match-map
minibuffer-local-isearch-map)
[escape] 'minibuffer-keyboard-quit)
```
#### Home & end {#home-and-end}
```emacs-lisp
(general-def :states '(normal insert visual)
"