首页 > 解决方案 > VSCode 高级自定义代码段

问题描述

语境

在过去的 18 个月里,我一直在使用 VSCode 的 LaTeX Workshop 扩展来满足我所有的 LaTeXing 需求。到目前为止,我主要将它用于长篇文章和报告,偶尔用于课堂笔记。到目前为止,我已经能够通过几个自定义宏(线性代数排版非常容易)使其实时工作得很好。然而,随着我进入不同的课程,我希望通过在 VSCode 中实现Gilles Castel 出色的基于 Vim 的工作流来扩展我的实时能力。不幸的是,VSCode 似乎隐藏或缺少(默认情况下)Castel 使用的大量功能(尤其是与代码片段相关的)。

我的问题

出于这篇文章的目的,我想把重点放在他的分数宏上(我相信如果我能让这个工作正常,我就能让剩下的大部分工作正常工作)。基本上,问题似乎是 VSCode 的片段功能相当有限,尤其是与 Vim 的UltiSnips相比。使用 UltiSnips,Castel 定义了一个自动扩展宏(我不确定 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)}{}

如何在 VSCode 中实现此行为?

我的线索

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

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

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

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

标签: visual-studio-codelatexvscode-snippets

解决方案


安装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

请注意,内插代码进入双反引号“``”,并且该代码的值必须分配给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 += "{$1}$0";
    rv = results;
    ``
endsnippet

^.*\)/是前缀/触发器。扩展程序会在您键入该模式时查看您的所有代码,该模式基本上至少)在 a 之前有一个/,然后将这些之前的所有内容与前一个单词边界匹配。然后该匹配信息在匹配的代码中可用m[0]. 您可以使用您的前缀/触发器获取捕获组并在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)只是计算出要回溯多远(逐个字符)以获得偶数个括号。输入前缀/触发器的任何前面部分都在该\frac部分之前。

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

实际演示:

HyperSnips 乳胶分数演示


推荐阅读