Better maths input in Emacs
In which we write a minor mode for better maths input in LaTeX and Agda2.
Much has been written on attempting to tame the verbosity of LaTeX with emacs. … has …. The LaTeX-math-mode
(which is included with AucTeX) was my preferred solution for some time. CD-LaTeX seems like a natural improvement. My biggest issue with CD-LaTeX was that the package included a lot of functionality that I did not find helpful (such as … and …). On top of that the defaults for `…` were not the keybindings that personally find helpful. At this point the majority of the CD-LaTeX package provides functionality I am not interested in and the remainder needs significant reconfiguration. So I happily used LaTeX-math-mode
for the past two years.
Those are fairly minor issues. However, I have recently started writing a fair bit of Agda (and I'm hoping to write a lot more in my masters) and I've realised I need a universal and consistent way of entering maths in emacs. Emacs agda2-mode
(which is by far the best integration of a language and editor I have ever seen) has an input method for converting LaTeX macros to Unicode. Entering, say, "→" will enter the Unicode string "→". I'm sure this is great for people who type out each and every macro, but switching back feels slow and painful. Ideally using "λ" as a variable shouldn't be any more painful then using "x". Furthermore, the input mode has its own conventions for entering things like blackboard bold letters, which are different to my own. For this reason I wrote math-transient-mode
to input my most commonly used LaTeX macros.
First we need a way to define commands which enter the correct text based on the current mode. In LaTeX-mode
we should prefer the built in LaTeX-math-
commands, but in agda2-mode
we should insert the corresponding Unicode symbol. We do this with the function my/insert-maths
:
(defun my/insert-maths (unicode auctex-fun)
(lambda ()
(interactive)
(when (eq major-mode #'LaTeX-mode) (call-interactively auctex-fun))
(when (eq major-mode #'agda2-mode) (insert unicode))))
The function my/insert-maths
takes in a Unicode symbol and the function symbol for the corresponding AucTeX command and returns a lambda which either inserts the Unicode or calls the provided AucTeX command, depending on context. With this we can start replicating the functionality of LaTeX-math-mode
. I have two layers for entering maths. The first layer contains greek letters and common symbols:
(defvar my/math-layer-one
(let ((map (make-sparse-keymap)))
; Greek Letters
(keymap-set map "a" `("α" . ,(my/insert-maths "α" #'LaTeX-math-alpha)))
(keymap-set map "b" `("β" . ,(my/insert-maths "β" #'LaTeX-math-beta)))
(keymap-set map "d" `("δ" . ,(my/insert-maths "δ" #'LaTeX-math-delta)))
(keymap-set map "D" `("Δ" . ,(my/insert-maths "Δ" #'LaTeX-math-Delta)))
(keymap-set map "e" `("ε" . ,(my/insert-maths "ε" #'LaTeX-math-varepsilon)))
(keymap-set map "C-e" `("ϵ" . ,(my/insert-maths "ϵ" #'LaTeX-math-epsilon)))
(keymap-set map "f" `("ϕ" . ,(my/insert-maths "ϕ" #'LaTeX-math-phi)))
(keymap-set map "C-f" `("φ" . ,(my/insert-maths "φ" #'LaTeX-math-varphi)))
(keymap-set map "F" `("Φ" . ,(my/insert-maths "Φ" #'LaTeX-math-Phi)))
(keymap-set map "g" `("γ" . ,(my/insert-maths "γ" #'LaTeX-math-gamma)))
(keymap-set map "G" `("Γ" . ,(my/insert-maths "Γ" #'LaTeX-math-Gamma)))
(keymap-set map "h" `("η" . ,(my/insert-maths "η" #'LaTeX-math-eta)))
(keymap-set map "i" `("ι" . ,(my/insert-maths "ι" #'LaTeX-math-iota)))
(keymap-set map "j" `("θ" . ,(my/insert-maths "θ" #'LaTeX-math-theta)))
(keymap-set map "C-j" `("ϑ" . ,(my/insert-maths "ϑ" #'LaTeX-math-vartheta)))
(keymap-set map "J" `("Θ" . ,(my/insert-maths "Θ" #'LaTeX-math-Theta)))
(keymap-set map "k" `("κ" . ,(my/insert-maths "κ" #'LaTeX-math-kappa)))
(keymap-set map "l" `("λ" . ,(my/insert-maths "λ" #'LaTeX-math-lambda)))
(keymap-set map "L" `("Λ" . ,(my/insert-maths "Λ" #'LaTeX-math-Lambda)))
(keymap-set map "C-l" `("ℓ" . ,(my/insert-maths "ℓ" #'LaTeX-math-ell)))
(keymap-set map "m" `("μ" . ,(my/insert-maths "μ" #'LaTeX-math-mu)))
(keymap-set map "n" `("ν" . ,(my/insert-maths "ν" #'LaTeX-math-nu)))
(keymap-set map "p" `("π" . ,(my/insert-maths "π" #'LaTeX-math-pi)))
(keymap-set map "C-p" `("ϖ" . ,(my/insert-maths "ϖ" #'LaTeX-math-varpi)))
(keymap-set map "P" `("Π" . ,(my/insert-maths "Π" #'LaTeX-math-Pi)))
(keymap-set map "q" `("χ" . ,(my/insert-maths "χ" #'LaTeX-math-chi)))
(keymap-set map "r" `("ρ" . ,(my/insert-maths "ρ" #'LaTeX-math-rho)))
(keymap-set map "C-r" `("ϱ" . ,(my/insert-maths "ϱ" #'LaTeX-math-varrho)))
(keymap-set map "s" `("σ" . ,(my/insert-maths "σ" #'LaTeX-math-sigma)))
(keymap-set map "S" `("Σ" . ,(my/insert-maths "Σ" #'LaTeX-math-Sigma)))
(keymap-set map "t" `("τ" . ,(my/insert-maths "τ" #'LaTeX-math-tau)))
(keymap-set map "u" `("υ" . ,(my/insert-maths "υ" #'LaTeX-math-upsilon)))
(keymap-set map "w" `("ω" . ,(my/insert-maths "ω" #'LaTeX-math-omega)))
(keymap-set map "W" `("Ω" . ,(my/insert-maths "Ω" #'LaTeX-math-Omega)))
(keymap-set map "x" `("ξ" . ,(my/insert-maths "ξ" #'LaTeX-math-xi)))
(keymap-set map "X" `("Ξ" . ,(my/insert-maths "Ξ" #'LaTeX-math-Xi)))
(keymap-set map "y" `("ψ" . ,(my/insert-maths "ψ" #'LaTeX-math-psi)))
(keymap-set map "Y" `("Ψ" . ,(my/insert-maths "Ψ" #'LaTeX-math-Psi)))
(keymap-set map "z" `("ζ" . ,(my/insert-maths "ζ" #'LaTeX-math-zeta)))
; Decorations
(keymap-set map "~" `("~" . ,(my/insert-maths "~" #'LaTeX-math-tilde)))
(keymap-set map "^" `("^" . ,(my/insert-maths "^" #'LaTeX-math-hat)))
(keymap-set map "_" `("‾" . ,(my/insert-maths "‾" #'LaTeX-math-bar)))
; Operations
(keymap-set map "*" `("×" . ,(my/insert-maths "×" #'LaTeX-math-times)))
(keymap-set map "+" `("∪" . ,(my/insert-maths "∪" #'LaTeX-math-cup)))
(keymap-set map "M-*" `("⊗" . ,(my/insert-maths "⊗" #'LaTeX-math-otimes)))
(keymap-set map "C-*" `("⊠" . ,(my/insert-maths "⊠" #'LaTeX-math-boxtimes)))
(keymap-set map "M-+" `("⊕" . ,(my/insert-maths "⊕" #'LaTeX-math-oplus)))
(keymap-set map "C-+" `("∨" . ,(my/insert-maths "∨" #'LaTeX-math-vee)))
(keymap-set map "-" `("∩" . ,(my/insert-maths "∩" #'LaTeX-math-cap)))
(keymap-set map "C--" `("∧" . ,(my/insert-maths "∧" #'LaTeX-math-wedge)))
(keymap-set map "." `("·" . ,(my/insert-maths "·" #'LaTeX-math-cdot)))
(keymap-set map ";" `("∘" . ,(my/insert-maths "∘" #'LaTeX-math-circ)))
; Relations
(keymap-set map "[" `("∈" . ,(my/insert-maths "∈" #'LaTeX-math-in)))
(keymap-set map "]" `("∋" . ,(my/insert-maths "∋" #'LaTeX-math-ni)))
(keymap-set map "{" `("⊂" . ,(my/insert-maths "⊂" #'LaTeX-math-subset)))
(keymap-set map "}" `("⊃" . ,(my/insert-maths "⊃" #'LaTeX-math-supset)))
(keymap-set map "<" `("≤" . ,(my/insert-maths "≤" #'LaTeX-math-leq)))
(keymap-set map ">" `("≥" . ,(my/insert-maths "≥" #'LaTeX-math-geq)))
(keymap-set map "=" `("≅" . ,(my/insert-maths "≅" #'LaTeX-math-cong)))
(keymap-set map "C-=" `("≡" . ,(my/insert-maths "≡" #'LaTeX-math-equiv)))
(keymap-set map "M-=" `("≃" . ,(my/insert-maths "≃" #'LaTeX-math-simeq)))
; Arrows
(keymap-set map "<right>" `("→" . ,(my/insert-maths "→" #'LaTeX-math-rightarrow)))
(keymap-set map "<left>" `("←" . ,(my/insert-maths "←" #'LaTeX-math-leftarrow)))
(keymap-set map "C-<right>" `("↪" . ,(my/insert-maths "↪" #'LaTeX-math-hookrightarrow)))
(keymap-set map "C-<left>" `("↩" . ,(my/insert-maths "↩" #'LaTeX-math-hookleftarrow)))
(keymap-set map "M-<right>" `("⇒" . ,(my/insert-maths "⇒" #'LaTeX-math-Rightarrow)))
(keymap-set map "M-<left>" `("⇐" . ,(my/insert-maths "⇐" #'LaTeX-math-Leftarrow)))
(keymap-set map "C-M-<right>" `("↷" . ,(my/insert-maths "↷" #'LaTeX-math-curvearrowright)))
(keymap-set map "C-M-<left>" `("↶" . ,(my/insert-maths "↶" #'LaTeX-math-curvearrowleft)))
; Other symbols
(keymap-set map "C-<" `("⟨" . ,(my/insert-maths "⟨" #'LaTeX-math-langle)))
(keymap-set map "C->" `("⟩" . ,(my/insert-maths "⟩" #'LaTeX-math-rangle)))
(keymap-set map "M->" `("▷" . ,(my/insert-maths "▷" #'LaTeX-math-triangleright)))
(keymap-set map "C-M-<" `("◂" . ,(my/insert-maths "◂" #'LaTeX-math-blacktriangleleft)))
(keymap-set map "C-M->" `("▸" . ,(my/insert-maths "▸" #'LaTeX-math-blacktriangleright)))
(keymap-set map "M-<" `("◁" . ,(my/insert-maths "◁" #'LaTeX-math-triangleleft)))
(keymap-set map "&" `("⋈" . ,(my/insert-maths "⋈" #'LaTeX-math-bowtie)))
(keymap-set map "I" `("∞" . ,(my/insert-maths "∞" #'LaTeX-math-infty)))
(keymap-set map "0" `("∅" . ,(my/insert-maths "∅" #'LaTeX-math-emptyset)))
(keymap-set map "A" `("∀" . ,(my/insert-maths "∀" #'LaTeX-math-forall)))
(keymap-set map "E" `("∃" . ,(my/insert-maths "∃" #'LaTeX-math-exists)))
(keymap-set map "'" `("∂" . ,(my/insert-maths "∂" #'LaTeX-math-partial)))
(keymap-set map "N" `("𝛻" . ,(my/insert-maths "𝛻" #'LaTeX-math-nabla)))
map)
"Common maths symbols.")
The second layer contains calligraphic and blackboard bold letters. Since the best way to enter these is depenedent on your own personal conventions, I had to write two functions which return lambdas inserting these letters to stand in for the LaTeX-math-
functions:
(defun my/latex-insert-cal (char)
(lambda ()
(interactive)
(when (eq major-mode #'LaTeX-mode) (insert (format "\\%c%c" (upcase char) (downcase char))))))
(defun my/latex-insert-bb (char)
(lambda ()
(interactive)
(when (eq major-mode #'LaTeX-mode) (insert (format "\\%c%c" (upcase char) (upcase char))))))
From this we then define our second layer:
(defvar my/math-layer-fonts-cal-bb
(let ((map (make-sparse-keymap)))
; Blackboard bold
(keymap-set map "A" `("𝔸" . ,(my/insert-maths "𝔸" (my/latex-insert-bb ?A))))
(keymap-set map "B" `("𝔹" . ,(my/insert-maths "𝔹" (my/latex-insert-bb ?B))))
(keymap-set map "C" `("ℂ" . ,(my/insert-maths "ℂ" (my/latex-insert-bb ?C))))
(keymap-set map "D" `("𝔻" . ,(my/insert-maths "𝔻" (my/latex-insert-bb ?D))))
(keymap-set map "E" `("𝔼" . ,(my/insert-maths "𝔼" (my/latex-insert-bb ?E))))
(keymap-set map "F" `("𝔽" . ,(my/insert-maths "𝔽" (my/latex-insert-bb ?F))))
(keymap-set map "G" `("𝔾" . ,(my/insert-maths "𝔾" (my/latex-insert-bb ?G))))
(keymap-set map "H" `("ℍ" . ,(my/insert-maths "ℍ" (my/latex-insert-bb ?H))))
(keymap-set map "I" `("𝕀" . ,(my/insert-maths "𝕀" (my/latex-insert-bb ?I))))
(keymap-set map "J" `("𝕁" . ,(my/insert-maths "𝕁" (my/latex-insert-bb ?J))))
(keymap-set map "K" `("𝕂" . ,(my/insert-maths "𝕂" (my/latex-insert-bb ?K))))
(keymap-set map "L" `("𝕃" . ,(my/insert-maths "𝕃" (my/latex-insert-bb ?L))))
(keymap-set map "M" `("𝕄" . ,(my/insert-maths "𝕄" (my/latex-insert-bb ?M))))
(keymap-set map "N" `("ℕ" . ,(my/insert-maths "ℕ" (my/latex-insert-bb ?N))))
(keymap-set map "O" `("𝕆" . ,(my/insert-maths "𝕆" (my/latex-insert-bb ?O))))
(keymap-set map "P" `("ℙ" . ,(my/insert-maths "ℙ" (my/latex-insert-bb ?P))))
(keymap-set map "Q" `("ℚ" . ,(my/insert-maths "ℚ" (my/latex-insert-bb ?Q))))
(keymap-set map "R" `("ℝ" . ,(my/insert-maths "ℝ" (my/latex-insert-bb ?R))))
(keymap-set map "S" `("𝕊" . ,(my/insert-maths "𝕊" (my/latex-insert-bb ?S))))
(keymap-set map "T" `("𝕋" . ,(my/insert-maths "𝕋" (my/latex-insert-bb ?T))))
(keymap-set map "U" `("𝕌" . ,(my/insert-maths "𝕌" (my/latex-insert-bb ?U))))
(keymap-set map "V" `("𝕍" . ,(my/insert-maths "𝕍" (my/latex-insert-bb ?V))))
(keymap-set map "W" `("𝕎" . ,(my/insert-maths "𝕎" (my/latex-insert-bb ?W))))
(keymap-set map "X" `("𝕏" . ,(my/insert-maths "𝕏" (my/latex-insert-bb ?X))))
(keymap-set map "Y" `("𝕐" . ,(my/insert-maths "𝕐" (my/latex-insert-bb ?Y))))
(keymap-set map "Z" `("ℤ" . ,(my/insert-maths "ℤ" (my/latex-insert-bb ?Z))))
(keymap-set map "1" `("𝟙" . ignore))
(keymap-set map "2" `("𝟚" . ignore))
(keymap-set map "3" `("𝟛" . ignore))
(keymap-set map "4" `("𝟜" . ignore))
(keymap-set map "5" `("𝟝" . ignore))
(keymap-set map "6" `("𝟞" . ignore))
(keymap-set map "7" `("𝟟" . ignore))
(keymap-set map "8" `("𝟠" . ignore))
(keymap-set map "9" `("𝟡" . ignore))
(keymap-set map "0" `("𝟘" . ignore))
; Calligraphic
(keymap-set map "a" `("𝒜" . ,(my/insert-maths "𝒜" (my/latex-insert-cal ?a))))
(keymap-set map "b" `("ℬ" . ,(my/insert-maths "ℬ" (my/latex-insert-cal ?b))))
(keymap-set map "c" `("𝒞" . ,(my/insert-maths "𝒞" (my/latex-insert-cal ?c))))
(keymap-set map "d" `("𝒟" . ,(my/insert-maths "𝒟" (my/latex-insert-cal ?d))))
(keymap-set map "e" `("ℰ" . ,(my/insert-maths "ℰ" (my/latex-insert-cal ?e))))
(keymap-set map "f" `("ℱ" . ,(my/insert-maths "ℱ" (my/latex-insert-cal ?f))))
(keymap-set map "g" `("𝒢" . ,(my/insert-maths "𝒢" (my/latex-insert-cal ?g))))
(keymap-set map "h" `("ℋ" . ,(my/insert-maths "ℋ" (my/latex-insert-cal ?h))))
(keymap-set map "i" `("ℐ" . ,(my/insert-maths "ℐ" (my/latex-insert-cal ?i))))
(keymap-set map "j" `("𝒥" . ,(my/insert-maths "𝒥" (my/latex-insert-cal ?j))))
(keymap-set map "k" `("𝒦" . ,(my/insert-maths "𝒦" (my/latex-insert-cal ?k))))
(keymap-set map "l" `("ℒ" . ,(my/insert-maths "ℒ" (my/latex-insert-cal ?l))))
(keymap-set map "m" `("ℳ" . ,(my/insert-maths "ℳ" (my/latex-insert-cal ?m))))
(keymap-set map "n" `("𝒩" . ,(my/insert-maths "𝒩" (my/latex-insert-cal ?n))))
(keymap-set map "o" `("𝒪" . ,(my/insert-maths "𝒪" (my/latex-insert-cal ?o))))
(keymap-set map "p" `("𝒫" . ,(my/insert-maths "𝒫" (my/latex-insert-cal ?p))))
(keymap-set map "q" `("𝒬" . ,(my/insert-maths "𝒬" (my/latex-insert-cal ?q))))
(keymap-set map "r" `("ℛ" . ,(my/insert-maths "ℛ" (my/latex-insert-cal ?r))))
(keymap-set map "s" `("𝒮" . ,(my/insert-maths "𝒮" (my/latex-insert-cal ?s))))
(keymap-set map "t" `("𝒯" . ,(my/insert-maths "𝒯" (my/latex-insert-cal ?t))))
(keymap-set map "u" `("𝒰" . ,(my/insert-maths "𝒰" (my/latex-insert-cal ?u))))
(keymap-set map "v" `("𝒱" . ,(my/insert-maths "𝒱" (my/latex-insert-cal ?v))))
(keymap-set map "w" `("𝒲" . ,(my/insert-maths "𝒲" (my/latex-insert-cal ?w))))
(keymap-set map "x" `("𝒳" . ,(my/insert-maths "𝒳" (my/latex-insert-cal ?x))))
(keymap-set map "y" `("𝒴" . ,(my/insert-maths "𝒴" (my/latex-insert-cal ?y))))
(keymap-set map "z" `("𝒵" . ,(my/insert-maths "𝒵" (my/latex-insert-cal ?z))))
map)
"Enter math-cal and math-bb.")
Finally, we package all of this up into a nice minor mode
(defvar my/math-layers
(let ((map (make-sparse-keymap)))
(define-key map "`" (cons "Math Symbols" my/math-layer-one))
(define-key map (kbd "C-`") (cons "Mathbb and Mathcal" my/math-layer-fonts-cal-bb))
map)
"Quickly enter maths.")
(define-minor-mode math-transient-mode
"Toggles Math Transient Mode."
:init-value nil
:lighter "M"
:keymap my/math-layers)
What about typing quotes? smart quotes