自制的第一个vscode语言扩展插件

自制的第一个vscode语言扩展插件

最近迷上了TRPG(Tabletop Role-playing game,桌上角色扮演游戏),即俗称的“跑团”。玩家在主持人的引导下,扮演自己的游戏角色进行冒险。在结束之后,会有想要将这个过程记录下来的欲望,从而有了各种各样的“跑团replay视频”。

制作跑团replay视频比较复杂,但回声工坊这一高效工具的出现,让这个过程变得非常简单,只需要找好媒体素材(角色立绘、背景图片、BGM、音效等)以及处理一下跑团Log(即跑团游戏记录)就可以很快输出一集视频。

为了更加方便跑团replay视频的制作,我编写了一个vscode插件——TRPG Replay Generator Log——来方便跑团Log的处理。

本文对编写这个插件的过程做一个记录和总结。

参考链接

目标

回声工坊这个软件所做的工作其实是根据输入的特定格式的Log文件、媒体定义文件、角色配置文件,将准备好的各种素材按照一定的规则拼接在一起。

回声工坊的Log文件的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 综合演示
<set:formula>:sincurve
<set:am_method_default>:<pass_up_black=20>
<set:tx_method_default>:<w2w=1>
<set:bb_method_default>:<replace=0>

<set:BGM>:BGM1
<set:speech_speed>:500
<background><replace=30>:bg1

[张安翔]:^本视频是DanDDXuanX 编写的#TRPG-Replay-Generator的基本演示工程。{SE1;*3.5}
[KP]:项目链接:#https://github.com/DanDDXuanX/TRPG-Replay-Generator{SE1;5}
<set:speech_speed>:300
<hitpoint>:(张安翔,10,10,9)

[张安翔]:^本工具基于python3 和 pygame2.0,在windows10系统上开发。#演示使用的媒体素材来自网络,侵删。{SE1;5}
[张安翔]:那么,演示开始。{SE1;5}

<dice>:(测试姓名1 力量,100,75,3),(测试姓名2 sancheck,100,60,25),(测试姓名3 图书馆,100,75,85),(测试姓名4 侦察,100,60,100)

<hitpoint>:(张安翔,10,9,5)

<dice>:(测试姓名1 力量,100,75,3),(测试姓名2 sancheck,100,60,25),(测试姓名3 图书馆,100,75,85),(测试姓名4 侦察,100,60,100)
<dice>:(1d8,8,NA,4),(1d4,4,NA,4)
<hitpoint>:(短,4,4,1)
<hitpoint>:(用来测试的一极长文本,4,1,4)

<background><cross=60>:bg2
<set:speech_speed>:250
<set:am_method_default>:<replace=0>
<set:tx_method_default>:<all=0>
<set:am_dur_default>:10
<set:tx_dur_default>:7

<set:BGM>:stop

[张安翔]:在对话行里建立最基本的发言小节,播放基本音效。{SE1;5}
[KP]<black>:使用切换效果修饰符,指定切换模式为渐隐。{SE1;5}

<hitpoint>:(张安翔,10,5,7)

[张安翔]<black=30>:在切换效果修饰符中,指定切换时间为30。{SE1;5}
[KP]<black>:使用文本效果修饰符,指定文字显示模式为逐字显示。<w2w>{SE1;5}
[张安翔]<black>:在文本效果修饰符中,指定单位时间为2。<w2w=2>{SE1;5}

<hitpoint>:(张安翔,10,7,4)

[张安翔,KP.double]<black>:在角色框里指定多位角色,实现多人同框。<w2w=2>{SE1;5}
[KP.double,张安翔]<black>:置于首位的角色为主要发言人,其余角色设置为半透明。<w2w=2>{SE1;5}

<set:BGM>:BGM1

[旁白]<black>:没有立绘的文本框同样以角色的形式在角色表里定义,可以作为旁白或者骰子使用。<w2w=2>

<hitpoint>:(张安翔,10,4,9)

<set:BGM>:stop

<background><black=60>:bg1

[张安翔(60),KP.double]<replace=0>:调整角色的透明度参数,手动设置立绘透明度。<w2w=5>{SE1;5}
[张安翔.scared,KP.double]:指明角色的subtype,展示差分立绘。<w2w=5>{SE1;5}
[张安翔,KP.double]:在发言文本中以井号作为换行符;#设置手动换行模式;#并逐行显示内容。<l2l=5>{SE1;5}

[旁白]<black>:———————演示结束——————

<hitpoint>:(张安翔,10,9,0)

回声工坊的作者用的是Sublime,所以他只写了适用于Sublime的语法高亮文件,用vscode编辑跑团Log的我就没法享受到语法高亮了。

于是我打算照着作者发在交流群里的Sublime语法高亮文件来写一个vscode语法高亮文件之类的东西,不过后来发现还是得开发一个语言扩展才行。

找到了这样一篇官网的文档:Visual Studio Code Syntax Highlight Guide-官网文档,接着开始开发vscode扩展。

搭建环境

安装脚手架工具

按照Your First Extension这篇文章中说的那样,首先安装Node.jsGit,接着用下面的命令安装Yeoman和VS Code Extension Generator:

1
npm install -g yo generator-code

Yeoman是个脚手架工具,可以用简单的指令快速搭建好开发环境。安装完毕之后,使用如下指令唤出一个命令行菜单,用上下箭头按键来选择要搭建的环境。

1
yo code

选择要搭建的扩展

借用官网文档中的图片:
选择要搭建的扩展

输入一些基本信息

输入一些基本信息

从回声工坊的作者写的sublime语法高亮文件RepGenLog.sublime-syntax中可以知道他将回声工坊的log文件的扩展名定义为“.rgl”,也就是Replay Generator Log的首字母缩写(一开始我还记错成“.rpl”,以为是“replay”的缩写),所以这个语言扩展的名字就定为了“TRPG Replay Generator Log”,语言名称也定为“rgl”

因为官网的文档已经说的很详细了,这部分就不细说了,不想单纯复制粘贴。

package.json文件

生成好的文件当中,比较重要的就是这个package.json文件了,因为这个扩展插件的基本信息都在这个里面,主要的文件的路径也是在这里配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"name": "trpg-replay-generator-log",
"displayName": "TRPG Replay Generator Log",
"description": "Syntax highlighting and code snippets for TRPG-Replay-Generator-Log language",
"version": "0.0.1",
"engines": {
"vscode": "^1.67.0"
},
"publisher": "yxChangingSelf",
"categories": [
"Programming Languages","Themes","Snippets"
],
"contributes": {
"languages": [{
"id": "rgl",
"aliases": ["rgl", "rgl"],
"extensions": [".rgl"],
"configuration": "./language-configuration.json"
}]
,"grammars": [{
"language": "rgl",
"scopeName": "source.rgl",
"path": "./syntaxes/rgl.tmLanguage.json"
}]
}
}

这里注意其中的contributes,这里配置了languagesgrammars两个属性。

前者配置了语言相关的内容,包括id、与该语言关联的文件扩展名以及语言配置文件的路径”./language-configuration.json”。

后者配置了语法相关的内容,包括展示给人看的语言名称、根scope以及语法定义文件的路径”./syntaxes/rgl.tmLanguage.json”。

因为目前只需要语法高亮,可以先看后面这个文件。

标记化(Tokenization)

要实现语法高亮,首先需要将这个语言标记化,学过编译原理的应该知道这就是词法分析那一步,也就是将输入划分为一个个词法单元(Token),告诉计算机每一个词法单元是哪一类。(Breaking text into a list of tokens)

Each token is associated with a scope that defines the context of the token. A scope is a dot separated list of identifiers that specify the context of the current token. The + operation in JavaScript, for example, has the scope keyword.operator.arithmetic.js.

这里的scope就是我们需要为token打上的“标记”了。

简单来说,就是我们需要先将各种token分好类,之后才方便将同类的东西染上相同的颜色。

TextMate

VS Code 使用 TextMate 语法作为语法标记引擎。为 TextMate 编辑器发明,由于开源社区创建和维护的大量语言包,它们已被许多其他编辑器和 IDE 采用。

TextMate的语法详见:TextMate language_grammars

详细的就不说了,只说用到的部分。

主要有两种形式:

1
2
3
4
{
"name": "comment.line",
"match": "^#.+$"
}

就是将name所定义的scope分配给match用正则表达式匹配出对应的token

另一种形式是:

1
2
3
4
5
6
7
8
9
10
11
{  
"name" :'string.quoted.double.untitled',
"begin" : "\"",
"end" : "\"",
"patterns" : [
{ "name" = 'constant.character.escape.untitled',
"match" = "\\."
}
]
}

beginend定义了首尾,将其中所有的内容分配为name定义的scope,其中的patterns属性是在被beginend框定的范围内继续进行标记。

调试

写好之后用f5运行一个加载了该扩展的vscode窗口来检查写的是否正确,可以在ctrl+shift+p调出来的命令面板中输入Developer: Inspect Editor Tokens and Scopes来开启Scope inspector这个东西,它可以显示光标所在位置的tokenscope,方便检查

最后写好的语法高亮文件

rgl.tmLanguage.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "rgl",
"patterns": [
{"include": "#comments"}
,{"include": "#dialog"}
,{"include": "#command"}
,{"include": "#error"}
],
"repository": {
"comments":{
"patterns": [{
"name": "comment.line",
"match": "^#.+$"
}]
},
"dialog":{
"patterns": [{
"name": "entity.name.function",
"begin": "(?=^\\[)",
"end":"\n",
"patterns": [{
"name": "variable.parameter",
"match": "^\\[([\\ \\w\\.\\;\\(\\)\\,]+)\\]"
}
,{
"name": "punctuation.colon",
"match": "\\B:"
}
,{
"name": "keyword.operator",
"match": "(\\^|#)"
}
,{
"name": "storage.type",
"match": "<\\w+(\\=\\d+)?>"
}
,{
"name": "string.quoted.double",
"match": "({.+})?$"
}
]
}]
},
"command":{
"patterns": [{
"name": "entity.name.function",
"begin": "(?=^<(set:[^>]+|background|dice|hitpoint)>)",
"end":"\n",
"patterns": [{
"name": "keyword.control",
"match": "^<(set:[^>]+|background|dice|hitpoint)>"
},{
"name": "punctuation.colon",
"match": "\\B:"
}
,{
"name": "constant.numeric",
"match": "\\b(-)?\\d+$\\b"
}
,{
"name": "keyword.declaration.function",
"match": "(linear|quadratic|quadraticR|sigmoid|right|left|sincurve)"
}
,{
"name": "storage.type",
"match": "<\\w+(\\=\\d+)?>"
}
,{
"name": "variable.parameter",
"match": "\\((.+?),(\\d+),([\\d]+|NA),(\\d+)\\)"
}
]
}]
},
"error":{
"patterns": [{
"name": "invalid.illegal",
"match": "^[\\t\\ ]+$"
}]
}
},

"scopeName": "source.rgl"
}

主题化(Theming)

标记好了所有的元素之后,就可以开始给它们染色了,也就是“使用主题或用户设置将标记映射到特定的颜色和样式”(Using themes or user settings to map the tokens to specific colors and styles)

其实没有这一步也是可以的,因为标记了这些token之后,运行时可以看到已经染好色了,这是正在使用的vscode主题根据这些标记染的色。

不过为了和回声工坊作者的语法高亮显示效果尽量一致,我自定义了一个主题rgl theme,调整配色。

因为不知道怎么在项目中创建主题,官网文档里面也只是说在一开始的时候用脚手架创建,我就用yo code命令创建了一个自定义主题的插件的项目,对比两个项目的不同,接着合并两个项目。

package.json里面新增了themes这一项,指定了主题定义文件的路径,最后再在自动生成的文件中修改需要修改的颜色就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"contributes": {
"languages": [{
"id": "rgl",
"aliases": ["rgl", "rgl"],
"extensions": [".rgl"],
"configuration": "./language-configuration.json"
}]
,"grammars": [{
"language": "rgl",
"scopeName": "source.rgl",
"path": "./syntaxes/rgl.tmLanguage.json"
}]
,"themes": [
{
"label": "rgl theme",
"uiTheme": "vs-dark",
"path": "./themes/rgl theme-color-theme.json"
}
]
}
}

发布

详细步骤参考:Visual Studio Code publishing extension-官网文档

简要步骤(请查看官网文档,这里仅仅是简化步骤):

  1. 安装VSCE。vsce是“Visual Studio Code Extensions”的缩写,是一个用于打包、发布和管理 VS Code 扩展的命令行工具。
  2. 在Azure DevOps中创建自己的组织,需要微软账号
  3. 在组织主页获取Personal access tokens,这个token需要开放Marketplace下的Manage权限
  4. 创建发布者,需要和刚才创建组织时登录的是同一个微软账号
  5. 使用vsce login <publisher name>命令,并用之前获取的Personal access tokens来验证
  6. 在扩展插件的文件夹内使用vsce publish命令发布扩展

看起来很复杂,但实际上做起来很快,没有遇到什么困难(除了纠结用什么名字之外)

前面的步骤都完成之后,以后发布新版本就只需要最后一步,即使用vsce publish命令发布扩展,很方便。

自制的第一个vscode语言扩展插件

https://yxchangingself.xyz/posts/vscode-extension/

作者

憧憬少

发布于

2022-06-01

更新于

2022-06-01

许可协议