插件工程
利用脚手架搭建插件工程
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和普通网页非常类似,不能直接调用任何VSCodeAPI,但是,它唯一特别之处就在于多了一个名叫 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文件中。