Hace algún tiempo, en Quora, respondí la pregunta:
cómo mantenerse al día con las notas de la clase de matemáticas en LaTeX . Allí expliqué mi flujo de trabajo para tomar notas en LaTeX usando Vim e Inkscape (para dibujos). Pero desde entonces, mucho ha cambiado, por lo que quiero publicar varias publicaciones de blog que describan el nuevo proceso. Este es el primero de los artículos.
Comencé a usar LaTeX para tomar notas en el segundo semestre de un curso de matemáticas, y desde entonces he escrito más de 1700 páginas. Estos son algunos ejemplos de cómo se ve el resumen:



Estas notas, incluidos los dibujos, se realizan directamente en la conferencia y no se editan posteriormente. Para escribir resúmenes de manera eficiente en LaTeX, se deben seguir cuatro reglas:
- Escribir texto y fórmulas en LaTeX debe ser tan rápido como un profesor escribiendo en la pizarra: el retraso es inaceptable.
- Dibujar ilustraciones debe ser casi tan rápido como el del profesor.
- Administrar notas, es decir, agregar notas, organizar todas las notas, las dos últimas conferencias, buscar notas, etc., debe ser rápido y fácil.
- Debería ser posible anotar documentos pdf usando LaTeX si quiero escribir una nota con un documento pdf.
Este artículo trata sobre el primer punto: tomar notas sobre LaTeX.
Vim y LaTeX
Para escribir fórmulas matemáticas y de texto en LaTeX, uso Vim. Este es un poderoso editor de texto de propósito general, altamente extensible. Lo uso para escribir código, LaTeX, texto de Markdown ... en general, cualquier texto. Tiene una curva de aprendizaje bastante empinada, pero si ha dominado la base, ya es difícil volver al editor sin las teclas de acceso rápido habituales. Así es como se ve mi pantalla cuando edito un documento de LaTeX:

A la izquierda está Vim, y a la derecha está el visor de PDF
Zathura , que también es compatible con los atajos de teclado de estilo Vim. Trabajo en Ubuntu con el
administrador de ventanas
bspwm . Como complemento, LaTeX instaló
vimtex . Proporciona resaltado de sintaxis, tabla de contenido, synctex, etc. Usando el
conector vim, lo configuré de la siguiente manera:
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'
Las dos últimas líneas ajustan el disfraz. Esta es una función en la que el código LaTeX se reemplaza o se vuelve invisible cuando el cursor no está en esta línea. Si oculta
\ [
,
\]
,
$
, entonces no son tan visibles, lo que proporciona una mejor visión general del documento. Esta función también reemplaza
\bigcap
con
∩
,
\in
con
∈
, etc., como se muestra en la animación:

Con esta configuración, puede lograr la tarea: escribir en LaTeX tan rápido como un profesor escribe en la pizarra. Los fragmentos entran en juego aquí.
Fragmentos
¿Qué es un fragmento?
Un fragmento es un fragmento breve de texto reutilizable que es invocado por otro texto. Por ejemplo, al escribir el signo y presionar Tab, el signo de la palabra se convierte en una firma:

Los fragmentos pueden ser dinámicos: cuando escribo
today
y presiono
Tab
, la palabra
today
se reemplaza por la fecha actual y el
box
-
Tab
convierte en un campo que aumenta automáticamente de tamaño.


Incluso puedes usar un fragmento dentro de otro:

Crear fragmentos con UltiSnips
Para controlar fragmentos, utilizo el complemento
UltiSnips . Aquí está su configuración:
Plug 'sirver/ultisnips' let g:UltiSnipsExpandTrigger = '<tab>' let g:UltiSnipsJumpForwardTrigger = '<tab>' let g:UltiSnipsJumpBackwardTrigger = '<s-tab>'
Código para el
sign
fragmento:
snippet sign "Signature" Yours sincerely, Gilles Castel endsnippet
Para fragmentos dinámicos, puede poner el código entre las comillas inversas, este código se ejecutará cuando se extienda el fragmento. Aquí usé bash para formatear la fecha actual:
date + %F
snippet today "Date" `date +%F` endsnippet
Dentro del bloque
`!p ... `
, puedes escribir en Python. Mire el código para el fragmento de
box
:
snippet box "Box" `!p snip.rv = '┌' + '─' * (len(t[1]) + 2) + '┐'` │ $1 │ `!p snip.rv = '└' + '─' * (len(t[1]) + 2) + '┘'` $0 endsnippet
En lugar de este código, el valor de la variable
snip.rv
se insertará en el documento. Dentro de los bloques, tiene acceso al estado actual del fragmento, por ejemplo,
t[1]
corresponde al lugar de la primera pestaña,
fn
nombre del archivo actual, etc.
Fragmentos de LaTeX
Los fragmentos aceleran significativamente el trabajo, especialmente algunos de los fragmentos más complejos. Comencemos con lo más simple.
El medio ambiente
Para insertar un entorno, simplemente ingrese
beg
al comienzo de la línea. Luego, el nombre del entorno, que se refleja en el comando
\end{}
. Al presionar
Tab
coloca el cursor dentro.

El código es el siguiente:
snippet beg "begin{} / end{}" bA \begin{$1} $0 \end{$1} endsnippet
El símbolo
b
significa que dicho fragmento funciona solo al comienzo de una línea,
A
significa expansión automática, es decir, no es necesario presionar
Tab
. Las pestañas donde vayas presionando
Tab
y
Shift
+
Tab
se denotan como
$1
,
$2
, ... y este último se denota como
$0
.
Fórmulas en línea y de visualización
Los dos fragmentos más utilizados son
mk
y
dm
, que activan el modo matemático. El primero para las fórmulas en línea, el segundo para las que se muestran.

Fragmento de fórmula inteligente: sabe cuándo insertar un espacio después del signo de dólar. Cuando empiezo a escribir una palabra inmediatamente después del cierre $, agrega un espacio. Pero si escribo otro carácter, entonces no agrega un espacio, como en el caso de '$ p $ -value'.

El código para este fragmento es:
snippet mk "Math" wA $${1}$`!p if t[2] and t[2][0] not in [',', '.', '?', '-', ' ']: snip.rv = ' ' else: snip.rv = '' `$2 endsnippet
W
al final de la primera línea significa que el fragmento se expande solo en los límites de las palabras. Por lo tanto, por ejemplo,
hellomk
no funcionará y
hello mk
funcionará.
El fragmento de las fórmulas mostradas es más simple, pero también bastante conveniente. Siempre hace que las ecuaciones terminen con un punto.

<snippet dm "Math" wA \[ $1 .\] $0 endsnippet
Subíndice y caracteres superíndice
Otro fragmento útil es para los índices. Cambia
a1
a
a_1
y
a_12
a
a_{12}
.

El código para este fragmento utiliza una expresión regular como desencadenante. Expande el fragmento cuando ingresa un carácter seguido de un dígito codificado como
[A-Za-z]\d
, o un carácter seguido de
_
y dos dígitos:
[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
Cuando combina partes de una expresión regular en un grupo usando paréntesis, por ejemplo,
(\d\d)
, puede usarlos en la extensión de fragmento a través de
match.group(i)
en Python.
Para los caracteres en superíndice, uso
td
, que se convierte en
^{}
. Aunque para los más comunes (cuadrado, cubo y varios otros), se pretenden fragmentos separados, como
sr
,
cb
y
comp
.

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
Fracción
Uno de los fragmentos más convenientes funciona con fracciones. Realiza los siguientes reemplazos:
//
→
\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)}{}

Para el primero, un código simple:
snippet // "Fraction" iA \\frac{$1}{$2}$0 endsnippet
Las sustituciones segunda y tercera se producen con la ayuda de expresiones regulares correspondientes a las expresiones
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
Como puede ver, las expresiones regulares pueden llegar a ser bastante largas, pero aquí hay un diagrama que debería explicar todo:

En el cuarto y quinto casos, el fragmento intenta encontrar el soporte correspondiente. Como el motor de expresiones regulares UltiSnips no sabe cómo hacer esto, tuve que usar 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
Finalmente, quiero compartir un fragmento que convierte la selección actual en una fracción. Seleccione el texto, presione
Tab
, escriba
/
y nuevamente
Tab
.

El código usa la variable
${VISUAL}
, que refleja su elección.
snippet / "Fraction" iA \\frac{${VISUAL}}{$1}$0 endsnippet
Sympy y Mathematica
Otro fragmento interesante pero menos usado se ejecuta
simétricamente para evaluar las expresiones matemáticas. Por ejemplo:
sympy
Tab
expande a
sympy | sympy
sympy | sympy
, y
sympy 1 + 1 sympy
Tab
convierte en
2
.

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('xyz t') k, m, n = symbols('km n', integer=True) f, g, h = symbols('fg h', cls=Function) init_printing() snip.rv = eval('latex(' + match.group(1).replace('\\', '') \ .replace('^', '**') \ .replace('{', '(') \ .replace('}', ')') + ')') ` endsnippet
Para Mathematica, algo similar también es posible:

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
Fragmentos de Postfix
Me parece que vale la pena mencionar también fragmentos de postfix que insertan el texto apropiado después de ingresar ciertos caracteres. Por ejemplo,
phat
→
\hat{p}
y
zbar
→
\overline{z}
. Un fragmento similar inserta un vector, por ejemplo,
v,.
→
\vec{v}
y
v.,
→
\vec{v}
. El orden del punto y el punto y coma no importa, por lo que puedo hacer clic en ellos al mismo tiempo. Estos fragmentos realmente ahorran tiempo, porque los ingresa a la misma velocidad que un profesor escribe en la pizarra.

Tenga en cuenta que los prefijos de
bar
y
hat
siguen funcionando, solo que con una prioridad más baja. El código para estos fragmentos es:
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
Otros fragmentos
Todavía tengo alrededor de un centenar de fragmentos de uso común. Todos ellos están disponibles
aquí . La mayoría de ellos son bastante simples. Por ejemplo,
!>
convierte en
\mapsto
,
->
convierte en
\to
, etc.

fun
transforma en
f: \R \to \R :
,
!>
→
\mapsto
,
cc
→
\subset
.

lim
convierte en
\lim_{n \to \infty}
,
sum
→
\sum_{n = 1}^{\infty}
,
ooo
→
\infty
.


Fragmentos específicos del curso
Además de los de uso frecuente, también tengo fragmentos específicos. Se cargan como una sola línea en
.vimrc
:
set rtp+=~/current_course
Aquí
current_course
es un
enlace simbólico al curso actual (más sobre esto en otro artículo). En esta carpeta está el archivo
~/current_course/UltiSnips/tex.snippets
, donde agrego fragmentos de curso. Por ejemplo, para la mecánica cuántica, hay fragmentos para registrar los estados cuánticos de apliques y cetos.
<a|
→
\bra{a}
<q|
→
\bra{\psi}
|a>
→
\ket{a}
|q>
→
\ket{\psi}
\braket{a}{b}
→
\braket{a}{b}
Como la mecánica cuántica a menudo usa
\psi
, reemplacé automáticamente todos los
q
en braket con
\psi
.

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
Contexto
Al escribir estos fragmentos, debe considerar si se pueden encontrar en texto sin formato. Por ejemplo, según mi diccionario, hay alrededor de 72 palabras en inglés y 2,000 palabras en holandés con sr. Por lo tanto, cuando
di^2egard
disregard
,
sr
cambia a
^2
, y obtenemos
di^2egard
.
La solución a este problema es agregar contexto a los fragmentos. El resaltado de sintaxis de Vim determina si UltiSnips debe usar un fragmento, dependiendo de si está en modo fórmula o texto. Se me ocurrió esta opción:
global !p texMathZones = ['texMathZone'+x for x in ['A', 'AS', 'B', 'BS', 'C', 'CS', 'D', 'DS', 'E', 'ES', 'F', 'FS', 'G', 'GS', 'H', 'HS', 'I', 'IS', 'J', 'JS', 'K', 'KS', 'L', 'LS', 'DS', 'V', 'W', 'X', 'Y', 'Z']] texIgnoreMathZones = ['texMathText'] texMathZoneIds = vim.eval('map('+str(texMathZones)+", 'hlID(v:val)')") texIgnoreMathZoneIds = vim.eval('map('+str(texIgnoreMathZones)+", 'hlID(v:val)')") ignore = texIgnoreMathZoneIds[0] def math(): synstackids = vim.eval("synstack(line('.'), col('.') - (col('.')>=2 ? 1 : 0))") try: first = next( i for i in reversed(synstackids) if i in texIgnoreMathZoneIds or i in texMathZoneIds ) return first != ignore except StopIteration: return False endglobal
Ahora puede agregar el
context "math()"
a esos fragmentos que desea aplicar solo en un contexto matemático.
context "math()" snippet sr "^2" iA ^2 endsnippet
Tenga en cuenta que el contexto matemático es algo sutil. Algunas veces en el modo fórmula también escribimos texto usando
\text{...}
. En este caso, no queremos usar fragmentos. Sin embargo, en el siguiente caso:
\[ \text{$...$} \]
,
deben aplicarse. Es por eso que el código para el contexto
math
no es tan simple. La siguiente animación ilustra estas sutilezas.

Corrección ortográfica sobre la marcha
Aunque las fórmulas son una parte importante del resumen, imprimo la mayor parte del tiempo en inglés. Alrededor de 80 palabras por minuto, mis habilidades de escritura son bastante buenas, pero hago muchos errores tipográficos. Es por eso que agregué un enlace a Vim que corrige los errores ortográficos sin interferir con el trabajo. Cuando
Ctrl+L
durante la entrada, se corrige el error de ortografía anterior. Se ve así:

Mi configuración para la corrección ortográfica:
setlocal spell set spelllang=nl,en_gb inoremap <Cl> <cg>u<Esc>[s1z=`]a<cg>u
Aquí, vaya al error de ortografía anterior
[s
, luego seleccione la primera opción
1z=
y devuelva
`]a
. Los comandos
<cg>u
en el medio le permiten deshacer rápidamente la corrección.
En conclusión
Gracias a los fragmentos de Vim, escribir código LaTeX ya no es molesto, sino más bien un placer. En combinación con la ortografía sobre la marcha, esto le permite esbozar conveniente y rápidamente conferencias sobre matemáticas. En el
próximo artículo, hablaré sobre otros temas, como dibujar ilustraciones digitalmente e insertarlas en un documento de LaTeX.