Como escrevo notas de matemática no LaTeX no Vim

Há algum tempo, no Quora, respondi à pergunta: como acompanhar as notas das aulas de matemática no LaTeX . Expliquei meu fluxo de trabalho de anotações no LaTeX usando o Vim e o Inkscape (para desenhos). Mas desde então, muita coisa mudou, então quero publicar várias postagens no blog com uma descrição do novo processo. Este é o primeiro dos artigos.

Comecei a usar o LaTeX para fazer anotações no segundo semestre de um curso de matemática e, desde então, escrevi mais de 1700 páginas. Aqui estão alguns exemplos de como o resumo se parece:







Essas anotações, incluindo desenhos, são feitas diretamente na palestra e não são editadas posteriormente. Para escrever resumos com eficiência no LaTeX, quatro regras devem ser seguidas:

  • Escrever texto e fórmulas no LaTeX deve ser tão rápido quanto um professor escrever no quadro: o atraso é inaceitável.
  • As ilustrações dos desenhos devem ser quase tão rápidas quanto as do professor.
  • Gerenciar notas, ou seja, adicionar notas, organizar todas as notas, as duas últimas palestras, pesquisar notas etc. deve ser rápido e fácil.
  • Deverá ser possível anotar documentos PDF usando o LaTeX, se eu quiser escrever uma nota com um documento PDF.

Este artigo é sobre o primeiro ponto: anotando o LaTeX.

Vim e LaTeX


Para escrever texto e fórmulas matemáticas no LaTeX, eu uso o Vim. Este é um poderoso editor de texto de uso geral, altamente extensível. Eu o uso para escrever código, LaTeX, texto Markdown ... em geral, qualquer texto. Ele tem uma curva de aprendizado bastante íngreme, mas se você dominou a base, já é difícil retornar ao editor sem as teclas de atalho habituais. É assim que minha tela se parece quando edito um documento do LaTeX:



À esquerda, o Vim, e à direita, o visualizador de PDF do Zathura , que também suporta atalhos de teclado no estilo do Vim. Eu trabalho no Ubuntu com o gerenciador de janelas bspwm . Como um plugin, o LaTeX instalou o vimtex . Ele fornece destaque de sintaxe, sumário, synctex, etc. Usando o vim-plug, eu o configurei da seguinte maneira:

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'


As duas últimas linhas ajustam o disfarce. Esta é uma função na qual o código LaTeX é substituído ou fica invisível quando o cursor não está nesta linha. Se você ocultar \ [ , \] , $ , eles não serão tão visíveis, o que fornece uma melhor visão geral do documento. Essa função também substitui \bigcap por , \in por , etc., conforme mostrado na animação:



Com essa configuração, você pode realizar a tarefa: escreva no LaTeX o mais rápido que um professor escreve no quadro. Snippets entram em jogo aqui.

Snippets


O que é um trecho?


Um snippet é um pequeno pedaço de texto reutilizável chamado por outro texto. Por exemplo, ao digitar sinal e pressionar Tab, o sinal da palavra se transforma em uma assinatura:



Os trechos podem ser dinâmicos: quando eu digito today e pressiono Tab , a palavra today é substituída pela data atual e a box - Tab se torna um campo que cresce automaticamente em tamanho.





Você pode até usar um trecho dentro de outro:



Criando snippets com o UltiSnips


Para controlar trechos, eu uso o plug- in UltiSnips . Aqui está sua configuração:

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

Código para o sign trecho:

 snippet sign "Signature" Yours sincerely, Gilles Castel endsnippet 

Para trechos dinâmicos, você pode colocar o código entre as aspas posteriores, esse código será executado quando o trecho se estender. Aqui eu usei o bash para formatar a data atual: date + %F

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

Dentro do bloco `!p ... ` , você pode escrever em Python. Veja o código do trecho da box :

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

Em vez desse código, o valor da variável snip.rv será inserido no documento. Dentro dos blocos, você tem acesso ao estado atual do trecho, por exemplo, t[1] corresponde ao local da primeira guia, fn nome do arquivo atual, etc.

Snippets do LaTeX


Os trechos aceleram significativamente o trabalho, especialmente alguns dos trechos mais complexos. Vamos começar com o mais simples.

O meio ambiente


Para inserir um ambiente, basta digitar beg no início da linha. Em seguida, o nome do ambiente, refletido no comando \end{} . Pressionar Tab coloca o cursor dentro.



O código é o seguinte:

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

O símbolo b significa que esse trecho funciona apenas no início de uma linha. A significa expansão automática, ou seja, você não precisa pressionar Tab . As guias para onde você pressiona Tab e Shift + Tab são indicadas como $1 , $2 , ... e a última é indicada como $0 .

Fórmulas em linha e de exibição


Os dois snippets mais usados ​​são mk e dm , que acionam o modo matemático. O primeiro para as fórmulas embutidas, o segundo para as exibidas.



Trecho de fórmula inteligente: ele sabe quando inserir um espaço após o sinal de cifrão. Quando começo a digitar uma palavra imediatamente após o fechamento de $, ele adiciona um espaço. Mas se eu digitar outro caractere, ele não adicionará um espaço, como no caso de '$ p $ -value'.



O código para esse snippet é:

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

W no final da primeira linha significa que o trecho se expande apenas nos limites da palavra. Portanto, por exemplo, hellomk não funcionará e o hello mk funcionará.

O snippet para as fórmulas exibidas é mais simples, mas também bastante conveniente. Sempre faz as equações terminarem com um ponto.



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

Caracteres subscritos e sobrescritos


Outro trecho útil é para índices. a_12 a1 para a_1 e a_12 para a_{12} .



O código para esse snippet usa uma expressão regular como gatilho. Ele expande o fragmento quando você digita um caractere seguido por um dígito codificado como [A-Za-z]\d , ou um caractere seguido por _ e dois 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 

Ao combinar partes de uma expressão regular em um grupo usando parênteses, por exemplo, (\d\d) , você pode usá-los na extensão de trecho via match.group(i) no Python.

Para caracteres sobrescritos, eu uso td , que se transforma em ^{} . Embora para os mais comuns (quadrado, cubo e vários outros), trechos separados sejam destinados, como sr , cb e 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 

Fração


Um dos trechos mais convenientes funciona com frações. Ele faz as seguintes substituições:

//\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 o primeiro, um código simples:

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

As segunda e terceira substituições ocorrem com a ajuda de expressões regulares correspondentes às expressões 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 você pode ver, expressões regulares podem se tornar bastante longas, mas aqui está um diagrama que deve explicar tudo:



No quarto e quinto casos, o trecho tenta encontrar o colchete correspondente. Como o mecanismo de regex UltiSnips não sabe como fazer isso, tive que usar o 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 

Por fim, quero compartilhar um trecho que transforme a seleção atual em uma fração. Selecione o texto, pressione Tab , digite / e novamente Tab .



O código usa a variável ${VISUAL} , que reflete sua escolha.

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

Sympy e Mathematica


Outro trecho interessante, mas menos usado, executa o sympy para avaliar expressões matemáticas. Por exemplo: a Tab sympy expande para sympy | sympy sympy | sympy e sympy 1 + 1 sympy Tab sympy 1 + 1 sympy transforma em 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 o Mathematica, algo semelhante também é possível:



 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 

Snippets do Postfix


Parece-me que vale a pena mencionar também trechos de postfix que inserem o texto apropriado após a inserção de certos caracteres. Por exemplo, phat\hat{p} e zbar\overline{z} . Um trecho semelhante insere um vetor, por exemplo, v,.\vec{v} e v.,\vec{v} . A ordem do ponto e ponto e vírgula não importa, para que eu possa clicar neles ao mesmo tempo. Esses trechos realmente economizam tempo, porque você os insere na mesma velocidade que um professor escreve no quadro.



Observe que os prefixos de bar e hat ainda funcionam, apenas com uma prioridade mais baixa. O código para esses trechos é:

 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 

Outros trechos


Eu ainda tenho cerca de cem trechos usados ​​com frequência. Todos eles estão disponíveis aqui . A maioria deles é bem simples. Por exemplo, !> Transforma-se em \mapsto , -> torna-se \to etc.



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



lim se torna \lim_{n \to \infty} , sum\sum_{n = 1}^{\infty} , ooo\infty .





Snippets específicos do curso


Além dos usados ​​com frequência, também tenho trechos específicos. Eles são carregados como uma única linha no .vimrc :

 set rtp+=~/current_course 

Aqui current_course é um link simbólico para o curso atual (mais sobre isso em outro artigo). Nesta pasta está o arquivo ~/current_course/UltiSnips/tex.snippets , onde adiciono trechos de curso. Por exemplo, para a mecânica quântica, existem trechos para registrar os estados quânticos de arandelas e cetos.

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

Como a mecânica quântica geralmente usa \psi , substituí automaticamente todos os q no braket por \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


Ao escrever esses trechos, considere se eles podem ser encontrados em texto sem formatação. Por exemplo, de acordo com o meu dicionário, existem cerca de 72 palavras em inglês e 2.000 palavras em holandês com sr. Portanto, quando digito disregard , sr muda para ^2 e obtemos di^2egard .

A solução para esse problema é adicionar contexto aos snippets. O destaque da sintaxe do Vim determina se o UltiSnips deve usar um snippet, dependendo se você está no modo de fórmula ou texto. Eu vim com esta opção:

 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 

Agora você pode adicionar o context "math()" aos trechos que deseja aplicar apenas em um contexto matemático.

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

Observe que o contexto matemático é uma coisa sutil. Às vezes, no modo de fórmula, também escrevemos texto usando \text{...} . Nesse caso, não queremos usar trechos. No entanto, no seguinte caso: \[ \text{$...$} \] , eles devem ser aplicados. É por isso que o código para o contexto math não é tão simples. A animação a seguir ilustra essas sutilezas.



Correção ortográfica em tempo real


Embora as fórmulas sejam uma parte importante do resumo, eu imprimo a maior parte do tempo em inglês. Cerca de 80 palavras por minuto, minhas habilidades de digitação são muito boas, mas eu faço muitos erros de digitação. Foi por isso que adicionei uma ligação ao Vim que corrige erros de ortografia sem interferir no trabalho. Quando Ctrl+L durante a entrada, o erro ortográfico anterior é corrigido. É assim:



Minhas configurações para verificação ortográfica:

 setlocal spell set spelllang=nl,en_gb inoremap <Cl> <cg>u<Esc>[s1z=`]a<cg>u 

Aqui, vá para o erro ortográfico anterior [s , selecione a primeira opção 1z= e retorne `]a . Os comandos <cg>u no meio permitem desfazer rapidamente a correção.

Em conclusão


Graças aos trechos do Vim, escrever código LaTeX não é mais irritante, mas sim um prazer. Em combinação com a ortografia instantânea, isso permite que você delineie de forma conveniente e rápida as palestras sobre matemática. No próximo artigo, falarei sobre outros tópicos, como desenhar ilustrações digitalmente e incorporá-las em um documento LaTeX.

Source: https://habr.com/ru/post/pt445066/


All Articles