原作者的小建议:这篇文章的最佳阅读姿势是在电脑上打开,并跟着文章一步步做下去。
创建项目
编写插件的第一步,就是创建我们的目录结构。这里我们使用一个叫做yo
的脚手架工具。yo
是一个富有高度扩展性的通用脚手架,可以通过插件来实现不同目录结构和初始选项。VS Code官方提供了名为generator-code
的插件,来进行插件目录的创建。首先我们需要安装yo
以及插件generator-code
。
1 | npm i -g yo generator-code |
在安装完成以后,使用下面的命令来创建目录结构。
1 | yo code |
在运行yo code
以后,它会问你下面这些问题。建议大家和我的输入保持相同,以免遇到意外。这里我们给伪代码的取名为zhuanzhuan
,并且告诉VS Code,当碰到一个文件的扩展名为.zhuanzhuan
或者.zz
时,就要运行我们这个插件。
输入完所有的选项以后,我们的插件目录就创建完成了。结构是这样子的:
1 | ├── CHANGELOG.md |
了解Scope
想要实现语法高亮,就需要将一串代码字符串,拆分成无数的小碎片,然后分别为它们指定color
等样式。这些拆分后的小碎片,被称作token
。这里的token
与jwt
中的token
不同,并没有安全、令牌等方面的意思,而是更偏向"符号"的含义。我们来看一个简单例子???来理解一下这段话。
1 | function sum(a: number, b: number): number { |
在这段TS代码中,我们定义了一个用于求和的函数。这时候我们按下VS Code快捷键,shift+cmd+p
,然后输入inspect editor tokens and scopes
,就可以看到每个token对应的类型。比如sum
这个token的类型就是function
,a
和b
的类型是parameter
。
另外从截图的底部中,我们还可以看到,每个token
还具有一个叫textmate scope
的属性。通俗地说,scope
指这个token
所处的位置。
比如下面的代码片段里,有两个a变量。第一个是一个变量声明,而第二个是函数的参数之一。虽然它们都可以被笼统地称为变量,但是因为所处的scope
不同(也就是处于不同环境),所以在VS Code中会被显示成不同的颜色。(由于微信文章中的代码高亮较弱,看不出区别。)
1 | const a = 1; |
支持注释
至此前置知识已经介绍完了,现在开始真正修改脚手架创建的代码。我们的第一个目标是,让zhuanzhuan
语言支持注释。
首先打开根目录下的language-configuration.json
文件,找到comments
字段,将lineComment
从默认的//
修改为注释:
。完成以后按下F5启动Debug程序,VS Code会打开一个新的窗口,且我们的插件会在其中生效。在新窗口中,我们随意打开一个空文件夹,然后新建名为fakeCode.zz
,并输入以下内容进行测试。
1 | 注释: 当我们用中文伪代码来描述执行过程的时候, |
这时,我们按下注释转换的快捷键cmd+/
,就会惊讶地发现,VS Code会为你自动转换注释内容,在这之间转换:注释:具体内容
⇔具体内容
。
这样,我们的Hello World项目就完成了,开始做稍微复杂一些的事情。打开/syntexes/zhuanzhuan.tmLanguage.json
文件,将这个文件的所有内容替换成下面的内容:
1 | { |
第一眼看的时候都会懵的,我们慢慢理解一下这到底是什么意思。
-
patterns & repository
repository是规则的仓库,它规定了该条规则如何识别其适用的对象。而patterns则是规定了规则仓库中,哪些规则是需要生效的。所以如果我们想要加一条新的规则,需要在repository中加规则的内容,并在patterns中将这条规则
include
,不然即使在repository添加了规则也不会生效。 -
comments
这是我们加入的自定义规则,comments是规则的名字。
-
begin & end
决定这条规则的适用对象。这里我们将
注释:
开头,回车符结尾的这部分字符作为适用对象。 -
beginCapture & endCapture & name
这三个属性,代表着我们赋予适用对象的scope名称。比这样一条注释"注释:不要试图重构这个方法,不然你会虚度一天的光阴"。,对应到我们这条规则,就是
注释:
这部分被赋予了punctuation.comment.open
scope,不要试图重构这个方法,不然你会虚度一天的光阴。scope为punctuation.comment
,最后的回车符scope为punctuation.comment.open
。
然后切换到刚才使用F5
打开的Debug窗口,按下cmd+shift+p
,运行reload window
,让我们的修改生效,就可以看到scope名称的变化:
支持关键字
关键字同样是编辑这个文件/syntexes/zhuanzhuan.tmLanguage.json
,在repository
中新加入keywords
规则(记得在顶部的patterns中include它):
1 | { |
解释一下这里的意思:
match
这是一个正则,如果碰上如果|遍历|结束|打印|函数
其中之一,就将它标记为关键字。name
这些关键字对应的scope是什么。
效果如图:
从图中我们可以看出,正则中匹配的字符(如果、遍历、函数等)已经被一一高亮了。不过你的VS Code中不一定是蓝色,这取决于你当前使用的主题。
支持字符串
接着我们让zhuanzhuan
语言支持字符串功能,同样是修改json文件。
1 | { |
就像JS中使用单双引号和模板字符串作为字符串的标志,为了体现zhuanzhuan
语言的不同之处,我们使用书名号,而不是单双引号,来标志一个字符串。例如《xxxx》
,它被分成了《
xxxx
》
三个部分,这三个部分有各自的scope
,对应关系如下:
- 《
string.quoted.book.open
- xxxx
string.quoted.book.zhuanzhuan
- 》
string.quoted.book.close
为了看到修改后的效果,需要在调试窗口中,cmd+shift+p
并运行reload window
。重载后的效果是这样的:
这时候第13行发生了变化,从原来的黑色,变成了绿色。
深入理解scope
看到效果以后,再回过头看那份json文件,它到底表达了什么意思?
首先我们规定了顶层的scope
名字叫source.zz
。也就是说,当我们新建了.zz
结尾的文件,开始写代码,这时所有的代码都处在顶层scope
。
patterns
属性规定了在顶层scope
中,有哪些方式可以开辟一个子scope
。patterns
数组inclucde
(即引入)了名为strings
和keywords
的规则,这些规则被放在了repository
(也就是仓库,一个规则的仓库)。
1 | { |
在repository
中,以strings
规则为例,当VS Code解析引擎遇到以“《”开头,“》”结尾的token
时,中间的内容会被认为是字符串。也就是说,我们让书名号具备了和JS中的单双引号相同的功能。字符串的scope
变成了我们规定的string.quoted.book.zhuanzhuan
。我们可以通过inspect editor tokens and scopes
命令来验证这一点。
图片中,xxxx
所属的scope
有两个,一个是constant.character.escape.zhuanzhuan
,另一个就是根scope
。一个token
往往拥有多个scope
,就像字符串,同时处于根scope
和书名号创建的一个scope
。
在上文的json文件中,还有一个叫keywords
的属性,当有字符串满足match
字段中的正则表达式时,会被认为是一个关键字。
事实上,当我们把上文的json规则进行更多的扩展和嵌套,就会越来越接近现流行的其他语言,存在无数的嵌套。一个token
会属于无数的scope
。
那么问题来了,这些scope
的作用是什么?我们花了很多的力气去定义json格式,来让不同位置的token
拥有不同的scope
。这样我们就拥有了一个类似于CSS选择器的东西,我们可以为不同scope
指定不同的样式,从而让我们自创的语言高亮起来。
使用Scope
接下来我们要使用上文中定义的几个scope
。因为目前为止,我们只是重新定义了zhuanzhuan
语言中一部分情景下的scope
名称,我们可以利用这些自定义的scope
,做出更细致的高亮配置。
使用scope
的方式就是创建一个theme
类型的插件(没错我们要写第二个插件了)。这次我们需要cd到用户文件夹下的.vscode/extensions
,这样我们的主题就可以免安装,可以直接出现在VS Code主题列表中。
使用VS Code打开项目,然后编辑theme/zhuanzhuan-lang-theme-color-theme.json
文件,文件的结构是这样的:
1 | { |
其中,tokenColors
字段是我们需要关心的地方,它针对了不同的scope
,指定不同的样式。name
是这条规则的名字,可以随意命名,保证唯一性即可。scope
类似于CSS选择器,是规则应用的对象。settings
则是具体的样式。
然后我们在tokenColors
中,加上我们自定义的样式。
1 | { |
然后在zhuanzhuan-lang
插件的调试窗口,打开主题选择列表,选择zhuanzhuan-lang-theme
主题,就可以看到上面的三条规则对《商品id》
这部分生效了。
文字及其对应的scope和颜色如下:
《
-
- scope:
string.quoted.book.open
- 颜色:#33ec0e
- scope:
》
-
- scope:
string.quoted.book.close
- 颜色:#33ec0e
- scope:
xxxx
-
- scope:
string.quoted.book.open
- 颜色:#eb8837
- scope:
其他的scope可以自行挑选喜欢的色值一一定义,这里就不再重复罗列。
成果
经过上面一系列的努力,然后再添加亿点点细节,最终的效果就是下图。
总结
通过阅读文章,我们总共创建了两个VS Code插件。一个是语言支持插件,通过简单的配置,使zhuanzhuan
语言支持了中文关键字、书名号字符串以及中括号表示的变量。第二个是主题插件,为zhuanzhuan
语言中自定义的scope
提供了高亮规则。scope
名称,是连接两个插件的枢纽。
不过zhuanzhuan
语言离一门完善的语言还需要海量的工作,我们需要定义更多的scope规则,规则之间往往还存在复杂的嵌套关系。这篇文章只是讲了冰山露出海面的那一角。如果想深入学习这方面的知识,仍需参考VS Code官方的文档,以及学习编译原理相关知识。
另外附上文章中两个插件最终的代码:
https://github.com/inkyMountain/zhuanzhuan-lang
https://github.com/inkyMountain/zhuanzhuan-lang-theme
参考文档:https://code.visualstudio.com/api#vscode
文章来源:链接
因为我对这方面也不是太懂,市面上也已经有很多的轮子了,本着不造烂轮子误人子弟的原则,整理了这篇文章。
水平有限,若有错漏,敬请指正。
- Post link: https://www.darkfox.top/posts/7923a685/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.