VsCode 插件语言插件开发
最近对如何开发一个VsCode的插件比较感兴趣,于是特意去读了一些插件的开发文档,主要看的是一个中文翻译的文档,不过这个文档并不全,基本属于重点功能的罗列,但是用来入门是挺好的,不过如果想要深入,还是要去看英文的文档。
大部分内容都比较简单,主要就是配置,就可以生效,我这次主要想总结的是如何开发一个全新的语言插件,也就是说,如果你想定义一门新类似JSX的语言,如果提供VsCode的支持,比如语法高亮,代码片段,标签自闭和,错误提示,自动补全等等。
插件目录结构
1 | ├── .vscode |
插件清单
每个VS Code插件都必须包含一个package.json
,它就是插件的配置清单。package.json
混合了Node.js字段,如:scripts
、dependencies
,还加入了一些VS Code独有的字段,如:publisher
、activationEvents
、contributes
等。关于这些VS Code字段说明都在插件清单参考中可以找到。我们在本节介绍一些非常重要的字段:
name
和publisher
: VS Code 使用<publisher>.<name>
作为一个插件的ID。你可以这么理解,Hello World 例子的 ID 就是vscode-samples.helloworld-sample
。VS Code 使用 ID 来区分各个不同的插件。main
: 插件的主入口。engines.vscode
: 描述了这个插件依赖的最低VS Code API版本。postinstall
脚本: 如果你的engines.vscode
声明的是1.25版的VS Code API,那它就会按照这个声明去安装目标版本。一旦vscode.d.ts
文件存在于node_modules/vscode/vscode.d.ts
,IntelliSense就会开始运作,你就可以对所有VS Code API进行定义跳转或者语法检查了。
1 | { |
入口文件(package.json中的main)
插件入口文件会导出两个函数,activate
和 deactivate
,你注册的激活事件被触发之时执行activate
,deactivate
则提供了插件关闭前执行清理工作的机会。
vscode
模块包含了一个位于node ./node_modules/vscode/bin/install
的脚本,这个脚本会拉取package.json
中engines.vscode
字段定义的VS Code API。这个脚本执行过后,你就得到了智能代码提示,定义跳转等TS特性了。
1 | // 'vscode'模块包含了VS Code extensibility API |
发布内容配置
插件可以做什么
常用功能
注册命令、配置、快捷键绑定、菜单等。
保存工作区或全局数据。
显示通知信息。
使用快速选择获得用户输入。
打开系统的文件选择工具,以便用户选择文件或文件夹。
使用进度API提示耗时较长的操作。
命令
- 使用pacakge.json中的contributes.commands注册命令,只有在这里注册的命令才可以在命令面板中搜索到。
- 默认情况下,所有命令面板中出现的命令都可以在
package.json
的commands
部分中配置。不过,有些命令是场景相关的,比如在特定的语言的编辑器中,或者只有用户设置了某些选项时才展示。Visual Studio Code Key Bindings:可以查看如何写这个when命令
1 | { |
- 使用vscode.command.registerCommand为命令注册回调函数
- 使用vscode.command.excuteCommand执行命令
vscode也有很多内置的命令(VS Code内置命令清单)
我们看个例子🌰:editor.action.addCommentLine
命令可以将当前选中行变成注释(你可以偷偷把这个功能地集成到你自己的插件中哦):
1 | import * as vscode from 'vscode'; |
配置
插件需要在contributes.configuration
发布内容配置点发布内容配置点package.json的一部分,用于配置插件启动命令、用户可更改的插件配置,可以理解为插件的主要配置文件。中填写有关的配置,你可以workspace.getConfiguration
API中阅读有关内容。
键位绑定
插件可以添加自定义键位映射,在contributes.keybindings
和键位绑定中了解更多有关内容。
菜单
插件可以自定义上下文菜单项,菜单会根据用户右击VS Code UI的不同位置而各不相同。查看更多contributes.menus
发布内容配置。
数据存储
VS Code中有三种数据储存方式:
ExtensionContext.workspaceState
:键值对组成的工作区数据。当同一个工作区再次打开时会重新取出数据。ExtensionContext.globalState
:键值对组成的全局数据。当插件激活时会再次取出这些数据。ExtensionContext.storagePath
:指向你的插件可以读写的本地文件夹的路径。如果你要储存比较大的数据,这是一个非常好的选择。ExtensionContext.globalStoragePath
:指向你的插件可以读写的本地存储的路径。如果你要存储所有工作区内的大文件,这是一个非常好的选择。
通知
几乎所有的插件都需要在某些时候为用户提示信息。VS Code提供了3个API来展示不同重要程度的信息:
window.showInformationMessage
window.showWarningMessage
window.showErrorMessage
主题
产品图标主题和色彩主题可以参考文档:VS Code插件创作中文开发文档 - 色彩主题
文件图标主题
VS Code的UI在文件名称左边显示图标,插件配置的图标系列可以让用户自由选择他们喜爱的图标。
但是Vscode目前不支持某个语言插件只定义自己语言文件的图标,如果用户切换到你的插件提供的主题,却不能支持所有文件类型,就会出现问题
首先,创建一个VS Code插件,然后把iconTheme
配置点(contribution point)添加进去
1 | "contributes": { |
复制到剪贴板复制错误已复制
id
作为这个图标主题的标识,目前只做内部使用,未来可能会用在设置里面,所以最好设置一个可读性强的唯一值。label
会显示在主题选择下拉框中。path
指示了图标集所在的位置。如果你的图标系列名称遵循*icon-theme.json
命名规范,那么VS Code就能提供完整的支持。
1 | //turtles-icon-theme.json{ |
这里,图标定义包含了一个标识符_folder_dark
。除此之外还支持以下属性:
iconPath
:当使用svg/png文件时:指向图片的路径。fontCharacter
:当使用glyph字体时:字体中使用的字符。fontColor
:当使用glyph字体时:设置glyph的颜色。fontSize
:当使用字体时:设置字体大小。默认情况下会使用字体本身定义的字体大小。这个值应为父级字号的相对值(如 150%)。fontId
:当使用字体时: 字体的ID。如果没有指定,则会采用font specification
部分的第一个字体。
在iconDefinitions中定义之后,就可以引用了
1 | { |
file
是一个默认文件图标,为那些没有匹配到任何插件、文件名、语言类型的文件所准备的。目前所有文件图标属性都会被继承(只适用于:glyphs字体、字体大小(fontSize))。folder
收起的文件夹图标,如果folderExpanded
没有设置,那么展开的文件夹也会使用这个图标。使用folderNames
关联特殊名称的文件夹。文件夹图标是可选的,如果不设置,那文件夹就不会显示任何图标。folderExpanded
展开的文件夹图标。这个图标是可选的,如果不设置就会使用folder
定义好的图标。folderNames
特殊名称文件夹图标。这个键是用于文件夹名称的,不支持包含路径的名称,不支持匹配模式和通配符。大小写不敏感。folderNamesExpanded
展开的特殊名称文件夹图标。rootFolder
收起的工作区根文件夹图标,如果rootFolderExpanded
没有设置,那么展开的工作区根文件夹也会使用这个图标。如果不设置,则会使用folder
定义的文件夹图标。rootFolderExpanded
展开的工作区根文件夹图标。如果没有设置,则会使用rootFolder
定义的文件夹图标。languageIds
语言类型图标。这个键将匹配在*语言配置点(contribution point)*配置的语言id。注意语言配置的’第一行’是不考虑在内的。fileExtensions
文件插件图标。根据文件插件的名称匹配。插件名称是文件名点号后面(不包含点号)。拥有多重点号的文件名称,如lib.d.ts
会匹配多个模式——d.ts
和ts
。大小写敏感。fileNames
文件图标。这个键需要文件的全称进行匹配,不支持包含路径的名称,不支持模式和通配符。大小写敏感。fileNames
是最高优先匹配。
匹配优先级:fileNames
> fileExtensions
> languageIds
light
和highContrast
部分的属性表和上面相同,只是会在对应的主题下覆盖原有图标配置。
语言特性
声明式语言特性
声明式语言特性添加了基础的编程语言编辑支持,如括号匹配、自动缩进和语法高亮。这些功能都可以通过声明配置而不用写任何代码就可以获得,更高级的语言特性如IntelliSense或调试,请看编程式添加语言特性
将常用的JS代码片段打包到插件中
为VS Code添加新的语言支持
为一门语言添加或替换语法
通过注入的方式,扩展一门语法
将现有的 TextMate 语法迁移到VS Code中
代码片段
语言配置
通过contributes.languages
发布内容配置,你可以配置以下声明式语言特性:
启用/关闭注释
定义括号
自动闭合符号
自动环绕符号
代码折叠
单词匹配
缩进规则
1 | { |
编程式语言特性(监听用户动作)
下面这表对应的是我们可以实现的编程性语言特性,每个特性都可以单独通过左边一列的VScode API来实现,同时可以单独通过Language Server Protocol在语言服务器中实现。
理论上所有通过语言服务器实现的特性都可以单独用客户端实现
Unable to copy while content loads
编程式添加语言特性可以为编程语言添加更为丰富的特性,如:悬停提示、转跳定义、错误诊断、IntelliSense和CodeLens。这些语言特性暴露于vscode.languages.*
API。语言插件可以直接使用这些API,或是自己写一个语言服务器,通过语言服务器库将它适配到VS Code。
虽然我们提供了一个语言特性列表,但是并不阻碍你发挥想象,自由使用这些API。比方说,在行内显示额外信息,使用CodeLens和代码悬停是非常好的方式,而错误诊断可以高亮拼写或代码风格错误。
鼠标悬停于API上时, 出现用法示例
使用诊断,报告代码风格错误
注册新的HTML代码格式化
提供丰富的IntelliSense中间件
为一门语言添加代码折叠、面包屑、轮廓支持
以自动补全为例子说明用法
客户端实现
1 | class GoCompletionItemProvider implements vscode.CompletionItemProvider { |
LSP实现
在接收响应的initialize
方法中,你的语言服务器需要声明它是否能提供补全,以及它是否支持动态计算补全项的completionItem\resolve
方法。
1 | { |
返回了这个配置,客户端才会在输入完成后发送textDocument/completion事件给语言服务器,语言服务器通过onCompletion事件监听这个事件,监听后返回匹配的列表。
当我们具体选择了某一个补全项之后,会发送completionItem/resolve事件,该事件被onCompletionResolve事件监听,可以补全具体的信息,或者进行修改
1 | //server.ts |
监听文档的变化
上面的编程式语言特性主要做的当我们进行了某些动作之后,客户端本身监听到或者客户端将监听到的动作与服务器之间利用事件进行通信。
还有一种方式,是文本本身发生变化时通知客户端或者告诉语言服务器文本的内容
客户端本身监听到的内容改变,可以在vscode.workspace.*中找到对应的api,如vscode.workspace.onDidChangeTextDocument
LSP则稍微复杂一点
1 | //DocumentService.ts |
这种方式每次都是发送全量文本,对性能会有影响,所以vscode又提供了增量文本更新同步