插件工程
利用脚手架搭建插件工程
vscode 官方贴心的为我们提供了插件工程生成器,我们可以用生成器很轻松的创建一个插件工程。
使用以下命令安装 Yeoman 和 VS Code Extension Generator:
sudo npm install -g yo generator-code
安装完成后,生成器搭建了一个准备开发的 TypeScript 或 JavaScript 项目。运行生成器并为 TypeScript 项目填写一些字段:
yo code
# ? What type of extension do you want to create? New Extension (TypeScript)// 使用 TypeScript 还是 JavaScript 书写插件
# ? What's the name of your extension? HelloWorld // 插件名称
# ? What's the identifier of your extension? helloworld// 插件标识符
# ? What's the description of your extension? LEAVE BLANK// 插件描述
# ? Initialize a git repository? Yes // 是否初始化git仓库
# ? Bundle the source code with webpack? Yes // 是否使用webpack打包项目
# ? Which package manager to use? npm// 使用哪个包管理器
工程目录
helloworld
├─ .eslintrc.json
├─ .vscode
│ ├─ extensions.json
│ ├─ launch.json
│ ├─ settings.json
│ └─ tasks.json
├─ .vscodeignore
├─ CHANGELOG.md
├─ README.md
├─ dist
│ ├─ extension.js
│ └─ extension.js.map
├─ package-lock.json
├─ package.json
├─ src
│ ├─ extension.ts
│ └─ test
│ ├─ runTest.ts
│ └─ suite
│ ├─ extension.test.ts
│ └─ index.ts
├─ tsconfig.json
├─ vsc-extension-quickstart.md
└─ webpack.config.js
工程启动
其中 .vscode 文件夹下的是 vscode 专属调试文件,在vscode中点击 F5 即可打开插件调试页面,然后按下 Ctrl+Shift+P
,输入HelloWorld
执行对应命令,可以发现右下角弹出了HelloWorld
提示,说明你的第一个插件启动成功,如下所示:
工程解读
插件源码文件在 src 文件夹下的 extension.ts 文件中
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('helloworld.helloWorld', () => {
vscode.window.showInformationMessage('Hello World from HelloWorld!');
});
context.subscriptions.push(disposable);
}
export function deactivate() {}
{
...,
"activationEvents": [
"onCommand:helloworld.helloWorld"
],
"main": "./dist/extension.js",
"contributes": {
"commands": [{
"command": "helloworld.helloWorld",
"title": "Hello World"
}]
}
...,
}
根据 extension.ts 和 package.json 文件
package.json文件中main字段
定义了整个插件的主入口,即 webpack 编译完成后的插件入口;- 我们在
contributes.commands
里面注册了一个名为 helloworld.helloWorld 的命令,并在src/extension.ts
中去实现了它(弹出一个Hello World
的提示); - 但是仅仅这样还不够,命令虽然定义了,但是vscode还不知道啥时候去执行它,还需要在
activationEvents
添加上onCommand:helloworld.helloWorld
用来告诉vscode,当用户执行了这个命令操作时去执行前面我们定义的内容;
至此,一个简单的 hello world 插件便完成了。
package.json文件解读
activationEvents
插件在VS Code
中默认是没有被激活的,那什么时候才被激活呢?就是通过 activationEvents
来配置,目前支持以下 12 种配置:
配置 | 说明 | 示例 |
---|---|---|
onLanguage | **每当打开解析为某种语言的文件时,就会发出此激活事件并激活注册的扩展名,**如果我配置了onLanguage:javascript ,那么只要我打开了JS类型的文件,插件就会被激活 | "activationEvents":["onLanguage:python"] |
onCommand | 每当调用命令时,将发出此激活事件并激活注册的扩展****即使用 command + shift + p 调出命令行中输入对应命令后插件被激活 | "activationEvents": ["onCommand:extension.HelloWorld" ] |
onDebug | 启动调试会话之前,将发出此激活事件并激活注册的扩展 | "activationEvents": [ "onDebug" ] |
workspaceContains | 每当打开文件夹并且该文件夹至少包含一个与glob模式匹配的文件时,就会发出此激活事件并激活感兴趣的扩展名 | "activationEvents": [ "workspaceContains:**/.eslintrc.js" ] |
onFileSystem | 每当读取特定方案中的文件或文件夹时,将发出此激活事件并激活注册的扩展名 | "activationEvents": [ "onFileSystem:sftp" ] |
onView | 每当在 VS Code 侧边栏中展开指定 id 的视图(扩展或源代码控制是内置视图的示例)时,都会发出此激活事件并激活注册的扩展 | "activationEvents": ["onView:nodeDependencies" ] |
onUri | 每当打开该扩展的系统范围的 Uri 时,就会发出此激活事件,并且会激活注册的扩展即访问系统范围内的文件时就会触发 | - |
onWebviewPanel | 该激活事件发出后,有兴趣的扩展将被激活,只要VS代码需要恢复WebView与匹配viewType | - |
onCustomEditor | 该激活事件发出后,有兴趣的扩展将被激活,只要VS代码需要创建一个自定义编辑器与匹配viewType | - |
onAuthenticationRequest | 每当扩展请求身份验证会话(通过authentication.getSession() API)与匹配的providerId,扩展被激活 | - |
onStartupFinished | **在VS Code 启动后的一段时间内,会发出此激活事件并激活感兴趣的扩展。**这与* 激活事件类似,但不会减慢 VS Code 的启动速度。目前,此事件在所有* 激活的扩展完成激活后发出。 | "activationEvents": [ "onStartupFinished" ] |
* | 如果配置了* ,只要一启动vscode,插件就会被激活 | "activationEvents": ["*" ] |
contributes
配置 | 说明 | 示例 | |
---|---|---|---|
commands | 为由标题和(可选)图标、类别和启用状态组成的命令提供 UI | "commands": [{"command": "helloworld.helloWorld", "title": "自定义插件:hello world" }] | |
snippets | 配置代码片段支持 | "snippets": [{ "language": "javascript", "path": "./src/test.json" }] | |
viewsContainers.activitybar | 配置活动栏图标 | "viewsContainers": { "activitybar": [{ "id": "rabbit", "title": "小白兔", "icon": "./src/logo.svg" }] } | |
views | 配置活动栏对应的view视图 | "views": { "rabbit": [{ "id": "rabbit1", "name": "小白兔" }] } | |
keybindings | 配置快捷键 | "keybindings": [{ "command": "helloworld.helloWorld", "key": "ctrl+f10", "mac": "cmd+f10", "when": "editorTextFocus" }] | |
menus | 自定义编辑器菜单,包括右键菜单、头部菜单等 | "menus": { "editor/title": [{ "when": "editorFocus", "command": "helloworld.helloWorld", "group": "navigation" }], "editor/context": [{ "when": "editorFocus", "command": "helloworld.helloWorld", "group": "z_commands" }] } | |
configuration | 配置可以在 setting.json 文件中配置的字段 | "configuration": { "title": "helloworld", "properties": { "helloworld.attr1": { "type": "boolean", "default": false, "description": "Complete functions with their parameter signature." }, "helloworld.attr2": { "type": ["string", "null"], "default": null, "description": "Specifies the folder path containing the tsserver and lib*.d.ts files to use." } } } 插件中使用如下方式获取配置项vscode.workspace.getConfiguration('helloworld') |
extension.ts文件解读
extension.js
是插件工程的入口文件,当插件被激活,即触发 package.json
中的 activationEvents
配置项时,extension.js
文件开始执行。
在 extension.js
中对需要的功能进行注册,主要使用 vscode.commands.register...
相关的api
,来为 package.json
中的 contributes
配置项中的事件绑定方法或者监听器。
vscode.commands.register...
相关的api
主要有:
vscode.languages.registerCompletionItemProvider() - 代码补全
vscode.commands.registerCommand() - 注册命令实现
vscode.languages.registerHoverProvider() - 代码悬浮提示
- ......
WebView
vscode 提供了webview 支持,可以在 vscode 中塞进各种网页内容。
可以将 webview 视为iframe
。webview 可以在此框架中呈现几乎所有 HTML 内容,并且可以使用消息传递与扩展进行通信。这种自由使 webviews 非常强大,并开辟了一个全新的扩展范围。
插件 vstoolkit 就是基于 webview 实现的。 iconfont 其实就是一个个网页。
创建 webview
const panel = vscode.window.createWebviewPanel(
'testWebview', // viewType
"WebView演示", // 视图标题
vscode.ViewColumn.One, // 显示在编辑器的哪个部位
{
enableScripts: true, // 启用JS,默认禁用
retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置
}
);
panel.webview.html = `<html><body>你好,我是Webview</body></html>`;
访问本地文件
出于安全考虑,webview 默认无法直接访问本地资源,它在一个孤立的上下文中执行,想要加载本地的图片、js、css等必须通过 vscode 提供的 vscode-resource 协议。
vscode-resource 协议类似 file 协议,但它只允许访问特定的本地文件。
如果想加载 html 文件中的内容,则需要使用 nodejs 的访问本地html文件,读取其内容,并将其中涉及到 script 、link、img 等标签的路径并且是从本地加载文件的地方转换为使用 vscode-resource 协议加载文件的方式 ,涉及到从网络下载的文件时注意必须使用 https 方式,否则会导致请求失败,以下是封装的方法:
/**
* 从某个HTML文件读取能被Webview加载的HTML内容
* @param {Object} context 上下文
* @param {String} templatePath 相对于插件根目录的html文件相对路径
*/
function getWebViewContent(context: vscode.ExtensionContext, templatePath: string):string {
const resourcePath:string = path.join(context.extensionPath,templatePath);
const dirPath:string = path.dirname(resourcePath);
let html:string = fs.readFileSync(resourcePath, "utf-8");
html = html.replace(
/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g,
(m: any, $1: string, $2: string) => {
if ($2.indexOf("https") < 0) {
return ($1 + vscode.Uri.file(path.resolve(dirPath, $2)).with({ scheme: "vscode-resource" }).toString() + '"');
} else {return $1 + $2;}
});
return html;
}
消息通信
Webview
和普通网页非常类似,不能直接调用任何VSCode
API,但是,它唯一特别之处就在于多了一个名叫 acquireVsCodeApi
的方法,执行这个方法会返回一个超级阉割版的 vscode
对象,这个对象里面有且仅有如下3个可以和插件通信:
postMessage
- 发送消息getState
- 获取state,state的作用主要是在恢复webview是提供可恢复数据支持setState
- 设置state
1、插件向 webview 发送消息
panel.webview.postMessage({text: '你好!'});
2、webview端接收消息
window.addEventListener('message', event => {
const message = event.data;
console.log('Webview接收到的消息:', message);
}
3、webview向插件发送消息
vscode.postMessage({text: '你好,我是Webview啊!'});
4、插件接收webview消息
panel.webview.onDidReceiveMessage(message => {
console.log('插件收到的消息:', message);
}, undefined, context.subscriptions);
插件打包
vsce
vsce是“Visual Studio Code Extensions”的缩写,是用于打包、发布和管理 VS Code 扩展的命令行工具。
npm install -g vsce vsce package # 打包插件 vsce publish # 发布插件,需要到vscode官网创建插件发布者,并把发布者放到package.json的publisher字段
如果您想在本地安装的 VS Code 上测试扩展或分发扩展而不将其发布到 VS Code Marketplace,您可以选择打包您的扩展。vsce
可以将您的扩展程序打包成一个VSIX
文件,用户可以从中轻松安装。
.vscodeignore
您可以创建一个.vscodeignore
文件来排除某些文件包含在您的扩展程序包中。该文件是一组glob模式,每行一个。
例如:
**/*.ts **/tsconfig.json !file.ts
您应该忽略运行时不需要的所有文件。例如,如果你的扩展是用 TypeScript 编写的,你应该忽略所有**/*.ts
文件,就像前面的例子一样。
注意: package.json 中列出的开发依赖项devDependencies
将被自动忽略,您无需将它们添加到.vscodeignore
文件中。