8

How I'm able to take notes in mathematics lectures using LaTeX and Vim | Gilles...

 2 years ago
source link: https://castel.dev/post/lecture-notes-1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How I'm able to take notes in mathematics lectures using LaTeX and Vim

A while back I an­swered a ques­tion on Quora: Can peo­ple ac­tu­al­ly keep up with note-​taking in Math­e­mat­ics lec­tures with LaTeX. There, I ex­plained my work­flow of tak­ing lec­ture notes in LaTeX using Vim and how I draw fig­ures in Inkscape. How­ev­er, a lot has changed since then and I’d like to write a few blog posts ex­plain­ing my work­flow.

I start­ed using LaTeX to write lec­ture notes in the sec­ond se­mes­ter of my bach­e­lor in math­e­mat­ics, and I’ve been using it ever since, which makes for a total of more than 1700 pages of notes. To give you an idea of what those notes look like, here are some ex­am­ples:

ca1
ca2
ca3

These lec­ture notes — in­clud­ing fig­ures — are made while at­tend­ing the lec­ture and have not been edit­ed af­ter­wards. To make note tak­ing using LaTeX vi­able, I had four goals in mind:

  • Writ­ing text and math­e­mat­i­cal for­mu­las in LaTeX should be as fast as the lec­tur­er writ­ing on a black­board: no delay is ac­cept­able.
  • Draw­ing fig­ures should be al­most as fast as the lec­tur­er.
  • Man­ag­ing notes, i.e. adding a note, com­pil­ing all my notes, com­pil­ing the last two lec­tures, search­ing in notes, etc. should be easy and quick.
  • An­no­tat­ing pdf doc­u­ments using LaTeX should be pos­si­ble for when I want to write notes along­side a pdf doc­u­ment.

This blog post will focus on the first item: writ­ing LaTeX.

Vim and LaTeX

For writ­ing text and math­e­mat­i­cal for­mu­las in LaTeX, I use Vim. Vim is a pow­er­ful gen­er­al pur­pose text ed­i­tor that’s very ex­ten­si­ble. I use it for writ­ing code, LaTeX, mark­down, … ba­si­cal­ly every­thing that’s text-​based. It has a fair­ly steep learn­ing curve, but once you’ve got the ba­sics down, it’s hard to get back to an ed­i­tor with­out Vim key­bind­ings. Here’s what my screen looks like when I’m edit­ing LaTeX:

Vim and Zathura

On the left you see Vim and on the right my pdf view­er, Za­thu­ra, which also has Vim-​like key­bind­ings. I’m using Ubun­tu with bspwm as my win­dow man­ag­er. The LaTeX plu­g­in I’m using in Vim is vim­tex. It pro­vides syn­tax high­light­ing, table of con­tents view, sync­tex, etc. Using vim-​plug, I con­fig­ured it as fol­lows:

Plug 'lervag/vimtex'
let g:tex_flavor='latex'
let g:vimtex_view_method='zathura'
let g:vimtex_quickfix_mode=0
set conceallevel=1
let g:tex_conceal='abdmg'

The last two lines con­fig­ure the con­ceal­ment. This is a fea­ture where LaTeX code is re­placed or made in­vis­i­ble when your cur­sor is not on that line. By mak­ing \[, \], $ in­vis­i­ble, they’re less ob­tru­sive which gives you a bet­ter overview of the doc­u­ment. This fea­ture also re­places \bigcap by by , \in by etc. The fol­low­ing an­i­ma­tion should make that clear.

conceal

With this set up, I come to the crux of this blog post: writ­ing LaTeX as fast as the lec­tur­er can write on the black­board. This is where snip­pets come into play.

Snippets

What’s a snippet?

A snip­pet is a short reusable piece of text that can be trig­gered by some other text. For ex­am­ple, when I type sign and press Tab, the word sign will be ex­pand­ed to a sig­na­ture:

sign

Snip­pets can also be dy­nam­ic: when I type today and press Tab, the word today will be re­placed by the cur­rent date, and box Tab be­comes a box that au­to­mat­i­cal­ly grows in size.

today

box

You can even use one snip­pet in­side an­oth­er:

todaybox

Using UltiSnips to create snippets

I use the plu­g­in Ul­tiSnips to man­age my snip­pets. My con­fig­u­ra­tion is

Plug 'sirver/ultisnips'
let g:UltiSnipsExpandTrigger = '<tab>'
let g:UltiSnipsJumpForwardTrigger = '<tab>'
let g:UltiSnipsJumpBackwardTrigger = '<s-tab>'

The code for the sign snip­pet is the fol­low­ing:

snippet sign "Signature"
Yours sincerely,

Gilles Castel
endsnippet

For dy­nam­ic snip­pets, you can put code be­tween back­ticks `` which will be run when the snip­pet is ex­pand­ed. Here, I’ve used bash to for­mat the cur­rent date: date + %F.

snippet today "Date"
`date +%F`
endsnippet

You can also use Python in­side a `!p ... ` block. Have a look at the code for the box snip­pet:

snippet box "Box"
`!p snip.rv = '┌' + '─' * (len(t[1]) + 2) + '┐'`
│ $1 │
`!p snip.rv = '└' + '─' * (len(t[1]) + 2) + '┘'`
$0
endsnippet

These Python code blocks will be re­placed by the value of the vari­able snip.rv. In­side these blocks, you have ac­cess to the cur­rent state of the snip­pet, e.g. t[1] con­tains the first tab stop, fn the cur­rent file­name, …

LaTeX snippets

Using snip­pets, writ­ing LaTeX is a lot faster than writ­ing it by hand. Es­pe­cial­ly some of the more com­plex snip­pets can save you a lot of time and frus­tra­tion. Let’s begin with some sim­ple snip­pets.

Environments

To in­sert an en­vi­ron­ment, all I have to do is type beg at the be­gin­ning of a line. Then I type the name of the en­vi­ron­ment, which is mir­rored in the \end{} com­mand. Press­ing Tab places the cur­sor in­side the newly cre­at­ed en­vi­ron­ment.

beginend

The code for this snip­pet is the fol­low­ing.

snippet beg "begin{} / end{}" bA
\begin{$1}
	$0
\end{$1}
endsnippet

The b means that this snip­pet will only be ex­pand­ed at the be­gin­ning of a line and A stands for auto ex­pand, which means I do not have to press Tab to ex­pand the snip­pet. Tab stops — i.e. places you can jump to by press­ing Tab and Shift+Tab — are rep­re­sent­ed by $1, $2, … and the last one with $0.

Inline and display math

Two of my most fre­quent­ly used snip­pets are mk and dm. They’re the snip­pets re­spon­si­ble for start­ing math mode. The first one is a snip­pet for in­line math, the sec­ond one for dis­played math.

mkdm

The snip­pet for in­line math is ‘smart’: it knows when to in­sert a space after the dol­lar sign. When I start typ­ing a word di­rect­ly be­hind the clos­ing $, it adds a space. How­ev­er, when I type a non-​word char­ac­ter, it does not add a space, which would be pre­ferred for ex­am­ple in the case of $p$-value.

mk_space

The code for this snip­pet is the fol­low­ing.

snippet mk "Math" wA
$${1}$`!p
if t[2] and t[2][0] not in [',', '.', '?', '-', ' ']:
    snip.rv = ' '
else:
    snip.rv = ''
`$2
endsnippet

The w at the end of the first line means that this snip­pet will ex­pand at word bound­aries, so e.g. hellomk won’t ex­pand, but hello mk will.

The snip­pet for dis­played math is more sim­ple, but it also is quite handy; it makes me never for­get end­ing equa­tions with a pe­ri­od.

dm

snippet dm "Math" wA
\[
$1
.\] $0
endsnippet

Sub- and superscripts

An­oth­er use­ful snip­pet is one for sub­scripts. It changes changes a1 to a_1 and a_12 to a_{12}.

subscripts

The code for this snip­pet uses a reg­u­lar ex­pres­sion for its trig­ger. It ex­pands the snip­pet when you type a char­ac­ter fol­lowed by a digit, which en­cod­ed by [A-Za-z]\d, or a char­ac­ter fol­lowed by _ and two dig­its: [A-Za-z]_\d\d.

snippet '([A-Za-z])(\d)' "auto subscript" wrA
`!p snip.rv = match.group(1)`_`!p snip.rv = match.group(2)`
endsnippet

snippet '([A-Za-z])_(\d\d)' "auto subscript2" wrA
`!p snip.rv = match.group(1)`_{`!p snip.rv = match.group(2)`}
endsnippet

When you wrap parts of a reg­u­lar ex­pres­sion in a group using paren­the­sis, e.g. (\d\d), you can use them in the ex­pan­sion of the snip­pet via match.group(i) in Python.

As for su­per­scripts, I use td, which be­comes ^{}. How­ev­er, for squared, cubed, com­ple­ment and a hand­ful of other com­mon ones, I use ded­i­cat­ed snip­pets such as sr, cb and comp.

superscripts

snippet sr "^2" iA
^2
endsnippet

snippet cb "^3" iA
^3
endsnippet

snippet compl "complement" iA
^{c}
endsnippet

snippet td "superscript" iA
^{$1}$0
endsnippet

Fractions

One of my most con­ve­nient snip­pets is one for frac­tions. This makes the fol­low­ing ex­pan­sions:

//\frac{}{} 3/\frac{3}{} 4\pi^2/\frac{4\pi^2}{} (1 + 2 + 3)/\frac{1 + 2 + 3}{} (1+(2+3)/)(1 + \frac{2+3}{}) (1 + (2+3))/\frac{1 + (2+3)}{}

frac

The code for the first one is easy:

snippet // "Fraction" iA
\\frac{$1}{$2}$0
endsnippet

The sec­ond and third ex­am­ples are made pos­si­ble using reg­u­lar ex­pres­sions to match for ex­pres­sions like 3/, 4ac/, 6\pi^2/, a_2/, etc.

snippet '((\d+)|(\d*)(\\)?([A-Za-z]+)((\^|_)(\{\d+\}|\d))*)/' "Fraction" wrA
\\frac{`!p snip.rv = match.group(1)`}{$1}$0
endsnippet

As you can see, reg­u­lar ex­pres­sions can be­come quite over­whelm­ing, but here’s a di­a­gram that should ex­plain it:

Diagram of the regular expression

In the fourth and fifth cases, it tries to find the match­ing paren­the­sis. As this isn’t pos­si­ble using the reg­u­lar ex­pres­sion en­gine of Ul­tiSnips, I re­sort­ed to using Python:

priority 1000
snippet '^.*\)/' "() Fraction" wrA
`!p
stripped = match.string[:-1]
depth = 0
i = len(stripped) - 1
while True:
	if stripped[i] == ')': depth += 1
	if stripped[i] == '(': depth -= 1
	if depth == 0: break;
	i -= 1
snip.rv = stripped[0:i] + "\\frac{" + stripped[i+1:-1] + "}"
`{$1}$0
endsnippet

The last snip­pet con­cern­ing frac­tions I’d like to share is one that uses your se­lec­tion to make a frac­tion. You can use it by first se­lect­ing some text, then press­ing Tab, typ­ing / and press­ing Tab again.

visualfrac

The code makes use of the ${VISUAL} vari­able that rep­re­sents your se­lec­tion.

snippet / "Fraction" iA
\\frac{${VISUAL}}{$1}$0
endsnippet

Sympy and Mathematica

An­oth­er cool — but less used — snip­pet is one that uses sympy to eval­u­ate math­e­mat­i­cal ex­pres­sions. For ex­am­ple: sympy Tab ex­pands to sympy | sympy, and sympy 1 + 1 sympy Tab ex­pands to 2.

sympy

snippet sympy "sympy block " w
sympy $1 sympy$0
endsnippet

priority 10000
snippet 'sympy(.*)sympy' "evaluate sympy" wr
`!p
from sympy import *
x, y, z, t = symbols('x y z t')
k, m, n = symbols('k m n', integer=True)
f, g, h = symbols('f g h', cls=Function)
init_printing()
snip.rv = eval('latex(' + match.group(1).replace('\\', '') \
    .replace('^', '**') \
    .replace('{', '(') \
    .replace('}', ')') + ')')
`
endsnippet

For the Math­e­mat­i­ca users out there, you can do some­thing sim­i­lar:

mathematica

priority 1000
snippet math "mathematica block" w
math $1 math$0
endsnippet

priority 10000
snippet 'math(.*)math' "evaluate mathematica" wr
`!p
import subprocess
code = 'ToString[' + match.group(1) + ', TeXForm]'
snip.rv = subprocess.check_output(['wolframscript', '-code', code])
`
endsnippet

Postfix snippets

Some other snip­pets I find worth shar­ing are post­fix snip­pets. Ex­am­ples of such snip­pets are phat\hat{p} and zbar\overline{z}. A sim­i­lar snip­pet is a post­fix vec­tor, for ex­am­ple v,.\vec{v} and v.,\vec{v}. The order of , and . doesn’t mat­ter, so I can press them both at the same time. These snip­pets are a real time-​saver, be­cause you can type in the same order the lec­tur­er writes on the black­board.

barhatvec

Note that I can still use bar and hat pre­fix too, as I’ve added them with a lower pri­or­i­ty. The code for those snip­pets is:

priority 10
snippet "bar" "bar" riA
\overline{$1}$0
endsnippet

priority 100
snippet "([a-zA-Z])bar" "bar" riA
\overline{`!p snip.rv=match.group(1)`}
endsnippet
priority 10
snippet "hat" "hat" riA
\hat{$1}$0
endsnippet

priority 100
snippet "([a-zA-Z])hat" "hat" riA
\hat{`!p snip.rv=match.group(1)`}
endsnippet
snippet "(\\?\w+)(,\.|\.,)" "Vector postfix" riA
\vec{`!p snip.rv=match.group(1)`}
endsnippet 

Other snippets

I have about 100 other com­mon­ly used snip­pets. They are avail­able here. Most of them are quite sim­ple. For ex­am­ple, !> be­comes \mapsto, -> be­comes \to, etc.

complex5

fun be­comes f: \R \to \R :, !>\mapsto, ->\to, cc\subset.

fun3

lim be­comes \lim_{n \to \infty}, sum\sum_{n = 1}^{\infty}, ooo\infty

sum4

bazel2

Course specific snippets

Be­side my com­mon­ly used snip­pets, I also have course spe­cif­ic snip­pets. These are loaded by adding the fol­low­ing to my .vimrc:

set rtp+=~/current_course

where current_course is a sym­link to my cur­rent­ly ac­ti­vat­ed course (more about that in an­oth­er blog post). In that fold­er, I have a file ~/current_course/UltiSnips/tex.snippets in which I in­clude course spe­cif­ic snip­pets. For ex­am­ple, for quan­tum me­chan­ics, I have snip­pets for bra/ket no­ta­tion.

<a|\bra{a} <q|\bra{\psi} |a>\ket{a} |q>\ket{\psi} <a|b>\braket{a}{b}

As \psi is used a lot in quan­tum me­chan­ics, I re­place all in­stances of q in a braket with \psi when ex­pand­ed.

braket

snippet "\<(.*?)\|" "bra" riA
\bra{`!p snip.rv = match.group(1).replace('q', f'\psi').replace('f', f'\phi')`}
endsnippet

snippet "\|(.*?)\>" "ket" riA
\ket{`!p snip.rv = match.group(1).replace('q', f'\psi').replace('f', f'\phi')`}
endsnippet

snippet "(.*)\\bra{(.*?)}([^\|]*?)\>" "braket" riA
`!p snip.rv = match.group(1)`\braket{`!p snip.rv = match.group(2)`}{`!p snip.rv = match.group(3).replace('q', f'\psi').replace('f', f'\phi')`}
endsnippet

Context

One thing to con­sid­er when writ­ing these snip­pets is, ‘will these snip­pets col­lide with usual text?’ For ex­am­ple, ac­cord­ing to my dic­tio­nary, there are about 72 words in Eng­lish and 2000 words in Dutch that con­tain sr, which means that while I’m typ­ing the word disregard, the sr would ex­pand to ^2, giv­ing me di^2egard.

The so­lu­tion to this prob­lem is adding a con­text to snip­pets. Using the syn­tax high­light­ing of Vim, it can be de­ter­mined whether or not Ul­tiSnips should ex­pand the snip­pet de­pend­ing if you’re in math or text. Add the fol­low­ing to the top of your snip­pets file:

global !p
def math():
    return vim.eval('vimtex#syntax#in_mathzone()') == '1'

def comment(): 
    return vim.eval('vimtex#syntax#in_comment()') == '1'

def env(name):
    [x,y] = vim.eval("vimtex#env#is_inside('" + name + "')") 
    return x != '0' and x != '0'

endglobal

Now you can add context "math()" to the snip­pets you’d only want to ex­pand in a math­e­mat­i­cal con­text.

context "math()"
snippet sr "^2" iA
^2
endsnippet

Note that a ‘math­e­mat­i­cal con­text’ is a sub­tle thing. Some­times you add some text in­side a math en­vi­ron­ment by using \text{...}. In that case, you do not want snip­pets to ex­pand. How­ev­er, in the fol­low­ing case: \[ \text{$...$} \], they should ex­pand. The fol­low­ing an­i­ma­tion il­lus­trates these sub­tleties.

syntaxtree

Sim­i­lar­ly, you can add context "env('itemize')" to snip­pets that only should ex­pand in an itemize en­vi­ron­ment or context "comment()" for snip­pets in com­ments.

This section was edited on 2020-12-24. The new version of vimtex makes the code for detecting the context a lot simpler. Click to see old version

Correcting spelling mistakes on the fly

While in­sert­ing math­e­mat­ics is an im­por­tant part of my note-​taking setup, most of the time I’m typ­ing Eng­lish. At about 80 words per minute, my typ­ing skills are not bad, but I still make a lot of typos. This is why I added a key­bind­ing to Vim that cor­rects the spelling mis­takes, with­out in­ter­rupt­ing my flow. When I press Ctrl+L while I’m typ­ing, the pre­vi­ous spelling mis­take is cor­rect­ed. It looks like this:

spellcheck

My con­fig­u­ra­tion for spell check is the fol­low­ing:

setlocal spell
set spelllang=nl,en_gb
inoremap <C-l> <c-g>u<Esc>[s1z=`]a<c-g>u

It ba­si­cal­ly jumps to the pre­vi­ous spelling mis­take [s, then picks the first sug­ges­tion 1z=, and then jumps back `]a. The <c-g>u in the mid­dle make it pos­si­ble to undo the spelling cor­rec­tion quick­ly.

In conclusion

Using snip­pets in Vim, writ­ing LaTeX is no longer an an­noy­ance, but rather a plea­sure. In com­bi­na­tion with spell check on the fly, it al­lows for a com­fort­able math­e­mat­i­cal note-​taking setup. A few pieces are miss­ing though, for ex­am­ple draw­ing fig­ures dig­i­tal­ly and em­bed­ding them in a LaTeX doc­u­ment. This is a topic I’d like to tack­le in a fu­ture blog post.

Liked this blog post? Con­sid­er buy­ing me a cof­fee!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK