目录
插件在 VSCode 中能做什么
1、发布应用市场
1、申请Microsoft账号
2、创建Azure DevOps组织
3、创建令牌
4、创建发布账号
5、发布应用市场
2、本地打包不发布
1、本地打包
2、导入应用商店
编辑
3、插件开发前的环境准备
2、项目初始化
3、运行项目
4、文件介绍
1、package.json
1、main
2、activationEvents
3、contributes
2、extension.js
activate:这是插件被激活时执行的函数
deactivate:这是插件被销毁时调用的方法,比如释放内存等。
5、实战-小试牛刀
1、package.json
2、extension.ts文件
6、webview详解Webview API | Visual Studio Code Extension API
1、实现一个简单的webview功能
2、加载本地资源
1、线引入头文件
2、替换路径
3、执行函数
3、消息通信
1、实现消息发送:
2、简单通信封装
4、生命周期
5、实现webiew的状态保持
6、调试
7、菜单
1、菜单出现的位置
2、菜单出现的条件when
3、控制菜单的分组和排序group
1、editor/context中的默认组:
2、explorer/context中的默认组:
插件在 VSCode 中能做什么
- 主题
- 界面和文本(TextMate 语法)主题色
- 图标样式
- 通用功能
- 添加命令
- 添加配置项
- 添加快捷键
- 添加菜单项
- 添加右键菜单
- 从文本输入框获取输入(QuickPick)
- 存储数据(localStorage)
- 工作区扩展
- 活动栏项目
- 显示提示框
- 状态栏信息
- 显示进度条
- 打开文件
- 显示网页(web view)
- 程序语言
- 实现新语言的高亮
- 实现新语言的调试器
- 代码库管理
- 定义和执行 Task
- 定义 snippet
1、发布应用市场
对于一个初学者来说,我们先了解下vscode的应用市场,做好开发前的准备工作
Visual Studio Code的应用市场基于微软自己的Azure DevOps
,插件的身份验证、托管和管理都是在这里。
- 要发布到应用市场首先得有应用市场的
publisher
账号; - 而要有发布账号首先得有
Azure DevOps
组织; - 而创建组织之前,首先得创建
Azure
账号; - 创建
Azure
账号首先得有Microsoft
账号;
我们来梳理下:
- 一个Microsoft账号可以创建多个
Azure
组织; - 一个组织可以创建多个
publisher
账号; - 同时一个组织可以创建多个
PAT
(Personal Access Token
,个人访问令牌);
1、申请Microsoft账号
访问 Sign in to your Microsoft account 登录你的Microsoft
账号,没有的先注册一个
2、创建Azure DevOps
组织
访问: https://aka.ms/SignupAzureDevOps
点击继续,默认会创建一个以邮箱前缀为名的组织。
3、创建令牌
进入组织的主页后,点击右上角的Security,
点击创建新的个人访问令牌,这里特别要注意Organization
要选择all accessible organizations
,Scopes
要选择Full access
,否则后面发布会失败。
创建令牌成功后你需要本地记下来,因为网站是不会帮你保存的,后面有可能会用到,如果没保存,后面需要用到的话,就只能重新创建令牌了
4、创建发布账号
访问https://aka.ms/vscode-create-publisher
保存无反应,可以f12看下控制台报什么错误,如果是无法加载recaptcha,可以下载安装火狐浏览器,使用firefox浏览器下载插件gooreplacer – 下载 🦊 Firefox 扩展(zh-CN)
安装完成后就可在firefox浏览器右上角看到此插件
配置gooreplacer的设置页面
当然也可以写全路径
然后在火狐浏览器中访问上述链接就通了
输入完成后点击Submit完成后,接下来再试试,之前无法加载的recaptcha应该就正常了 ;
这里需要注意,使用vsce命令创建会报错,只能使用上面的网页方式
具体步骤如下:
先全局安装vsce
npm i vsce -g
创建发布者:
vsce create-publisher fuyun
lzfuyun是发布者的名字,然后就会报如下错误,提示让走网页方式创建:
5、发布应用市场
上面一切准备好后,package.json中添加刚刚创建的发布者,就可以发布了
对应项目目录终端输入命令
vsce publish
等个几分钟就可以看到发布的应用了
在vscode的应用市场上搜索一下,已经有了
2、本地打包不发布
本地打包安装是在不能公开的,私密性比较高的应用,使用本地打包后,把包发给需要使用的人,手动导入安装到自己的vscode中;
1、本地打包
打包之前需要在package.json中配置发布人: “publisher”:“fuyun”
注意:如果不需要发布应用市场的话,这里的publisher可以随便取个名字,也不需要想上面发布应用市场一样申请一堆东西的长流程。
然后把README.md删干净,随便添加一句话就行,如果不删除会报错提示你打包之前需要先编辑README.md文件
准备就绪,就可以打包了,终端执行命令:
vsce package
然后一路y下去就行ok了
vsix就生成了
2、导入应用商店
选择本地刚刚生成的vsix文件,就安装成功了
3、插件开发前的环境准备
在前面我们已经准备好了发布的账号、Personal Access Token、以及创建发布者后,我们就可以开始准备我们的开发环境了
1、安装依赖
官方为了方便开发人员进行vscode插件的开发,提供了对yo应的脚手架来生成对应的项目。vscode 扩展开发官方文档
// 全局安装需要的包
npm install -g yo generator-code
在 mac 安装失败,报错没有权限 permission denied
字眼的时候,可以加 sudo
来授权
// 全局安装需要的包
sudo npm install -g yo generator-code
上述命令其实安装了两个包(yo
和 generator-code
),这两个包用途如下:
- yo模块全局安装后就安装了Yeoman,Yeoman是通用型项目脚手架工具,可以根据一套模板,生成一个对应的项目结构
- generator-code模块是VS Code扩展生成器,与yo配合使用才能构建项目。
2、项目初始化
// 运行
yo code
执行 yo code之后,会有下面几个选项:
- ? What type of extension do you want to create(您想要创建什么类型的扩展?)? New Extension (
JavaScript
) - ? What's the name of your extension(你的分机名是什么,也就是项目名)?
demo
- ? What's the identifier of your extension(你的扩展的标识符是什么)?
demo
- ? What's the description of your extension(什么是您的扩展的描述)?
learn vscode plugin
- ? Enable JavaScript type checking in 'jsconfig.json'(在'jsconfig.json'中启用JavaScript类型检查)?
Yes
- ? Initialize a git repository(初始化一个git仓库)?
Yes
- ? bundel the source code with webpack (是否用webpack打包源码)?
Yes
- ? Which package manager to use(使用哪个包管理器)? yarn
在项目生成之后,目录结构如下所示
3、运行项目
然后,在编辑器中,按F5
。这将在新的扩展开发主机窗口中编译和运行扩展。
在新窗口中从命令面板 ( shift + win + p )运行Hello World
命令:
您 vscode 界面会看到Hello World from HelloWorld!
显示的通知。成功!
注意:如果按键F5无法启动台调试,鼠标点击debug
这样下次直接点击F5就会直接运行插件
4、文件介绍
1、package.json
该文件是vscode扩展的清单文件,里面有很多字段,官方 (opens new window)对每个字段都进行了详细阐述,本次我们重点阐述以下初始化后期清单文件。
{"name": "demo", // 插件名"displayName": "demo", // 显示在应用市场的名字"description": "learn vscode plugin", // 具体描述"version": "0.0.1", // 插件的版本号"publisher": "lzfuyun", // 发布publisher账户名"engines": {"vscode": "^1.60.0" // 最低支持的vscode版本},"categories": ["Other" // 扩展类别],// 激活事件组,在那些事件情况下被激活"activationEvents": ["onCommand:hello.helloWorld"],// 插件的主入口文件"main": "./extension.js",// 贡献点"contributes": {// 命令"commands": [{"command": "hello.helloWorld","title": "Hello World"}]},"scripts": {"lint": "eslint .","pretest": "npm run lint","test": "node ./test/runTest.js"},// 开发依赖项"devDependencies": {"@types/vscode": "^1.63.0","@types/glob": "^7.1.4","@types/mocha": "^9.0.0","@types/node": "14.x","eslint": "^8.1.0","glob": "^7.1.7","mocha": "^9.1.3","typescript": "^4.4.4","@vscode/test-electron": "^1.6.2"}
}
{// 插件的名字,应全部小写,不能有空格"name": "vscode-plugin-demo",// 插件的友好显示名称,用于显示在应用市场,支持中文"displayName": "VSCode插件demo",// 描述"description": "VSCode插件demo集锦",// 关键字,用于应用市场搜索"keywords": ["vscode", "plugin", "demo"],// 版本号"version": "1.0.0",// 发布者,如果要发布到应用市场的话,这个名字必须与发布者一致"publisher": "sxei",// 表示插件最低支持的vscode版本"engines": {"vscode": "^1.27.0"},// 插件应用市场分类,可选值: [Programming Languages, Snippets, Linters, Themes, Debuggers, Formatters, Keymaps, SCM Providers, Other, Extension Packs, Language Packs]"categories": ["Other"],// 插件图标,至少128x128像素"icon": "images/icon.png",// 扩展的激活事件数组,可以被哪些事件激活扩展,后文有详细介绍"activationEvents": ["onCommand:extension.sayHello"],// 插件的主入口"main": "./src/extension",// 贡献点,整个插件最重要最多的配置项"contributes": {// 插件配置项"configuration": {"type": "object",// 配置项标题,会显示在vscode的设置页"title": "vscode-plugin-demo","properties": {// 这里我随便写了2个设置,配置你的昵称"vscodePluginDemo.yourName": {"type": "string","default": "guest","description": "你的名字"},// 是否在启动时显示提示"vscodePluginDemo.showTip": {"type": "boolean","default": true,"description": "是否在每次启动时显示欢迎提示!"}}},// 命令"commands": [{"command": "extension.sayHello","title": "Hello World"}],// 快捷键绑定"keybindings": [{"command": "extension.sayHello","key": "ctrl+f10","mac": "cmd+f10","when": "editorTextFocus"}],// 菜单"menus": {// 编辑器右键菜单"editor/context": [{// 表示只有编辑器具有焦点时才会在菜单中出现"when": "editorFocus","command": "extension.sayHello",// navigation是一个永远置顶的分组,后面的@6是人工进行组内排序"group": "navigation@6"},{"when": "editorFocus","command": "extension.demo.getCurrentFilePath","group": "navigation@5"},{// 只有编辑器具有焦点,并且打开的是JS文件才会出现"when": "editorFocus && resourceLangId == javascript","command": "extension.demo.testMenuShow","group": "z_commands"},{"command": "extension.demo.openWebview","group": "navigation"}],// 编辑器右上角图标,不配置图片就显示文字"editor/title": [{"when": "editorFocus && resourceLangId == javascript","command": "extension.demo.testMenuShow","group": "navigation"}],// 编辑器标题右键菜单"editor/title/context": [{"when": "resourceLangId == javascript","command": "extension.demo.testMenuShow","group": "navigation"}],// 资源管理器右键菜单"explorer/context": [{"command": "extension.demo.getCurrentFilePath","group": "navigation"},{"command": "extension.demo.openWebview","group": "navigation"}]},// 代码片段"snippets": [{"language": "javascript","path": "./snippets/javascript.json"},{"language": "html","path": "./snippets/html.json"}],// 自定义新的activitybar图标,也就是左侧侧边栏大的图标"viewsContainers": {"activitybar": [{"id": "beautifulGirl","title": "美女","icon": "images/beautifulGirl.svg"}]},// 自定义侧边栏内view的实现"views": {// 和 viewsContainers 的id对应"beautifulGirl": [{"id": "beautifulGirl1","name": "国内美女"},{"id": "beautifulGirl2","name": "国外美女"},{"id": "beautifulGirl3","name": "人妖"}]},// 图标主题"iconThemes": [{"id": "testIconTheme","label": "测试图标主题","path": "./theme/icon-theme.json"}]},// 同 npm scripts"scripts": {"postinstall": "node ./node_modules/vscode/bin/install","test": "node ./node_modules/vscode/bin/test"},// 开发依赖"devDependencies": {"typescript": "^2.6.1","vscode": "^1.1.6","eslint": "^4.11.0","@types/node": "^7.0.43","@types/mocha": "^2.2.42"},// 后面这几个应该不用介绍了"license": "SEE LICENSE IN LICENSE.txt","bugs": {"url": "https://github.com/sxei/vscode-plugin-demo/issues"},"repository": {"type": "git","url": "https://github.com/sxei/vscode-plugin-demo"},// 主页"homepage": "https://github.com/sxei/vscode-plugin-demo/blob/master/README.md"
}
在这package.json
文件中,重点关注的主要有三部分内容:activationEvents
、main
以及contributes
,其是整个文件中的重中之重。
1、main
指明了该插件的主入口在哪,只有找到主入口整个项目才能正常的运转
2、activationEvents
指明该插件在何种情况下才会被激活,因为只有激活后插件才能被正常使用,官网已经指明了激活的时机 (opens new window),这样我们就可以按需设置对应时机。(具体每个时机用的时候详细查看即可)
- onLanguage 打开解析为特定语言文件时被激活,例如"onLanguage:python"
- onCommand 在调用命令时被激活
- onDebug 在启动调试话之前被激活
- onDebugInitialConfigurations
- onDebugResolve
- workspaceContains 每当打开文件夹并且该文件夹包含至少一个与 glob 模式匹配的文件时
- onFileSystem 每当读取来自特定方案的文件或文件夹时
- onView 每当在 VS Code 侧栏中展开指定 id 的视图
- onUri 每当打开该扩展的系统范围的 Uri 时
- onWebviewPanel
- onCustomEditor
- onAuthenticationRequest
- 只要一启动vscode,插件就会被激活
- onStartupFinished
3、contributes
通过扩展注册contributes用来扩展Visual Studio Code中的各项技能,其有多个配置,如下所示:
- breakpoints 断点
- colors 主题颜色
- commands 命令
- configuration 配置
- configurationDefaults 默认的特定于语言的编辑器配置
- customEditors 自定义编辑器
- debuggers
- grammars
- iconThemes
- jsonValidation
- keybindings 快捷键绑定
- languages
- menus
- problemMatchers
- problemPatterns
- productIconThemes
- resourceLabelFormatters
- snippets 特定语言的片段
- submenus
- taskDefinitions
- themes 颜色主题
- typescriptServerPlugins
- views
- viewsContainers
- viewsWelcome
- walkthroughs
2、extension.js
该文件时其入口文件,即 package.json
中 main
字段对应的文件(不一定叫extension.js这个名字),该文件中将导出两个方法:activate
和deactivate
,两个方法的执行时机如下所示:
-
activate:这是插件被激活时执行的函数
-
deactivate:这是插件被销毁时调用的方法,比如释放内存等。
5、实战-小试牛刀
需求:实现简单的选中大小写转换(document-editing)
1、package.json
{"name": "demo","displayName": "demo","description": "demo","version": "0.0.1","engines": {"vscode": "^1.69.0"},"categories": ["Other"],"activationEvents": ["*"],"main": "./extension","contributes": {"commands": [{"command": "extension.toLowerCase","title": "Lower"},{"command": "extension.toUpperCase","title": "Upper"}]},"scripts": {"vscode:prepublish": "npm run compile","compile": "tsc -p ./","lint": "eslint . --ext .ts,.tsx","watch": "tsc -watch -p ./"},"devDependencies": {"@types/glob": "^7.2.0","@types/mocha": "^9.1.1","@types/node": "16.x","@types/vscode": "^1.69.0","@vscode/test-electron": "^2.1.5","eslint": "^8.18.0","glob": "^8.0.3","mocha": "^10.0.0","typescript": "^4.7.4"}
}
配置两个命令一个转大些,一个转小写,如果有多个事件组,activationEvents就可以用✳️代替
"activationEvents": ["*"],
2、extension.ts文件
/** @Descripttion: automobile* @version: 1.0* @Author: 刘钊* @Date: 2022-07-23 10:51:31*/const vscode = require('vscode');/*** @param {vscode.ExtensionContext} context*/
function activate(context) {let lowerCase = vscode.commands.registerCommand('extension.toLowerCase',toLowerCase);let upperCase = vscode.commands.registerCommand('extension.toUpperCase',toUpperCase);context.subscriptions.push(lowerCase);context.subscriptions.push(upperCase);
}function toLowerCase() {toLowerCaseOrUpperCase('toLowerCase');
}function toUpperCase() {toLowerCaseOrUpperCase('toUpperCase');
}//转小写
function toLowerCaseOrUpperCase(command) {//获取activeTextEditorconst editor = vscode.window.activeTextEditor;if (editor) {const document = editor.document;const selection = editor.selection;//获取选中单词文本const word = document.getText(selection);//文本转大小写const newWord = command == 'toLowerCase' ? word.toLowerCase() : word.toUpperCase();//替换原来文本editor.edit((editBuilder) => {editBuilder.replace(selection, newWord);});}
}// this method is called when your extension is deactivated
function deactivate() {}module.exports = {activate,deactivate,
};
我们运行看看效果
6、webview详解Webview API | Visual Studio Code Extension API
整个VSCode编辑器就是一张大的网页,其实,我们还可以在Visual Studio Code
中创建完全自定义的、可以间接和nodejs
通信的特殊网页(通过一个acquireVsCodeApi
特殊方法),这个网页就叫WebView
。内置的Markdown
的预览就是使用WebView
实现的。使用Webview
可以构建复杂的、支持本地文件操作的用户界面。
VSCode插件的WebView类似于iframe的实现,但并不是真正的iframe(我猜底层应该还是基于iframe实现的,只不过上层包装了一层),通过开发者工具可以看到:
1、实现一个简单的webview功能
const vscode = require('vscode');/*** @param {vscode.ExtensionContext} context*/
function activate(context) {let disposable = vscode.commands.registerCommand('demo1.webview',showWebview);context.subscriptions.push(disposable);
}function showWebview() {// 创建webviewconst panel = vscode.window.createWebviewPanel('testWebview', // viewType'WebView演示', // 视图标题vscode.ViewColumn.One, // 显示在编辑器的哪个部位{enableScripts: true, // 启用JS,默认禁用retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置});panel.webview.html = `<html><body>你好,我是Webview</body></html>`;
}function deactivate() {}module.exports = {activate,deactivate,
};
可以看到创建一个panel,然后直接写html就可以了,是不是很简单,package.json配置好后,看看运行效果
- 默认情况下,在Web视图中禁用
JavaScript
,但可以通过传入enableScripts: true
选项轻松启用; - 默认情况下当webview被隐藏时资源会被销毁,通过
retainContextWhenHidden: true
会一直保存,但会占用较大内存开销,仅在需要时开启;
2、加载本地资源
出于安全考虑,Webview默认无法直接访问本地资源,它在一个孤立的上下文中运行,想要加载本地图片、js、css等必须通过特殊的vscode-resource:
协议,网页里面所有的静态资源都要转换成这种格式,否则无法被正常加载。
vscode-resource:
协议类似于file:
协议,但它只允许访问特定的本地文件。和file:
一样,vscode-resource:
从磁盘加载绝对路径的资源
默认情况下,vscode-resource:
只能访问以下位置中的资源:
- 扩展程序安装目录中的文件。
- 用户当前活动的工作区内。
- 当然,你还可以使用
dataURI
直接在Webview中嵌入资源,这种方式没有限制;
因此我们先抽个加载资源的函数:
1、线引入头文件
import { posix } from 'path';
const fs = require('fs');
2、替换路径
/*** 从某个HTML文件读取能被Webview加载的HTML内容* @param {*} context 上下文* @param {*} templatePath 相对于插件根目录的html文件相对路径*/function getWebViewContent(context, templatePath) {const resourcePath = posix.join(context.extensionPath, templatePath);const dirPath = posix.dirname(resourcePath);let html = fs.readFileSync(resourcePath, 'utf-8');// vscode不支持直接加载本地资源,需要替换成其专有路径格式,这里只是简单的将样式和JS的路径替换html = html.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => {return $1 + vscode.Uri.file(posix.resolve(dirPath, $2)).with({ scheme: 'vscode-resource' }).toString() + '"';});return html;
}
运行这段代码之后,会自动将HTML文件中link
、href
、script
、img
的资源相对路径全部替换成正确的vscode-resource:
绝对路径,例如:
./src/view/index.html
变成
vscode-resource:/Users/liuzhao/Documents/liuzhao/01_learn/15_vscode插件开发/demo1/src/view/index.html
3、执行函数
panel.webview.html = getWebViewContent(context, 'src/view/index.html');
/** @Descripttion: automobile* @version: 1.0* @Author: 刘钊* @Date: 2022-07-21 19:22:34*/
/** @Descripttion: automobile* @version: 1.0* @Author: 刘钊* @Date: 2022-07-21 19:22:34*/
const vscode = require('vscode');
const { posix } = require('path');
// import { posix } from 'path';
const fs = require('fs');/*** @param {vscode.ExtensionContext} context*/
function activate(context) {let disposable = vscode.commands.registerCommand('demo1.webview', () => {// 创建webviewconst panel = vscode.window.createWebviewPanel('testWebview', // viewType'WebView演示', // 视图标题vscode.ViewColumn.One, // 显示在编辑器的哪个部位{enableScripts: true, // 启用JS,默认禁用retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置});// panel.webview.html = `<html><body>你好,我是Webview</body></html>`;panel.webview.html = getWebViewContent(context, 'src/view/index.html');});context.subscriptions.push(disposable);
}function deactivate() {}/*** 从某个HTML文件读取能被Webview加载的HTML内容* @param {*} context 上下文* @param {*} templatePath 相对于插件根目录的html文件相对路径*/
function getWebViewContent(context, templatePath) {const resourcePath = posix.join(context.extensionPath, templatePath);const dirPath = posix.dirname(resourcePath);let html = fs.readFileSync(resourcePath, 'utf-8');// vscode不支持直接加载本地资源,需要替换成其专有路径格式,这里只是简单的将样式和JS的路径替换html = html.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g,(m, $1, $2) => {return ($1 +vscode.Uri.file(posix.resolve(dirPath, $2)).with({ scheme: 'vscode-resource' }).toString() +'"');});return html;
}module.exports = {activate,deactivate,
};
3、消息通信
webview
内部不允许发送ajax请求,所有ajax
请求都是跨域的,因为webview
本身是没有host
的
而VSCode
并不会让我们接触electron
配置,所以通过简单的electron
配置webSecurity: false
就可以开放跨域权限是行不通的,那么该如何实现网络请求呢?
webview
和普通网页一样,并不能直接调用任何VSCode API
。但是,它唯一特别之处就在于多了一个名叫acquireVsCodeApi
的方法,执行这个方法会返回一个简易版的vscode
对象,具有如下三个方法:
- getState()
- postMessage(msg)
- setState(newState)
这样的话,我们可以发消息让extension
去帮我们发送http
请求!
1、实现消息发送:
// 插件给Webview发送消息(支持发送任意可以被JSON化的数据)
panel.webview.postMessage(message);// webview接收消息
window.addEventListener('message', event => {const message = event.data;console.log('Webview接收到的消息:', message);
};// Webview主动发送消息给插件
const vscode = acquireVsCodeApi();
vscode.postMessage(message);// 插件端接收消息
panel.webview.onDidReceiveMessage(message => {console.log('插件收到的消息:', message);
}, undefined, context.subscriptions);
2、简单通信封装
Webview端:
const callbacks = {}; // 存放所有的回调函数
/*** 调用vscode原生api* @param data 可以是类似 {cmd: 'xxx', param1: 'xxx'},也可以直接是 cmd 字符串* @param cb 可选的回调函数*/
function callVscode(data, cb) {if (typeof data === 'string') {data = { cmd: data };}if (cb) {// 时间戳加上5位随机数const cbid = Date.now() + '' + Math.round(Math.random() * 100000);// 将回调函数分配一个随机cbid然后存起来,后续需要执行的时候再捞起来callbacks[cbid] = cb;data.cbid = cbid;}vscode.postMessage(data);
}
window.addEventListener('message', event => {const message = event.data;switch (message.cmd) {// 来自vscode的回调case 'vscodeCallback':console.log(message.data);(callbacks[message.cbid] || function () { })(message.data);delete callbacks[message.cbid]; // 执行完回调删除break;default: break;}
});
插件端:
let global = { projectPath, panel};
panel.webview.onDidReceiveMessage(message => {if (messageHandler[message.cmd]) {// cmd表示要执行的方法名称messageHandler[message.cmd](global, message);} else {util.showError(`未找到名为 ${message.cmd} 的方法!`);}
}, undefined, context.subscriptions);/*** 存放所有消息回调函数,根据 message.cmd 来决定调用哪个方法,* 想调用什么方法,就在这里写一个和cmd同名的方法实现即可*/
const messageHandler = {// 弹出提示alert(global, message) {util.showInfo(message.info);},// 显示错误提示error(global, message) {util.showError(message.info);},// 回调示例:获取工程名getProjectName(global, message) {invokeCallback(global.panel, message, util.getProjectName(global.projectPath));}
}
/*** 执行回调函数* @param {*} panel * @param {*} message * @param {*} resp */
function invokeCallback(panel, message, resp) {console.log('回调消息:', resp);// 错误码在400-600之间的,默认弹出错误提示if (typeof resp == 'object' && resp.code && resp.code >= 400 && resp.code < 600) {util.showError(resp.message || '发生未知错误!');}panel.webview.postMessage({cmd: 'vscodeCallback', cbid: message.cbid, data: resp});
}
4、生命周期
webview
由创建它的扩展程序所有,返回的panel
对象你必须自己保存,如果你的扩展程序丢失了这个引用,那么将无法再次重新访问该webview
,即使Web视图继续显示在vscode
中。
用户也可以随时关闭webview
面板。当用户关闭webview面板时,webview本身将被销毁,此时不能再使用panel引用,否则将会出现异常,可以通过监听onDidDispose
事件在这里面做一些销毁操作。
可以通过panel.dispose()
方法主动关闭webview。
5、实现webiew的状态保持
当webview移动到后台又再次显示时,webview中的任何状态都将丢失。
解决此问题的最佳方法是使你的webview无状态,通过消息传递来保存webview的状态。
1、在webview的js中我们可以使用vscode.getState()
和vscode.setState()
方法来保存和恢复JSON可序列化状态对象。当webview被隐藏时,即使webview内容本身被破坏,这些状态仍然会保存。当然了,当webview被销毁时,状态将被销毁。
2、通过注册WebviewPanelSerializer
可以实现在VScode
重启后自动恢复你的webview
,当然,序列化其实也是建立在getState
和setState
之上的。
注册方法:vscode.window.registerWebviewPanelSerializer
3、对于具有非常复杂的UI或状态且无法快速保存和恢复的webview
,我们可以直接使用retainContextWhenHidden
选项。设置retainContextWhenHidden: true
后即使webview被隐藏到后台其状态也不会丢失。
尽管retainContextWhenHidden
很有吸引力,但它需要很高的内存开销,一般建议在实在没办法的时候才启用。getState
和setState
是持久化的首选方式,因为它们的性能开销要比retainContextWhenHidden
低得多。
6、调试
按下Ctrl+Shift+P
然后执行打开Webview开发工具
,英文版应该是Open Webview Developer Tools:
7、菜单
菜单配置是在 package.json
的 contributes
中添加:
"menus": {"editor/title": [{"when": "editorFocus","command": "plugin-demo.helloWorld","alt": "","group": "navigation"}]
}
以上是一个菜单项的完整配置.
editor/title:
定义这个菜单出现在哪里,这里是定义出现在编辑标题菜单栏。when:
菜单在什么时候出现,这里是有光标的时候出现command:
点击这个菜单要执行的命令alt:
按住alt
再选择菜单时应该执行的命令group:
定义菜单分组
1、菜单出现的位置
- 资源管理器上下文菜单: explorer/context
- 编辑器上下文菜单: editor/context
- 编辑标题菜单栏: editor/title
- 编辑器标题上下文菜单: editor/title/context
- 调试callstack视图上下文菜单: debug/callstack/context
- SCM标题菜单:scm/title
- SCM资源组菜单:scm/resourceGroup/context
- SCM资源菜单:scm/resource/context
- SCM更改标题菜单:scm/change/title
- 左侧视图标题菜单:view/title
- 视图项菜单:view/item/context
- 控制命令是否显示在命令选项板中:commandPalette
2、菜单出现的条件when
多个条件可以通过与或非进行组合,例如:editorFocus && isWindows && resourceLangId == javascript
- resourceLangId == javascript:当编辑的文件是js文件时
- resourceFilename == test.js:当当前打开文件名是test.js时
- isLinux、isMac、isWindows:判断当前操作系统
- editorFocus:编辑器具有焦点时
- editorHasSelection:编辑器中有文本被选中时
- view == someViewId:当当前视图ID等于someViewId时
3、控制菜单的分组和排序group
不同的菜单拥有不同的默认分组
1、editor/context中的默认组:
- navigation:放在这个组的永远排在最前面;
- 1_modification:更改组;
- 9_cutcopypaste:编辑组
- z_commands:最后一个默认组,其中包含用于打开命令选项板的条目。
2、explorer/context中的默认组:
- navigation:放在这个组的永远排在最前面;
- 2_workspace:与工作空间操作相关的命令。
- 3_compare:与差异编辑器中的文件比较相关的命令。
- 4_search:与在搜索视图中搜索相关的命令。
- 5_cutcopypaste:与剪切,复制和粘贴文件相关的命令。
- 7_modification:与修改文件相关的命令。
8、快捷键
快捷键的设置比较简单,其执行功能同样依赖于其关联的命令command
。
"keybindings": [{"command": "plugin-demo.helloWorld","key": "ctrl+h","mac": "cmd+h","when": "editorTextFocus"}
]
command:
快捷键关联的命令key:
Windows平台对应的快捷键mac:
mac平台对应的快捷键when:
什么时候快捷键有效
当插件被激活后,并且满足快捷键有效的时间,按快捷键就可以找到extension.js
中与快捷键关联的command
所不绑定的事件并执行。