我如何在Vim中的LaTeX上写数学笔记

前段时间,我在Quora回答了一个问题: 如何跟上LaTeX上的数学讲义 。 在那里,我使用Vim和Inkscape(用于图纸)在LaTeX中解释了我的笔记工作流程。 但是自那时以来,发生了很多变化,因此我想发布一些博客文章,并描述新过程。 这是文章的第一篇。

我在数学课程的第二学期开始使用LaTeX做笔记,从那时起我写了1700多页。 以下是摘要显示的一些示例:







这些笔记(包括图纸)直接在讲座上制作,以后不再编辑。 为了在LaTeX中有效地编写摘要,必须遵循四个规则:

  • 在LaTeX中编写文本和公式的速度应与讲师在黑板上书写的速度一样快:延迟是不可接受的。
  • 绘图插图应几乎与讲师的插图一样快。
  • 管理便笺,即添加便笺,安排所有便笺,最后两节讲座,搜索便笺等,应该快速简便。
  • 如果我想用pdf文档写注释,应该可以使用LaTeX注释pdf文档。

本文是关于第一点的:在LaTeX上做笔记。

Vim和LaTeX


要在LaTeX上编写文本和数学公式,我使用Vim。 这是一个功能强大的通用文本编辑器,具有高度可扩展性。 我用它来编写代码,LaTeX,Markdown文本……一般来说,任何文本。 他的学习曲线相当陡峭,但是如果您掌握了基础知识,那么如果没有通常的热键就很难返回编辑器。 这是我编辑LaTeX文档时的屏幕外观:



左边是Vim,右边是Zathura PDF查看器,它也支持Vim样式的键盘快捷键。 我在Ubuntu中使用bspwm窗口管理器。 LaTeX作为插件安装了vimtex 。 它提供语法高亮显示,目录,synctex等。使用vim-plug,我将其配置如下:

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'


最后两行调整伪装。 此功能是当光标不在此行上时,LaTeX代码将被替换或变得不可见的功能。 如果您隐藏\ [\]$ ,那么它们并不那么显眼,可以更好地浏览文档。 该函数还用\bigcap替换\bigcap ,用替换\bigcap \in ,等等,如动画所示:



使用此设置,您可以完成任务:在LaTeX上书写的速度与讲师在黑板上书写的速度相同。 片段在这里起作用。

片段


什么是摘要?


摘录是一小段可重用的文本,被其他文本调用。 例如,当键入sign并按Tab时,sign单词变成签名:



代码段可以是动态的:当我today输入并按Tabtoday一词将替换为当前日期,并且box Tab变为自动增加大小的字段。





您甚至可以在另一个代码段中使用一个代码段:



使用UltiSnips创建片段


为了控制代码片段,我使用UltiSnips插件。 这是它的配置:

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

代码段sign的代码:

 snippet sign "Signature" Yours sincerely, Gilles Castel endsnippet 

对于动态代码段,您可以将代码放在反引号之间,当代码段扩展时,将运行该代码。 在这里,我使用bash格式化当前日期: date + %F

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

`!p ... `块中,您可以用Python编写。 查看box摘要的代码:

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

代替此代码, snip.rv变量的值将插入到文档中。 在块内部,您可以访问代码段的当前状态,例如, t[1]对应于第一个选项卡的位置, fn当前文件名,等等。

乳胶片段


片段显着加快了工作速度,尤其是一些较复杂的片段。 让我们从最简单的开始。

环境


要插入环境,只需在行首输入beg 。 然后是环境名称,该名称反映在\end{}命令中。 按Tab将光标放在其中。



代码如下:

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

b符号表示这样的代码段仅在行的开头起作用, A表示自动扩展,即不需要按Tab 。 通过按TabShift + Tab转到的Tab Tab表示为$1$2 ,...,后者表示为$0

内联和显示公式


两个最常用的代码段是mkdm ,它们会触发数学模式。 第一个用于内联公式,第二个用于显示公式。



聪明的公式摘要:他知道何时在美元符号后插入空格。 当我在$结束后立即开始输入单词时,它会添加一个空格。 但是,如果我键入另一个字符,则不会添加空格,例如'$ p $ -value'。



此代码段的代码为:

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

第一行末尾W表示该代码段仅在单词边界处扩展。 因此,例如, hellomk将不起作用,而hello mk将起作用。

显示公式的代码段比较简单,但也很方便。 它总是使方程式以点结尾。



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

下标和上标字符


另一个有用的代码段是用于索引。 它将a1更改为a_1 ,将a_12a_{12}



此代码段的代码使用正则表达式作为触发器。 当您输入一个字符后跟一个编码为[A-Za-z]\d的字符,或一个字符后接_和两个数字时,它将扩展该片段。 [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 

当您使用括号将正则表达式的各个部分组合成一个组时,例如(\d\d) ,则可以通过Python中的match.group(i)在代码段扩展中使用它们。

对于上标字符,我使用td ,它变成^{} 。 尽管对于最常见的(正方形,立方体和其他几个),打算使用单独的代码段,例如srcbcomp



 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 

分数


最便捷的片段之一适用于分数。 他进行了以下替换:

//\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)}{}



首先,一个简单的代码:

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

在与表达式3/4ac/6\pi^2/a_2/等对应的正则表达式的帮助下,进行第二和第三次替换。

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

如您所见,正则表达式可能会变得很长,但是下面的图应该解释了所有内容:



在第四和第五种情况下,代码段尝试查找相应的括号。 由于UltiSnips正则表达式引擎不知道如何执行此操作,因此我不得不使用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 

最后,我想分享一个片段,将当前选择片段化。 选择文本,按Tab ,输入/然后再次按Tab Tab



该代码使用${VISUAL}变量,它反映了您的选择。

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

Sympy和Mathematica


另一个很酷但使用较少的代码段运行sympy来评估数学表达式。 例如: sympy Tab扩展为sympy | sympy sympy | sympysympy 1 + 1 sympy Tab变为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 

对于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片段


在我看来,值得一提的是后缀代码段,这些后缀代码段在输入某些字符后插入了适当的文本。 例如, phat\hat{p}zbar\overline{z} 。 类似的代码段会插入一个向量,例如v,.\vec{v}v.,\vec{v} 。 句号和分号的顺序无关紧要,因此我可以同时单击它们。 这些摘要确实可以节省时间,因为您以与讲师在黑板上相同的速度输入它们。



请注意, barhat前缀仍然可以使用,但是优先级较低。 这些代码段的代码是:

 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 

其他片段


我仍然有大约一百个常用片段。 所有这些都可以在这里找到 。 其中大多数都很简单。 例如, !>变成\mapsto->变成\to ,等等。



fun转换为f: \R \to \R : , !>\mapstocc\subset



lim变为\lim_{n \to \infty}sum\sum_{n = 1}^{\infty}ooo\infty





课程特定片段


除了常用的片段外,我还提供了一些特定的片段。 它们在.vimrc作为单行加载:

 set rtp+=~/current_course 

这里current_course是到当前课程的符号链接 (另一篇文章中有更多关于此课程的信息)。 这个文件夹中的文件是~/current_course/UltiSnips/tex.snippets ,在其中添加课程摘要。 例如,对于量子力学,有一些片段可以记录壁式和酮式量子态。

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

由于量子力学经常使用\psi ,因此我自动用\psi替换了brake中的所有q



 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 

语境


编写这些摘要时,应考虑是否可以在纯文本中找到它们。 例如,根据我的词典,带sr的英语大约72个单词,荷兰语的大约2000个单词。 因此,当我键入disregardsr更改为^2 ,并且得到di^2egard

解决此问题的方法是向片段添加上下文。 Vim的语法高亮显示确定UltiSnips是否应使用代码段,具体取决于您是处于公式模式还是文本模式。 我想出了这个选项:

 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 

现在,您可以将context "math()"添加到只想在数学上下文中应用的那些片段。

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

请注意,数学上下文是微妙的事情。 有时在公式模式下,我们也使用\text{...}编写文本。 在这种情况下,我们不想使用片段。 但是,在以下情况下: \[ \text{$...$} \]必须应用它们。 这就是为什么math上下文的代码不是那么简单。 以下动画说明了这些细微之处。



即时进行拼写校正


尽管公式是摘要的重要组成部分,但我大部分时间都是用英语打印的。 每分钟大约80个单词,我的打字技巧非常好,但我经常打错字。 这就是为什么我在Vim上添加了一个绑定,该绑定可以纠正拼写错误而不干扰工作。 在输入过程中Ctrl+L时,先前的拼写错误已得到纠正。 看起来像这样:



我的拼写检查设置:

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

在这里,转到上一个拼写错误[s ,然后选择第一个选项1z=并返回`]a 。 中间的<cg>u命令可让您快速撤消更正。

总结


感谢Vim片段,编写LaTeX代码不再烦人,而是一种乐趣。 结合动态拼写,这使您可以方便快捷地概述数学讲座。 在下一篇文章中,我将讨论其他主题,例如以数字方式绘制插图并将其嵌入LaTeX文档中。

Source: https://habr.com/ru/post/zh-CN445066/


All Articles