VSCode 中的 LaTeX 分数片段

Sha*_*uck 3 latex visual-studio-code vscode-snippets

语境

在过去的 18 个月里,我一直使用 VSCode 的 LaTeX Workshop 扩展来满足我的所有 LaTeXing 需求。到目前为止,我主要将其用于较长篇幅的文章和报告,偶尔也用于课堂笔记。到目前为止,我只需要几个自定义宏就可以使其实时运行得足够好(线性代数排版非常容易)。然而,当我继续学习不同的课程时,我希望通过在 VSCode 中实现Gilles Castel 出色的基于 Vim 的工作流程来扩展我的实时能力。不幸的是,VSCode 似乎掩盖或缺乏(默认情况下)Castel 使用的大量功能(尤其是与片段相关的功能)。

我的问题

出于这篇文章的目的,我想重点关注他的分数宏(我相信,如果我能让这个工作正常,我就能让其余的大部分工作正常)。基本上,问题似乎是 VSCode 的片段功能相当有限,特别是与 Vim 的UltiSnips相比。Castel 使用 UltiSnips 定义了一个自动扩展宏(我不确定 VSCode 是否支持自动扩展片段),当/键入 a 时,它会获取前面的单词(如果存在括号,则为单词)并将其转换为 LaTeX分数格式。例如:

//             --> \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)}{}
Run Code Online (Sandbox Code Playgroud)

如何在 VSCode 中实现此行为?

我的线索

我花了很多时间研究这个问题,我有充分的理由相信这是可能的,并且同样有充分的理由相信我的问题的任何成功答案都必须 ELI5 给我——这种软件定制并不是我的菜,但我绝对愿意学习!

首先,有两个有前景的 VSCode 扩展可以实现 UltiSnips:VsnipsHyperSnips。Vsnips 看起来不错,但它似乎依赖于对 UltiSnips 的现有熟悉程度以及如何为您的特定计算机配置 UltiSnips(如果这确实很重要,我使用 2019 款 MacBook Pro,并且我的软件是最新的 [macOS Catalina 10.15] .5 截至本文])。关于 HyperSnips,我什至无法弄清楚那么多——两者都没有很好的文档,而且没有一个文档是为我这个水平的读者编写的。

虽然我说过 VSCode 的内部片段引擎似乎相当有限,但我可能是错的。它似乎与另一个名为TextMate的片段引擎交互。

目前我能想到的就是这些。如果我可以提供任何进一步的信息,请告诉我!谢谢!

Mar*_*ark 7

安装HyperSnips后,使用其命令HyperSnips: Open snippets directory打开要放置代码片段的目录。

中的片段all.hsnips将在所有语言文件中可用。您还可以将片段放入类似目录latex.hsnipsLatex.hsnips同一目录中,这两个版本都适合我。


修改Castel 指南中的代码,将其放入您选择的<language>.hsnips文件中:

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

snippet `((\d+)|(\d*)(\\)?([A-Za-z]+)((\^|_)(\{\d+\}|\d))*)/` "Fraction no ()" A
\frac{``rv = m[1]``}{$1}$0
endsnippet
Run Code Online (Sandbox Code Playgroud)

请注意,内插代码进入双反引号“``”,并且该代码中的值必须分配给rv(返回值)。无论分配给什么,rv都会出现在代码片段输出中。另请注意,上面的代码片段中还有其他制表符$1, $2 and $0- 可以通过数组中的内插代码访问这些值t,但这里不需要。

然后这是最终的片段,适用于“前缀”中嵌入括号的更困难的情况,例如(1 + (2 + 3))/. 我认为(1 + (2 + 3))/就像传统的 vscode 片段前缀一样,除了您可以使用正则表达式作为前缀!正则表达式前缀/触发器必须在反引号内。

snippet `^.*\)/` "Fraction with ()" A
``
    let str = m[0];
    str = str.slice(0, -1);
    let lastIndex = str.length - 1;

    let depth = 0;
    let i = str.length - 1;

    while (true) {
        if (str[i] == ')') depth += 1;
        if (str[i] == '(') depth -= 1;
        if (depth == 0) break;
        i -= 1;
    }

    let results = str.slice(0, i) + "\\frac{" + str.slice(i+1, -1) + "}";
    results += '{' + snip.tabstop(1, "") + "} " + snip.tabstop(0, "");
    rv = results;
    ``
endsnippet
Run Code Online (Sandbox Code Playgroud)

^.*\)/是前缀/触发器。当您键入该模式时,扩展程序会查看您的所有代码,该模式基本上至少是)a 之前的一个/,然后将这些代码之前的所有内容与前一个单词边界相匹配。然后该匹配信息在匹配代码中可用,如下所示m[0]。您可以使用带有前缀/触发器的捕获组并在m[1]等中访问它们,但这在这里不需要。

另请注意,在此代码片段中,我们使用snip.tabstop(num, str)而不是$1, $2, $0. 每当我们想要在字符串中包含制表符时,包含带有$符号的制表符是不容易实现的,因为它们被字面解释为字符。是num制表位的编号,即 ,$1 = snip.tabstop(1, "")而字符串参数表示该制表位的默认值(此处为空)。

如您所见,要使此扩展正常工作,要插入的代码必须是 javascript。

第一组反引号的位置很重要!这里

snippet `^.*\)/` "Fraction with ()" A
``
   <other code indented here>
   ``  <indented or flush left, didn't seem to matter in my testing>
endsnippet
Run Code Online (Sandbox Code Playgroud)

缩进的代码在我看来更容易阅读,但输出也会缩进,除非第一组反引号不缩进(当然除非您希望输出缩进)。如果这是一个“怪癖”或按计划进行,我不这么认为。但第一组反引号的位置似乎决定了输出的位置。

最后一个片段的主体(同样是您链接到的指南中的代码,但由我从他的 python 代码翻译为 javascript)只是计算出要回溯(逐个字符)多远才能获得偶数个括号。输入前缀/触发器的任何前面部分都位于该\frac部分之前。

更改此文件后,请始终运行命令HyperSnips: Reload Snippets以确保它们已准备好立即进行测试。

实际演示:

HyperSnips 乳胶分数演示