VSCode 高级自定义代码段

VSCode Advanced Custom Snippets

上下文

在过去的 18 个月里,我一直在使用 VSCode 的 LaTeX Workshop 扩展来满足我所有的 LaTeX 需求。到目前为止,我主要将它用于较长篇幅的文章和报告,偶尔在 class 中用于做笔记。到目前为止,我已经能够通过几个自定义宏(线性代数排版非常容易)使其实时工作得足够好。然而,随着我转向不同的 classes,我希望通过在 VSCode 中实现 Gilles Castel's excellent Vim-based workflow 来扩展我的实时能力。不幸的是,VSCode 似乎模糊或缺少(默认情况下)Castel 使用的大量功能(尤其是与片段相关的功能)。

我的问题

出于此 post 的目的,我想重点关注他的 fraction macro (I believe that if I can get this working, I can get much of the rest of it working). Basically, the problem appears to be that VSCode has fairly limited snippets functionality, especially compared to Vim's UltiSnips。使用 UltiSnips,Castel 定义了一个自动扩展宏(我不确定 VSCode 是否支持自动扩展片段),当输入 / 时,采用前面的单词(或括号中的单词)存在)并将其转换为 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)}{}

如何在 VSCode 中实现此行为?

我的线索

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

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

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

的片段引擎交互

我暂时只能想到这些。如果有任何我可以提供的进一步信息,请告诉我!谢谢!

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

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


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

snippet // "Fraction simple" A
\frac{}{}[=10=]
endsnippet

snippet `((\d+)|(\d*)(\)?([A-Za-z]+)((\^|_)(\{\d+\}|\d))*)/` "Fraction no ()" A
\frac{``rv = m[1]``}{}[=10=]
endsnippet

请注意,内插代码进入双反引号“``”并且该代码的值必须分配给 rv(return 值)。分配给 rv 的任何内容都将出现在代码段输出中。另请注意,在上面的代码片段中还有其他制表位 , and [=20=] - t 数组中的内插代码可以访问这些值,但您在这里不需要它。

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

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 += "{}[=11=]";
    rv = results;
    ``
endsnippet

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

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

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

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

IMO 缩进代码更易于阅读,但输出也会缩进,除非第一组反引号未缩进(当然除非您希望输出缩进)。如果这是一个“怪癖”或按计划,我不会那样说。但是第一组反引号的位置似乎决定了输出的位置。

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

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

实际演示: