欢迎学习 VSCode 插件
你好! 这是你第一次学习编写 VSCode 插件 。如果你想学习如何在VSCode上制作插件, 可以仔细阅读这篇文章,了解一下VSCode插件基本知识。
问题描述
你也许遇到过和我一样的问题,你在某个领域需要使用编程语言去解决某一件事,但是这种编程语言可能不是市面/网络上主流所使用的,他可能是无从查起的、非常小众的。他的语言支持可能非常糟糕,使得你的工作效率受到限制或是极尽繁琐,那么,你一定灵光一闪想要去制作一款专属于你支持,因为你知道,理论上这并不困难。
那么,这样一款极尽繁琐、尽可能详细初学者教程我觉得有必要交与你
新的征程
我们开始前首先对VSCode有一个基本的了解,VSCode是基于Electron开发的应用程序。
- Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。它嵌入 了Chromium 和 Node.js 到 Electron 并允许您创建在Windows、 macOS和 Linux 跨平台应用。
- Node.js 是一个免费、开源、跨平台的 JavaScript 运行时环境,它让开发人员能够创建服务器、Web 应用、命令行工具和脚本,Node.js 可以让你在任何地方运行 JavaScript。
- npm 是Node.js 的扩展包管理器,你可以称它“ Node Package Manager ”,你可以用它方便的管理各种扩展包。
node提供一个Javascript语言环境,Electron使用它构建了一个桌面程序框架,而VSCode使用这样一个框架。
开发一个插件通常有很多的文件需要生成或者添加,我们一个一个添加当然可以,可是作为初学者往往并不清楚需要做些什么。因此如果有一个模板供我们修改,我们只需要填写或修改我们关心的部分就可以那该多好。
小工具
很庆幸,现实中有这样一个能够辅助我们达到这样目的的小工具,我们先介绍一下这个小工具:
-
Yeoman是一个通用的脚手架系统允许创建任何的 app 。它可以迅速的搭建一个新项目,并且能够简化了现有项目的维护。
-
Yeoman 构建的项目与语言无关。 它可以构建任何语言的项目 (Web, Java, Python, C#, 等。)
-
Yeoman 工作流包含三种类型的工具,用于在构建 Web 应用程序时提高您的工作效率和满意度:基架工具 (yo)、构建工具 (Gulp、Grunt 等) 和包管理器 (如 npm 和 Bower)。
-
yo 搭建一个新的应用程序,编写你的构建配置(例如 Gulpfile)并拉取构建可能需要的相关构建任务和包管理器依赖项(例如 npm)。
-
Build System 用于构建、预览和测试您的项目。Gulp 和 Grunt 是两个流行的选择。
-
Package Manager 用于依赖项管理,因此您不再需要手动下载和管理脚本。npm 和 Yarn 是两个流行的选项。
-
Yeoman 它自己不能做任何操作。 每个操作都是由 generators 基本插件在 Yeoman 环境所完成的。
-
这三种工具都是单独开发和维护的,但作为Yeoman 规定的工作流程的一部分,会使用到它们。
下载它的npm
我们现在开始使用这样一个小工具建立起一个VSCode的插件模板,你可以跟着我这样一个步骤开始:
- 首先安装好Node.js (https://nodejs.org/zh-cn),你可以在官网上很容易下载到,安装它。
- 如果你使用安装版的话,npm 在你安装Node时会附带安装;如果你在国内,你可能需要更换安装包源,用下面的操作会很方便
#设置源
npm config set registry https://npm.aliyun.com
#查询源
npm config get registry
#清除缓存
npm cache clean --force
- 这时我们打开终端输入 npm 会输出相应信息,代表我们可以使用它了。
PS C:\Users\ABC> npm
npm <command>
Usage:
npm install install all the dependencies in your project
npm install <foo> add the <foo> dependency to your project
npm test run this project's tests
npm run <foo> run the script named <foo>
npm <command> -h quick help on <command>
npm -l display usage info for all commands
npm help <term> search for help on <term> (in a browser)
npm help npm more involved overview (in a browser)
All commands:
access, adduser, audit, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
help-search, hook, init, install, install-ci-test,
install-test, link, ll, login, logout, ls, org, outdated,
owner, pack, ping, pkg, prefix, profile, prune, publish,
query, rebuild, repo, restart, root, run-script, sbom,
search, set, shrinkwrap, star, stars, start, stop, team,
test, token, uninstall, unpublish, unstar, update, version,
view, whoami
- 我们接下来安装我们所需的工具Yeoman
# 安装Yeoman
npm install -g yo grunt-cli bower
实际上一般你只需要安装 yo
# 安装 yo
npm install -g yo
你可以在Github上看到这个项目 (https://github.com/yeoman/yo)
使用我们的小工具
终于到了使用我们工具的时候了,经过一番折腾
- 在命令行中输入 yo 终于见到我们的好伙伴,说一句 yo~
C:\Users\ABC>yo
? 'Allo ABC! What would you like to do?
──────────────
Update your generators
Install a generator
> Find some help
Get me out of here!
──────────────
Run a generator
(Move up and down to reveal more choices)
-
你可以使用你键盘上的方向键去选择它提供的选项,这对我们来说是一个不错的消息,简单易懂的操作是美好生活的关键。
-
接下来我们安装我们所需的模板,我们真正需要的是Generators (https://yowebapp.github.io/generators/) 库中文件,我们可以使用npm下载到这个一般是一个名字为 generator-ABC 的 npm 包,而yo会帮助我们完成一些必要的初始化,非常人性的过程
npm install -g generator-code
如果你恰巧需要构建一个网络APP,你可以
npm install -g generator-webapp
我们目前只需要一个code就足够了
- 你也可以这样下载到你需要的包
C:\Users\ABC>yo
? 'Allo ABC! What would you like to do? Install a generator
? Search npm for generators: web
? Sorry, no results matches your search term Return home
? 'Allo zhang! What would you like to do?
Code
──────────────
Update your generators
> Install a generator
Find some help
Get me out of here!
C:\Users\ABC>yo
? 'Allo ABC! What would you like to do? Install a generator
? Search npm for generators:
- 下载完成后,需要进行简单的初始化,我们选择New Language Support 因为我们目的是制作一个语法/特殊字高亮的插件
PS C:\Users\ABC> yo code
_-----_ ╭──────────────────────────╮
| | │ Welcome to the Visual │
|--(o)--| │ Studio Code Extension │
`---------´ │ generator! │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? What type of extension do you want to create?
New Extension (TypeScript)
New Extension (JavaScript)
New Color Theme
❯ New Language Support
New Code Snippets
New Keymap
New Extension Pack
New Language Pack (Localization)
New Web Extension (TypeScript)
New Notebook Renderer (TypeScript)
- 你可以获取到的一些帮助
yo code --help
npm home generator-code
#Yo 也能提供下面这些命令
yo --help #访问完整的帮助文档
yo --generators #列出所有已经安装的 generators
yo --version #得到它的版本
#大多数问题可以通过下面的命令去发现:
yo doctor
- 你也可以获取官网的帮助,Yeoman,help!
必要的选项
回车后会开始让我们回答一系列问题,分别为:
? What type of extension do you want to create? New Language Support
Enter the URL (http, https) or the file path of the tmLanguage grammar or press ENTER to start with a new grammar.
? URL or file to import, or none for new:
# 引入文件/URL,或者留空以搭建新项目。我们直接回车
? What's the name of your extension? myname
# 扩展的名字是什么。给扩展起一个名字,如: myname
? What's the identifier of your extension? (myname)
# 扩展的标识符是什么。当用户安装插件后,在VSCode的扩展一栏中的显示名。一般会根据上面的起名给一个默认的名字
? What's the description of your extension? abc
# 描述你的扩展。随意的说明
Enter the id of the language. The id is an identifier and is single, lower-case name such as 'php', 'javascript'
? Language id: myname
# 输入你的语言的id。注意,这里的id唯一标记了你的语言,格式要求为单词(无空格)、小写。比如:“myname”
Enter the name of the language. The name will be shown in the VS Code editor mode selector.
? Language name: myname
# 输入你的语言的名字。在VSCode中新建了一个文件,它会提示“选择语言以开始”,这里的起名就是显示在这里的。
Enter the file extensions of the language. Use commas to separate multiple entries (e.g. .ruby, .rb)
? File extensions: .myname
# 输入启用扩展的文件后缀。例子:C++扩展为.cpp,Python扩展为.py。你需要指定哪些文件后缀启用我们自定义的扩展,比如:".myname"
Enter the root scope name of the grammar (e.g. source.ruby)
? Scope names: source.myname
# 输入语法根作用域名。这个一般就是source.定义的后缀,比如:“source.myname”
? Initialize a git repository? No
# 是否需要初始化一个git仓库。如果只是为了自己使用,选no就好了
现在,你会看到自动生成了一个文件夹,我们所需要的项目文件就全在里面了!把整个插件文件夹,拷贝到
- Windows: %USERPROFILE%.vscode\extensions
- macOS: ~/.vscode/extensions
- Linux: ~/.vscode/extensions
重启VSCode就可以启动了。
文件说明
│ .vscodeignore
│ CHANGELOG.md #更改日志模板
│ README.md # 引导模板
│ vsc-extension-quickstart.md #VSCode语言插件快速开始说明
│ language-configuration.json #全局定义规则,注释、括号、自动补全等。
│ package.json #使用yo进行过的初始化设置,
│
├─.vscode
│ launch.json #插件调试
│
└─syntaxes #语法
myname.tmLanguage.json #在此处定义编程语言的文法
文件vsc-extension-quickstart.md中的一段描述:
package.json
- this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension.syntaxes/myname.tmLanguage.json
- this is the Text mate grammar file that is used for tokenization.language-configuration.json
- this is the language configuration, defining the tokens that are used for comments and brackets.
近在咫尺
进行到这里我们已经过了大半,距离成功已经近在咫尺了。我们已经拥有了一个像样的基础,终于能够开始实现我们一开始想要要做的事了,万事俱备:
一阵东风
在我们大干特干之前,我们还需要先了解一点东西,
-
微软的VSCode语法高亮规则使用TextMate文法定义。
-
TextMate 是一款功能强大且易于使用的文本编辑器,专为 macOS 平台设计。
-
TextMate grammars是一种定义编程语言或文件格式的规则集,用于指导编辑器如何识别、标记和突出显示代码中的不同部分。
-
VSCode TextMate是微软为VSCode贡献的一个开源项目,它是一个实现了TextMate grammars规范的解析器。通过这个项目,VSCode能够支持数百种语言的语法高亮,并且可以轻松地添加新的语言支持。
因此实际上我们需要的是使用TextMate grammars来实现我们自定义语法的需求。
我们先看一遍 myname.tmLanguage.json 文件中都有些什么
{
声明表的类型及tmLanguage建和值的对应关系
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
语法的名字
"name": "myname",
语法规则列表
"patterns": [
{
"include": "#keywords"
},
{
"include": "#strings"
}
],
字典,一个键值对应的字典
"repository": {
语法A
"keywords": {
"patterns": [{
高亮名
"name": "keyword.control.myname",
匹配规则
"match": "\\b(if|while|for|return)\\b"
}]
},
语法B
"strings": {
"name": "string.quoted.double.myname",
匹配开始范围
"begin": "\"",
匹配结束范围
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.myname",
"match": "\\\\."
}
]
}
语法C
..............
},
"scopeName": "source.myname"
}
万事俱备
现在你可以自己定义自己的语法高亮,你可能需要参考其他语法的高亮,快捷键Ctrl +' 可以方便的查看其他语法高亮,或者打开VSCode的命令面板,搜索检查编辑器标记和作用域
更详细的TextMate grammars语法支持可以参考官网:
https://macromates.com/manual/en/language_grammars
下面是我按照自己需求制作语法高亮
{
"scopeName": "source.iw",
"patterns": [
{
"include": "#expression"
}
],
"repository": {
"expression": {
"patterns": [
{
"include": "#iw_letter"
},
{
"include": "#iw_control"
},
{
"include": "#iw_func"
},
{
"include": "#iw_type"
},
{
"include": "#iw_def"
},
{
"include": "#iw_num"
},
{
"include": "#iw_comment"
},
{
"include": "#iw_str"
},
{
"include": "#strings"
},
{
"include": "#iw_comment_block"
},
{
"include": "#iw_symbol"
}
]
},
"iw_letter": {
"match": "\\b(True|False|ture|false)\\b",
"name": "constant.language.iw"
},
"iw_control": {
"match": "SOURCE_OBJECTS|IF|END_IF|ELSE|GET_SOURCES",
"name": "keyword.control.iw"
},
"iw_func": {
"match": "FUNCTION|END_FUNCTION",
"name": "entity.name.function.iw"
},
"iw_type": {
"match": "DOUBLE|INTEGER|STRING|OBJECT",
"name": "support.type.iw"
},
"iw_def": {
"match": "DEFINE_LOCALS|END_DEFINES",
"name": "storage.type.iw"
},
"iw_num": {
"match": "[0-9]",
"name": "constant.numeric.iw"
},
"iw_comment": {
"match": "//.*$",
"name": "comment.line.double-slash.iw"
},
"iw_comment_block": {
"begin": "\/\\*",
"end": "\\*\/",
"name": "comment.block.iw"
},
"iw_str": {
"begin": "\"",
"end": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.paren.open"
}
},
"endCaptures": {
"0": {
"name": "punctuation.paren.close"
}
},
"name": "string.quoted.double"
},
"iw_symbol": {
"match": "(\\=)|(\\+)|(\\-)|(\\<)|(\\>)|(\\,)|(\\*)|(\\.)|(\\,)",
"captures": {
"0": {
"name": "variable.parameter.iw"
}
}
}
}
}
如果你感兴趣或者觉得有趣,欢迎留下你的足迹 ^ _ ^