Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.5k views
in Technique[技术] by (71.8m points)

visual studio code - VSCode Advanced Custom Snippets

Context

For the past 18 months, I've been using the LaTeX Workshop extension of VSCode for all my LaTeXing needs. Up to this point, I've primarily used it for longer-form articles and reports, and sporadically for note-taking purposes in class. I've been able to make it work sufficiently well in real-time so far with just a few custom macros (Linear Algebra typesets pretty easily). However, as I move on to different classes, I'm looking to expand my real-time abilities by implementing Gilles Castel's excellent Vim-based workflow in VSCode. Unfortunately, VSCode seems to obscure or lack (by default) a great deal of the features (especially related to snippets) of which Castel makes use.

My Question

For the purposes of this post, I'd like to focus on his 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. Using UltiSnips, Castel has defined an auto-expanding macro (I'm not sure if VSCode supports auto-expanding snippets) that, when a / is typed, takes the preceding word (or words if parentheses are present) and transforms it into the LaTeX fraction format. For example:

//             --> frac{}{}
3/             --> frac{3}{}
4pi^2/        --> frac{4pi^2}{}
(1 + 2 + 3)/   --> frac{1 + 2 + 3}{}
(1 + (2 + 3)/) --> (1 + frac{2 + 3}{})
(1 + (2 + 3))/ --> frac{1 + (2 + 3)}{}

How can I implement this behavior in VSCode?

My Leads

I have spent a significant amount of time researching this, and I have strong reasons to believe that it's possible, and similarly strong reasons to believe that any successful answer to my question is going to have to ELI5 it to me -- this kind of software customization isn't exactly my cup of soup, but I'm definitely willing to learn!

First off, there are two promising VSCode extensions that may be able to implement UltiSnips: Vsnips and HyperSnips. Vsnips looks decent, but it seems to rely on an existing familiarity with UltiSnips and how to configure UltiSnips for your specific computer (if this does end up being important, I use a 2019 MacBook Pro and my software is up to date [macOS Catalina 10.15.5 as of this post]). I haven't been able to figure out even that much with regard to HyperSnips -- neither is very well documented, and none of the documentation is written for readers of my level.

Although I said that VSCode's internal snippets engine appears to be fairly limited, I may be wrong. It seems to interface with another snippets engine called TextMate.

That's all I can think to do for now. If there's any further information that I can provide, please let me know! Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After you install HyperSnips, use its command HyperSnips: Open snippets directory to open the directory where you will put your snippets.

Snippets in all.hsnips will be available in all language files. You can also put your snippets into something like latex.hsnips or Latex.hsnips in the same directory and both versions work for me.


Modifying the code from Castel's guide put this into your chosen <language>.hsnips file:

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

Note that interpolated code goes into double backticks "``" and the value from that code must be assigned to rv (return value). Whatever is assigned to rv will appear in the snippet output. Note also there are additional tabstops $1, $2 and $0 in the above snippets - there values can be accessed by the interpolated code within the t array but you don't need that here.

And then here is the final snippet that works for the harder case of embedded parentheses within your "prefix" like (1 + (2 + 3))/. I think of (1 + (2 + 3))/ just like a traditional vscode snippet prefix EXCEPT that you can use regex expressions as the prefix!! Regex prefixes/triggers must within backticks.

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

Here ^.*)/ is the prefix/trigger. The extension looks at all your code as you type for that pattern which is basically at least one ) before a / and then match everything before those to the previous word boundary. And then that match info is available within the matched code as m[0]. You can have capture groups with your prefix/trigger and access them in m[1], etc. but that isn't needed here.

As you can see, the code to be interpolated must be javascript for this extension to work.

The location of that first set of backticks is important! Here

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

the indented code is easier to read IMO, but the output will also be indented unless that first set of backticks is not indented (unless of course you want the output indented). I don't that if that is a "quirk" or as planned. But the position of that first set of backticks seems to determine the location of the output.

The body of this final snippet (again code from the guide you linked to, but translated to javascript by me from his python code) just figures out how far to backtrack (character by character) to get an even number of parentheses. Any preceding part of the input prefix/trigger goes before the frac part.

After making changes to this file, always run the command HyperSnips: Reload Snippets to ensure they are ready for testing immediately.

Demo in action:

HyperSnips latex fraction demo


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...