發現一個小眾但是巨好用的VSCode自動補全插件:HyperSnips。
作者顯然受到了 這位小哥 的啟發,將 Vim Ultisnips 的大部分功能搬到了VSCode上。並用 JavaScript 重寫了 Python 的部分程序。有了這個插件,你不需要使用令人望而生畏的Vim,就能愉快地寫 LaTeX/markdown,甚至在數學課上用 markdown/LaTeX 做筆記。
更有趣的是,由於這個插件具有可編程能力,你甚至可以拿到 VSCode 的 API 接口,用這個插件寫“插件”,或者干一些有趣的事情。
你可以實現 這篇文章 中的幾乎所有功能,足以看出其強大:
-
一個自動變大的盒子:
-
無需按下Tab,就可以自動給\(\LaTeX\)的命令加斜杠
\
-
可以自動展開分數(
//
⇨\frac{}{}
、1/
⇨\frac{1}{}
) -
使用后綴片段修改字母樣式(
Ecal
⇨\mathcal{E}
、q_idot
⇨\dot{q_i}
等等);
-
自動生成m*n矩陣:
以上演示使用markdown完成(不是LaTeX,但是LaTeX同樣可以使用該插件),預覽是Markdown Preview Enhanced插件。這是非常好用的markdown同步預覽插件,只是更新有些慢。
使用方法
安裝
- 下載VSCode,在插件市場安裝 HyperSnips。
注意安裝后右鍵插件選擇“安裝另一個版本”,安裝0.2.3版本。0.2.4有一些bug,會導致無法使用,暫時不要更新。(2021/12/16)- 注:最新的版本0.2.7更新了光標插入的語法,與之前的版本有不兼容之處,在 我的GitHub 上有兩個不同版本的文件,可分別取用。(2022/2/8)
- 按下 Ctrl+Shift+P ,搜索
Open Snippets Directory
,打開存放代碼片段的文件夾
新建*.hsnips
文件,如希望在LaTeX補全就建立latex.hsnips
,希望補全markdown就建立markdown.hsnips
。如果希望使用全局片段,則建立all.hsnips
。
然后就可以輸入片段了。可以按照下面的基本用法自定義片段,也可以來這里 hsnips-snippet 下載一些自用的片段來試用。
基本用法
輸入片段的方法基本按照 Vim Ultisnips 的方法,參照幫助文檔。這和VSCode自帶的片段是對應的。同樣也可以使用占位符,可以使用Tab跳來跳去(如$1
表示第一個Tab Stop,$2
第二個,特別的,$0
是最后一個,${1:text}
表示第一個占位符中有預設的文本)。
舉例:
snippet hello "greeting"
Welcome to ${1:HyperSnips}!
endsnippet
snippet
和endsnippet
標志了片段的開始和結束,hello是觸發字符。"greeting"是片段名稱(沒什么用)。第二行就是要展開的片段。如果用 VSCode 內置片段的寫法(json),就是:
{
"greeting":{
"perfix":"hello",
"body":"Welcome to ${1:HyperSnips}!"
}
}
基本的功能都有的。輸入片段(或者其中的幾個字母),按下 Tab ,你可以得到:
自動展開
使用 A 標記,可以自動展開:
snippet hello "greeting" A
Welcome to HyperSnips!
endsnippet
這樣只要輸入觸發字符,整個片段就會立即展開。上面的快速輸入,幾乎都是使用了A
標記。另外還有i
、w
、M
等標記,詳見幫助文檔( @infinity1900 翻譯,感謝!):
A:自動展開。默認是按下tab時觸發代碼段,Flag 設置為 A,代碼段將在 Trigger 匹配后立即激活,這對於 regex 正則表達式代碼段特別有用。
i: 詞內展開*。默認情況下,觸發器僅在前面帶有空格字符時才匹配觸發。如果設置 為 i,則不管前面的字符如何,都會觸發帶有此選項的代碼段,例如,可以在單詞的中間觸發代碼段。
w: 單詞邊界*。使用此選項時,觸發器將在單詞邊界處匹配。
M: Multi-line mode - By default, regex matches will only match content on the current line, when this option is enabled the last hsnips.multiLineContext lines will be available for matching.
- 只對非正則表達式有效
內嵌代碼、動態片段、正則識別
HyperSnips支持在片段中使用JavaScript代碼。如下面可以返回當前日期:
snippet dategreeting "Gives you the current date!"
Hello from your hsnip at ``rv = new Date().toDateString()``!
endsnippet
其中,JavaScript片段使用四個 ` 括起來,其中rv
表示返回的片段。
- 一開始那個小盒子,是插件中的示例代碼:
snippet box "Box" A
``rv = '┌' + '─'.repeat(t[0].length + 2) + '┐'``
│ $1 │
``rv = '└' + '─'.repeat(t[0].length + 2) + '┘'``
endsnippet
同樣支持正則表達式。如“自動數字下標”的片段是這樣寫的:
snippet `(?<=[A-Za-z])(\d)` "auto subscript" iA
_``rv = m[1]``
endsnippet
效果就是:a1
⇨a_1
。m[1]
表示正則識別處的第一個組合。除此之外還有t
變量,用的不多。
- 自動展開分數 會麻煩一些。
//
⇨\frac{}{}
最容易,直接寫就行;
如果要實現1/
⇨\frac{1}{}
:
snippet `((\d+)|(\d*)(\\)?([A-Za-z]+)((\^|_)(\{\d+\}|\d))*)/` "Fraction no ()" A
\frac{``rv = m[1]``}{$1}$0
endsnippet
而如果要實現(1+2)/
⇨\frac{1+2}{}
,可能還需要借助JavaScript:
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
這和原作者的邏輯是一樣的,但是轉寫成了JavaScript。
全局程序
在.hsnips
文件開頭加入如下片段:
global
endglobal
即可在其中定義全局函數。這里定義的函數可以在當前文件中的任何片段中使用。
比如生成矩陣的函數:
// generate matrix
function gen_matrix(nrow, ncol, index) {
let results = "\n";
let order = 1;
for (var i=0; i<nrow; i++){
results += ' ';
for(var j = 0;j <ncol;j++){
results += "$" +(order ).toString() + ((j == ncol -1) ? " \\\\\\" : " & ");
order ++;
}
results += index ? "\n" : "";
}
return results;
}
再使用以下片段:
snippet `(bm|pm|m|vm)at([1-9])([1-9])` "matrix" iA
\begin{``rv = m[1]``atrix}``
rv = gen_matrix(m[2],m[3],1);
``\end{``rv = m[1]``atrix}$0
endsnippet
則bmat33
就能給出一個3*3 的\bmatrix
矩陣:
環境/作用域
有時我們希望片段只有在數學環境中展開,或者只在文本環境中展開。比如,你不希望在正文中寫“green”的時候突然展開成“gr_en"吧(下標自動展開)。HyperSnips提供了這一方法:
在開頭的global 中,定義如下:
function math(context) {
return context.scopes.some(s => s.includes("math"));
}
這個作用是,檢查當前文本的標記和作用域(token & scope),如果其中含有math
,判斷為數學環境並返回true
。然后再我們需要的片段前面加上:
context math(context)
就可以讓片段只在數學環境中展開。如前面片段如果寫成:
context math(context)
snippet // "frac" A
\frac{$1}{$2}
endsnippet
效果:
檢查哪一種環境,可以按下Ctrl+Shift+P ,使用開發人員: 檢查編輯器標記和作用域(Inspect Editor Tokens and Scopes) 命令,得到如下窗口,框中就是作用域(scope)。這個標志了當前所處的環境。
更多玩法
上述我們提到,HyperSnips支持Javascript,且放在global中的函數可以全局通用。考慮到VSCode插件本質上也是JavaScript寫的,那么一些小插件能做的事情,HyperSnips為什么不能做?
在global區域中放置如下代碼:
const vscode = require("vscode");
var editor=vscode.window.activeTextEditor
var document=editor.document
然后發現,你可以使用 VSCode 的 API 接口了!
可能只需要幾行代碼,你就能做出一個“插件”!
好么,既然能夠編程了,那就……沒有什么好怕的了( ̄︶ ̄*))
- 比如,四行代碼加在
global
區域中:
setInterval(function(){myTimer()},1000);
function myTimer(){
vscode.window.setStatusBarMessage(new Date().toLocaleTimeString());
}
你就得到了一個在狀態欄顯示時間的小插件!
- 再比如,在global 中加入如下函數
function cycle(arr,str){
let count = 0 ;
while (str!=arr[++count]&&count<arr.length);
return arr[(count+1)%arr.length]
}
然后考慮到分號;在LaTeX中幾乎用不到,而且Tab太容易和其他快捷鍵沖突,於是放一些類似於這樣的片段
snippet `(\\rightarrow|\\Rightarrow|\\longrightarrow|\\Longrightarrow|\\to|\\implies)(| );` "change ⇨" Ai
``
let r=["\\rightarrow","\\Rightarrow","\\longrightarrow","\\Longrightarrow","\\to","\\implies"];
rv=cycle(r,m[1])+m[2];
``
endsnippet
然后有趣的就來了:
按下分號,可以換一個樣式。雖然沒什么用,但是很好玩啊哈哈哈哈 靈感來自 @張同學
甚至括號也不在話下(當然程序就更長一些):
- 再比如,原生的hyperSnips插件沒有提供
{VISUAL}
變量(當前選中的文本),不妨利用API寫一個:在global里加上:
// get ActiveTextEditor
vscode.window.onDidChangeActiveTextEditor((e) => {
editor=vscode.window.activeTextEditor;
document=editor.document
});
let selectedText = "";
// get selected text
vscode.window.onDidChangeTextEditorSelection((e) => {
const newSelectedText = e.textEditor.document.getText(e.selections[0]);
if (newSelectedText) {
selectedText = newSelectedText;
}
});
// return text
function VISUAL() {
let sText=selectedText.replace(/\\\\/g,"\\\\\\ ").replace(/\}/g,"\\}");
selectedText="";
return sText;
}
然后比如配置如下的片段:
context math(context)
snippet `Aln` "align" iA
\begin{aligned}
``rv = VISUAL();``$0
\end{aligned}
endsnippet
就可以使用了
盡管有時有bug,但是至少管用啊
所以現在,僅剩的限制就是想象力了吧
Bug
- 當疊加了太多的占位符時,可能出現多光標。這是VSCode自身的Bug
- 偶爾的環境/作用域識別錯誤,需要重開一下文件
參考:
-
HyperSnips作者Github:draivin/hsnips
-
@infinity1900 一位師兄的經驗,之前似乎是知乎上唯一一篇關於Hypersnips的介紹,受他的啟發,才發現了這個插件。
師兄也用JavaScript實現了許多片段,包括上述引用的部分片段。再次表示感謝。infinity1900:【LaTeX 編輯環境搭建】TeX Live+VS code+HyperSnips(for math)插件
-
@一只方橙 君的markdown筆記,非常感謝這位同學,提供了大量片段,而且一同折騰了不少奇奇怪怪的HyperSnips使用方法.
一只方橙:教程向: 在 VSCode 中用 Markdown 做「數字化」學習筆記
- 他的片段配置:
OrangeX4/OrangeX4-HyperSnips - 他之前仿照Hypersnips寫過一個Hypersnips For Math插件,在原來的插件上實現了{VISUAL}變量和數學環境支持,但是此后官方也實現了這些。在插件市場可以很容易搜到這個插件。
- 他的片段配置:
-
自己的(markdown)配置,有的片段寫得很呆,但也是能用就行。歡迎Star⭐~
歡迎關注、點贊、交流~~~
原文鏈接:https://www.cnblogs.com/yf-zhao/p/15699500.html
轉載請注明出處!!