背景
LaTeX Suite是个用来加速latex公式输入的插件,其工作逻辑就是简单的文本拓展,除此之外都不是主要功能。我之前在 我如何用 ob 做考研笔记中分享了我如何做考研笔记,其中最大头的部分就是在讨论如何加速数学和专业课笔记中的公式输入,当时我只是简单在LaTeX Suite中自定义了几条拓展规则,在这里我将结合近来的思考继续开发LaTeX Suite这款插件的用途,力图用OB带来更好的编辑体验。
LaTeX Suite 与 Text Expansion(文本拓展)
打开LaTeX Suite的Snippets表,我们发现它是这样的:
每一条snippet都是这样的:
{trigger: "触发文本", replacement: "替换文本", options: "选项"}
它提供的选项有
m
: math的意思,只在公式块中启用t
: text的意思,只在公式块外启用A
: Auto的意思,不需要按Tab就会执行替换r
: Regex正则表达式,trigger
的内容会被解析一下,后面再详细说w
: 只有在trigger后面或者前面有.
,,
,-
这类标点时启用
从这里可以发现,说到底 LaTeX Suite 也就是提供了个文本替换功能。但是这就已经够了。就像我之前说的那样,往往是这种又不起眼又朴素无比的小功能带来了最好的编辑体验。
既然在公式块外面也可用,而且还提供了正则表达式这类功能,这就给了我们操作的空间。我们先从数学开始。
数学公式
LaTeX Suite 默认的Snippets表是基于 Gilles Castel 的方法写的。这位老哥在大学课堂上用vim编辑器+纯 LaTeX以及这套文本拓展思路来做笔记,达到了非常恐怖的效率。这是他提供的动图:
Castel 老哥的做法非常硬核,不适合普通用户掌握。而且他这套纯vim模式也不见得适合使用汉语的用户,LaTeX Suite也只是采取了他的一部分思路而已。有兴趣的朋友可以移步他的博客:
Gilles Castel’s setup using UltiSnips
接下来我会基于本人的使用体验对LaTeX Suite的默认设置进行改动。因为本人是数二考生,所以数学部分主要聚焦于微积分学和线性代数笔记体验的改良。需要做其他科目笔记的朋友可以基于同样的思路自己改造。
开始
mk和dm是公式块的触发器。为了让行内公式按正常尺寸显示,改动mk这一条:
{trigger: "mk", replacement: "$\\displaystyle $0$", options: "tA"},
加一个\displaystyle就可以了。然后因为要经常用,给\displaystyle也加一个snippet。
{trigger: "dsp", replacement: "\\displaystyle ", options: "mA"},
注意trigger各个字母在键盘上的位置,不要选择难打的字符,比如@符号这样的边缘上的字符。trigger也不要太长,2~4个最佳。
希腊字母
默认的希腊字母都是@
+小写字母作为trigger,@
不好打,所以都换成双击某个字母。比如 $\alpha$ 就从 $@a$ 换成 $aa$ 。
// Greek letters
{trigger: "aa", replacement: "\\alpha", options: "mA"},
{trigger: "AA", replacement: "\\alpha", options: "mA"},
{trigger: "bb", replacement: "\\beta", options: "mA"},
{trigger: "BB", replacement: "\\beta", options: "mA"},
{trigger: "cc", replacement: "\\chi", options: "mA"},
{trigger: "CC", replacement: "\\chi", options: "mA"},
{trigger: "gg", replacement: "\\gamma", options: "mA"},
{trigger: "GG", replacement: "\\Gamma", options: "mA"},
{trigger: "dd", replacement: "\\delta", options: "mA"},
{trigger: "la", replacement: "\\lambda", options: "mA"},
{trigger: "LA", replacement: "\\Lambda", options: "mA"},
{trigger: "tt", replacement: "\\theta", options: "mA"},
{trigger: "DD", replacement: "\\Delta", options: "mA"},
但是像比如$\xi$这样的符号,本来就很方便打出来,就不需要再设置snippet了。
下标
下标是我们最常用的latex符号,但是输入一次下标需要按键至少四次。数字下标直接输入在后就能转换,但对于常用的带下标的字母情况,比如i、j,默认给出了一些方案
$$
a_i, b_i, a_j, b_j, a_n, b_n, x_i, x_j, x_n
$$
实际使用下来就可以发现,主要就是a、b、x、y、\xi、\lambda这几个符号经常需要字母下标。给这几个分别设置方便的字母下标snippet就好了。下面部分注释掉的snippets供选用
{trigger: "xnn", replacement: "x_{n}", options: "mA"},
{trigger: "ann", replacement: "a_{n}", options: "mA"},
{trigger: "\\xi i", replacement: "x_{i}", options: "mA"},
{trigger: "aii", replacement: "a_{i}", options: "mA"},
{trigger: "xjj", replacement: "x_{j}", options: "mA"},
{trigger: "ajj", replacement: "a_{j}", options: "mA"},
{trigger: "xp1", replacement: "x_{n+1}", options: "mA"},
{trigger: "ap1", replacement: "a_{n+1}", options: "mA"},
{trigger: "ynn", replacement: "y_{n}", options: "mA"},
{trigger: "yii", replacement: "y_{i}", options: "mA"},
{trigger: "yjj", replacement: "y_{j}", options: "mA"},
// {trigger: "jj", replacement: "_{j}", options: "mA"},
// {trigger: "ii", replacement: "_{i}", options: "mA"},
// {trigger: "nn", replacement: "_{n}", options: "mA"},
// {trigger: "npp", replacement: "_{n+1}", options: "mA"},
// {trigger: "ipp", replacement: "_{i+1}", options: "mA"},
// {trigger: "jpp", replacement: "_{j+1}", options: "mA"},
数列
很少有人想过给数列设置快捷键,但实际上其却经常被用到。
第二行、第三行那样的公式可以直接设立单独的snippet,这就涉及了关于正则表达式的一部分知识。我不在这里多讲如何写正则表达式,只举个例子。比如下面这个数列:
它的trigger是([A-Za-z]+)ppp
,我输入的是justppp
。
[A-Za-z]
的意思是「任意大写或者小写英文字母」,而+号的意思是前面的字符是1个或者多个。just显然就符合这个规则;ppp则是plus plus plus的意思,是trigger识别的标志。它的snippet是:
{trigger: "([A-Za-z]+)ppp", replacement: "[[0]]_1+[[0]]_2+\\cdots+[[0]]_{${0:n}} ", options: "rm"},
[[0]]
的意思是第0处正则表达式匹配到的内容,${0:n}
的意思是第0次按下tab,光标应该跳跃到的位置,n是默认值。至此本插件的两个特殊符号就登场了
- $0, $1, ${2:xxx},光标跳跃位置
- [[0]], [[1]], [[2]],正则表达式匹配内容
这两个看起来都没做什么,好像用鼠标点击一下也能做到,但是我们要改善效率的话,这就不可或缺了。毕竟按一下tab键比离开键盘去拿鼠标,或者用左右键要快得多。
矩阵
矩阵难打,即使有很多加速措施也快不过手写。我们能做的也就是快速打出矩阵环境,避免花太多工夫在重复的地方。这方面而言,默认的配置就已经很好了,能在四个字母而且不难记忆的情况下写出圆括号矩阵(pmat)、方括号矩阵(bmat)、花括号矩阵(Bmat)、行列式(vmat)、范数(Vmat)。所以这里只需要改良一下无边框的矩阵(通常用于换行、分段函数)以及单侧大括号就好了。
首先,给无边框array加上左对齐标志,写死在里面就可以了。因为事实证明居中和右对齐几乎不会用。
{trigger: "array", replacement: "\\begin{array}{l}\n$0\n\\end{array}", options: "mA"},
然后是左侧、右侧的单侧大括号,用于分段函数之类的需要单独一个snippet
{trigger: "larr", replacement: "\\left\\{\\begin{array}{l}\n$0\n\\end{array}\\right.", options: "mA"},
{trigger: "\\lambda rr", replacement: "\\left\\{\\begin{array}{l}\n$0\n\\end{array}\\right.", options: "mA"},
{trigger: "rarr", replacement: "\\left.\\begin{array}{l}\n$0\n\\end{array}\\right\{", options: "mA"},
\lambda rr是用于缓和之前把lambda的snippet设为la的情况下使用的。
有了这些措施之后处理矩阵就会比之前快很多。但是这仍然是慢于手写很多的,尤其是遇到大型矩阵。这种情况下建议还是作图或者截图,这样会比用latex好。
大型公式
某些重复使用但又格式复杂的大型公式也可以写进来拥有自己的snippet。比如泰勒公式,这个作者已经给出,trigger是tayl。
可以根据自己的需要制造更多大型公式。比如我经常使用拉格朗日中值定理,于是就可以用一个这样的snippet:
{trigger: "lagrange", replacement: "${0:f}'(${1:\\xi})=\\frac{${0:f}(${2:b})-${0:f}(${3:a})}{${2:b}-${3:a}}", options: "m"},
一阶线性常微分方程的解
// 一阶线性常微分方程的解
{trigger: "linear", replacement: "e^{-\\int ${0:P(x)} d${2:x}}\\left[\\int ${1:Q(x)}e^{\\int ${0:P(x)} d${2:x}} d${2:x} +C \\right] $3", options: "m"},
动态规划状态转移方程
// 状态转移方程
{trigger: "transition", replacement: "${0:dp}=\\left\\{\\begin{array}{l}\n$1\n\\end{array}\\right.", options: "m"},
物理学、化学相关邻域笔者并不熟悉,可以请更多专业领域的朋友们来负责。我仅仅在此给出几个例子,要适合自己还是需要自己配置。
数学之外
因为本插件也可以在公式块外发挥作用,所以我们仍然有操作空间。比如ob最近支持了原生callouts,就很适合用文本拓展的思路来加速书写速度。
// Callouts
{trigger: ">note", replacement: "> [!NOTE]\n> $0", options: "tA"},
{trigger: ">abs", replacement: "> [!ABSTRACT]\n> $0", options: "tA"},
{trigger: ">sum", replacement: "> [!SUMMARY]\n> $0", options: "tA"},
{trigger: ">tldr", replacement: "> [!TLDR]\n> $0", options: "tA"},
{trigger: ">info", replacement: "> [!INFO]\n> $0", options: "tA"},
{trigger: ">todo", replacement: "> [!TODO]\n> $0", options: "tA"},
{trigger: ">tip", replacement: "> [!TIP]\n> $0", options: "tA"},
{trigger: ">hint", replacement: "> [!HINT]\n> $0", options: "tA"},
{trigger: ">imp", replacement: "> [!IMPORTANT]\n> $0", options: "tA"},
{trigger: ">suc", replacement: "> [!SUCCESS]\n> $0", options: "tA"},
{trigger: ">check", replacement: "> [!CHECK]\n> $0", options: "tA"},
{trigger: ">done", replacement: "> [!DONE]\n> $0", options: "tA"},
{trigger: ">ques", replacement: "> [!QUESTION]\n> $0", options: "tA"},
{trigger: ">help", replacement: "> [!HELP]\n> $0", options: "tA"},
{trigger: ">faq", replacement: "> [!FAQ]\n> $0", options: "tA"},
{trigger: ">warn", replacement: "> [!WARNING]\n> $0", options: "tA"},
{trigger: ">atten", replacement: "> [!ATTRENTION]\n> $0", options: "tA"},
{trigger: ">caut", replacement: "> [!CAUTION]\n> $0", options: "tA"},
{trigger: ">fail", replacement: "> [!FAIL]\n> $0", options: "tA"},
{trigger: ">miss", replacement: "> [!MISSING]\n> $0", options: "tA"},
{trigger: ">danger", replacement: "> [!DANGER]\n> $0", options: "tA"},
{trigger: ">error", replacement: "> [!ERROR]\n> $0", options: "tA"},
{trigger: ">bug", replacement: "> [!BUG]\n> $0", options: "tA"},
{trigger: ">exam", replacement: "> [!EXAMPLE]\n> $0", options: "tA"},
{trigger: ">quote", replacement: "> [!QUOTE]\n> $0", options: "tA"},
{trigger: ">cite", replacement: "> [!CITE]\n> $0", options: "tA"},
此外我想到的是某些车轱辘话也可以用LaTeX Suite解决,但是如果都是车轱辘话,那还有什么写下来的必要呢?
此外,LaTeX Suite终究不是代码驱动的,不能完成某些逻辑操作。如果能满足的话,用两个数字代表行数、列数,加上Table Enhancer,就可以轻松实现完美的表格编辑功能,解决OB长久以来的一大痛点。
结语
本文可以算作是 我如何用 ob 做考研笔记的下文,仍然继承了之前的实用主义风格,只关注真正写笔记、写文章时会遇到的问题。当前本人的笔记库约为24万字(不统计标点符号,英文按照单词数统计),共计198个文件。以我的体验,我可以负责任地说当前这种简练的风格是能够适应如此规模的数据的。如果之后遇到更多问题,我也仍然会把解决思路写成文章发表出来。