✍️ 一起学习 vscode 插件
- 01. 第一个 vscode 插件
- 02. 输入内容(Input)-例子
- 03. 自定义 WebView 欢迎页面-例子
- 04. 快捷键注册-例子
- 05. 自定义菜单 Menu-例子
- 06. 悬停提示-例子
- 07. 代码片段-例子
- 08. 自定义侧边栏+面板-例子
- 09. 读取文件夹目录+内容复制剪切板——例子
- 10. 自定义插件首选项配置,根据配置执行不同逻辑——例子
关键有效的 API 字段链接
✍️ 一起动手学习 vscode 插件
该插件场景适合一些需要输入内容之后做的操作;
// demo1 需要Input输入内容
import * as vscode from 'vscode'
module.exports = function (context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('beehive.inputName', () => {
vscode.window
.showInputBox({
ignoreFocusOut: true,
password: false,
prompt: 'entry your name',
})
.then((value) => {
if (value === undefined || value.trim() === '') {
vscode.window.showInformationMessage('Please type your name.')
} else {
const name = value.trim()
vscode.window.showInformationMessage('your name is: ', name)
return
}
})
})
context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('your extension "sugar-demo-vscode" is now active!')
require('./beehive-inputName')(context) // demo1 输入Input内容
}
export function deactivate() {}
当你鼠标光标hover至某个代码时,你想要显示一些文字内容
// demo6 对package.json中的author进行悬停提示
import * as vscode from 'vscode'
module.exports = function (context: vscode.ExtensionContext) {
let disposable = vscode.languages.registerHoverProvider('json', {
provideHover(document, position, token) {
const fileName = document.fileName
const word = document.getText(document.getWordRangeAtPosition(position))
if (/\/package\.json$/.test(fileName) && /\bauthor\b/.test(word)) {
return new vscode.Hover('悬停提示: 彭道宽牛逼!')
}
return undefined
},
})
context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('your extension "sugar-demo-vscode" is now active!')
require('./beehive-hoverTips')(context) // demo6 悬停提示
}
export function deactivate() {}
适用于一些快捷按钮的自定义,可通过Menu操作
// demo5 自定义菜单
import * as vscode from 'vscode'
module.exports = function (context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand('beehive.customMenu', () => {
vscode.window.showInformationMessage("I' am custom menu !")
})
context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('your extension "sugar-demo-vscode" is now active!')
require('./beehive-customMenu')(context) // demo5 自定义菜单
}
export function deactivate() {}
// package.json
"commands": [
{
"command": "beehive.customMenu",
"title": "demo5: 启动自定义菜单"
}
],
"menus": {
"editor/title": [
{
"command": "beehive.customMenu",
"alt": "beehive.customMenu",
"group": "navigation"
}
]
},
点击之后,触发事件
上一个例子: 自定义侧边栏+面板虽然可以实现我们的功能,但是还是有些缺点的,毕竟我们需要根据文件路径来动态生成我们的面板内容。
以下面例子为例:根据 vscode
工作目录,读取目录中 package.json
文件的 scripts 字段,筛选出符合规范的脚本命令,动态生成我们的按钮
首先需要明确的是:vscode有一个 vscode.workspace.rootPath
,由于后来vscode支持multipleRoot模式,所以这个字段已经过时作废了。我们只能通过 vscode.workspace.workspaceFolders
获取当前工作区所有根文件夹数组;
注意:是得到的跟文件夹数组路径,也就是说,下面这种情况,得到是路径内容是这样的
├── A_Folder
│ ├── B_Folder
│ │ ├── D_Folder
│ │ └──
│ │
│ ├── C_Folder
│ └──
└──
上面得到的只会是 : A_Folder 的路径,得不到下面的 B、C、D路径。
还需要注意的一点是:很难划分这个文件夹是不是属于一个前端工程或者Node工程,这边我是以该文件夹下有没有 package.json
来划分,也就是如果 A下面存在 package.json
,我就认为它是一个完整的工程项目(不把它当作文件夹)
如果你真的想得,我的想法是:只能通过 Node 的 fs 模块去获取 A 文件夹下的文件目录,然后递归,一路找下去,办法总会有的。
OK,扯远了,来看看大概的一个思路
先注册一下侧边栏面板
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('插件已启动,忙去吧~')
require('./container/commandSet')(context)
}
export function deactivate() {}
不要忘记 package.json
也需要添加
{
"contributes": {
"commands": [],
"viewsWelcome": [
{
"view": "BeeHive-Command",
"contents": "提高你的效率,释放你的双手~"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "sugar",
"title": "Sugar-BeeHive",
"icon": "./assets/logo_default.svg"
}
],
"panel": [
{
"id": "sugar",
"title": "Package Explorer",
"icon": "./assets/logo_default.svg"
}
]
},
"views": {
"sugar": [
{
"id": "BeeHive-Command",
"name": "01.命令集"
},
{
"id": "BeeHive-Package",
"name": "02.包分析"
}
]
}
},
}
接下来就是我们的重头戏了,我们看看 require
进来的 commandSet 怎么写的~
// commandSet.ts
import * as vscode from 'vscode'
import SideBarCommand from './SideBarCommand'
import { PREFIX } from '../../constants'
import { ShellType } from '../../type/common'
import { getWorkSpaceFolderList } from '../../utils'
module.exports = function (context: vscode.ExtensionContext) {
// 1. 得到vscode所有工程项目
const folderList = getWorkSpaceFolderList()
// 2. 注册侧边栏面板
const sideBar = new SideBarCommand(folderList)
vscode.window.registerTreeDataProvider('BeeHive-Command', sideBar)
// 3. 注册命令
vscode.commands.registerCommand(
'BeeHive-Command.openChild',
(args: { title: string; shell: ShellType; [key: string]: any }) => {
const { title, shell = null, path = '' } = args
const reg = new RegExp(`${PREFIX}`)
if (reg.test(title)) {
vscode.window.showInformationMessage(title)
} else {
// 4. 复制到剪切板
vscode.env.clipboard.writeText(`cd ${path} \n npm run ${shell?.key}`)
vscode.window.showInformationMessage(
`ok, fine ! shell copied to clipboard ~`
);
}
}
)
}
接下来的重头戏就是,我们实现的这个 SideBarCommand
了,这里主要重写了 getChildren
方法,通过动态去生成面板内容
// SideBarCommand.ts
/**
* @description 命令集侧边栏实例
*/
import * as vscode from 'vscode'
import { PREFIX } from '../../constants'
import { FolderType, ShellType } from '../../type/common'
import { isExist, read, getShellFromScripts } from '../../utils/package'
import { SideBarEntryItem, SideBarEntryListImplements,} from '../../factory/SideBar'
function getNode(
title: string,
description?: string,
args?: { [key: string]: any }
) {
let node = new SideBarEntryItem(title, vscode.TreeItemCollapsibleState.None, description)
node.command = {
command: 'BeeHive-Command.openChild', //命令id
title: title,
arguments: [{ title, ...args }], //命令接收的参数
}
return node
}
export default class SideBarCommand extends SideBarEntryListImplements {
constructor(private folderPathList: FolderType[] | undefined) {
super()
}
getChildren(
element: SideBarEntryItem | undefined
): vscode.ProviderResult<SideBarEntryItem[]> {
if (element) {
var childrenList: any = []
if (isExist(`${element.path}/package.json`)) {
const packageValues = read(`${element.path}/package.json`)
if (packageValues && packageValues.scripts) {
const eggShell = getShellFromScripts(packageValues.scripts, 'server')
const webpackShell = getShellFromScripts(packageValues.scripts, 'webpack')
const shellList = [...webpackShell, ...eggShell]
if (!!shellList.length) {
shellList.forEach((shell: ShellType, index: number) => {
const node = getNode(shell.key, `[${shell.environment}]`, { shell, path: element.path })
childrenList[index] = node
})
} else {
const noneNode = getNode(`[${PREFIX}]: scripts 脚本命令不符合规则`)
childrenList = [noneNode]
}
} else {
const noneNode = getNode(`[${PREFIX}]: 不存在 scripts 脚本命令`)
childrenList = [noneNode]
}
} else {
const noneNode = getNode(`[${PREFIX}]: 工程项目不存在package.json`)
childrenList = [noneNode]
}
return childrenList
} else {
const folderNode = this.folderPathList?.map((folder: FolderType) => {
return new SideBarEntryItem(
folder.name,
vscode.TreeItemCollapsibleState.Collapsed,
'',
folder.path
)
})
return folderNode
}
}
}
上面的例子来自实战: vscode-beehive-extension
具体关于业务代码,就不展示了~
该插件场景适合做一些简单页面展示、欢迎页面等
// demo3 自定义显示页
// 具体看 package.json 中的 configuration 和 commands
import * as vscode from 'vscode'
import * as fs from 'fs'
import * as path from 'path'
module.exports = function (context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
'beehive.customWelcome',
() => {
const panel = vscode.window.createWebviewPanel(
'welcome',
'自定义欢迎页',
vscode.ViewColumn.One,
{
enableScripts: true,
}
)
const htmlPath = path.join(
context.extensionPath,
'src/customWelcome.html'
)
let html = fs.readFileSync(htmlPath, 'utf-8')
panel.webview.html = html
}
)
context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('your extension "sugar-demo-vscode" is now active!')
require('./beehive-customWelcome')(context) // demo3 加载自定义WebView欢迎页
}
export function deactivate() {}
// package.json
"commands": [
{
"command": "beehive.customWelcome",
"title": "demo4: beehive.customWelcome !"
}
],
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo3 自定义欢迎页面</title>
<style>
.app {
margin: 12px 0;
font-size: 24px;
}
.title {
font-size: 18px;
margin-top: 24px;
}
</style>
</head>
<body>
<div class="app">
Welcome BeeHiver ~
<div class="title">后人哀之而不鉴之 亦使后人而复哀后人也 !</div>
</div>
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
</body>
</html>
每次我们都需要 cmd+shift + P 调出选择器,然后输入我们注册的事件名,特别麻烦,vscode 支持快捷键注册,下面看看如何实现吧!
"commands": [
{
"command": "beehive.keybindings",
"title": "demo4: beehive.keybindings !"
}
],
"keybindings": [
{
"command": "beehive.keybindings",
"key": "Cmd+]",
"mac": "Cmd+]",
"when": "editorTextFocus"
}
]
然后此时我们通过 Run Extension ,在本地窗口,我们随便打开一个文件,然后按下 : cmd + ] 就可以触发我们定义的
beehive.keybindings
事件
需注意,因为我们加了 when 属性,这边表示的是当编辑器聚焦时才可以
每一个插件都可以自行添加首选项的配置,当打开 vscode
时,根据首选项选择的配置,执行不同的逻辑,接下来说说如何实现此效果
我们现在 package.json
中配置一下我们首选项参数
{
"contributes": {
"configuration": {
"title": "sugar-demo-vscod",
"properties": {
"sugar-demo-vscode.matchConfig": {
"type": "string",
"description": "sugar-demo-vscod 配置,默认低配版本",
"enum": [
"lowMatch",
"middleMatch",
"highMatch"
],
"default": "lowMatch",
"scope": "window"
}
}
}
}
}
这里需要注意,名称 sugar-demo-vscode
要一致!
上面我们已经实现了首选项配置,看看效果
我们再获取配置,然后执行不同逻辑
// 获取用户配置的版本设置
const matchConfig = vscode.workspace.getConfiguration().get('vscode-beehive-extension.matchConfig')
if (matchConfig === MATCH_CONFIG_MAPS.LOW) {
console.log('低配')
} else if (matchConfig === MATCH_CONFIG_MAPS.MIDDLE) {
console.log('中配')
} else if (matchConfig === MATCH_CONFIG_MAPS.HIGH) {
console.log('高配')
} else {
vscode.window.showErrorMessage(`unknown error`)
}
如果要通过代码修改 matchConfig 内容,可以通过
// 最后一个参数,为true时表示写入全局配置,为false或不传时则只写入工作区配置
vscode.workspace.getConfiguration().update('vscode-beehive-extension.matchConfig', 'middleMatch, true);
需要在左侧自定义侧边栏,完成一些交互逻辑操作
首先,我们先看官方文档,看看如何在左边这个侧边栏添加我们自定义的内容
👉 contribution-points#contributes.viewsContainers
// package.json
{
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "sugar",
"title": "Sugar-BeeHive",
"icon": "./src/logo/sugar.svg"
}
]
},
"views": {
"sugar": [
{
"id": "BeeHive-Command",
"name": "01.命令集"
},
{
"id": "BeeHive-PackageAnalysis",
"name": "02.包分析"
}
]
},
}
}
这时候运行我们的插件:Run Extenstion
,就可以看到在左侧有我们自定义的侧边栏啦
关于图标svg这个大家自己注意一下路径就好了,我这边重点不是图标哈~
上面我们配置完 package.json
之后,我们再回到文档,会看到这么一段话:tree-view#activationEvents
如果需要,你就加上下面这段代码即可
"activationEvents": [
// 这个id是对应 contributes.views 中的 id
"onView:BeeHive-Command",
],
上面是展示出来了侧边栏,但是我们需要展示内容啊,怎么整?通过官方文档:tree-data-provider 可以实现一个小 demo
// beehive-sidebar.ts
// demo8 自定义侧边栏入口和面板
import * as vscode from 'vscode'
const scripts = [
{
script: 'webpack:dev',
},
{
script: 'webpack:prod',
},
{
script: 'server:dev',
},
{
script: 'server:test',
},
{
script: 'server:test-1',
},
{
script: 'server:test-2',
},
]
/**
* @description 重写每个节点
*/
export class SideBarEntryItem extends vscode.TreeItem {
constructor(private version: string, public readonly label: string, public readonly collapsibleState: vscode.TreeItemCollapsibleState
) {
super(label, collapsibleState)
this.tooltip = `${this.label}-${this.version}`
this.description = `${this.version}-${Math.ceil(Math.random() * 1000)}`
}
}
/**
* @description 入口文件
*/
export class SideBarBeeHiveCommand implements vscode.TreeDataProvider<SideBarEntryItem> {
constructor(private workspaceRoot?: string) {}
getTreeItem(element: SideBarEntryItem): vscode.TreeItem {
return element
}
getChildren(element?: SideBarEntryItem): vscode.ProviderResult<SideBarEntryItem[]> {
if (element) {
//子节点
var childrenList = []
for (let index = 0; index < scripts.length; index++) {
var item = new SideBarEntryItem('1.0.0', scripts[index].script, vscode.TreeItemCollapsibleState.None)
item.command = {
command: 'BeeHive-Command.openChild', //命令id
title: scripts[index].script,
arguments: [scripts[index].script], //命令接收的参数
}
childrenList[index] = item
}
return childrenList
} else {
//根节点
return [
new SideBarEntryItem('1.0.0', '按钮组', vscode.TreeItemCollapsibleState.Collapsed),
]
}
}
}
export class SideBarBeeHivePackageAnalysis implements vscode.TreeDataProvider<SideBarEntryItem> {
constructor(private workspaceRoot?: string) {}
getTreeItem(element: SideBarEntryItem): vscode.TreeItem {
return element
}
getChildren(element?: SideBarEntryItem): vscode.ProviderResult<SideBarEntryItem[]> {
if (element) {
//子节点
var childrenList = []
for (let index = 0; index < scripts.length; index++) {
var item = new SideBarEntryItem('1.0.0', scripts[index].script, vscode.TreeItemCollapsibleState.None)
item.command = {
command: 'BeeHive-PackageAnalysis.openChild', //命令id
title: scripts[index].script,
arguments: [index], //命令接收的参数
}
childrenList[index] = item
}
return childrenList
} else {
//根节点
return [
new SideBarEntryItem('1.0.0', '按钮组', vscode.TreeItemCollapsibleState.Collapsed),
]
}
}
}
module.exports = function (context: vscode.ExtensionContext) {
// 注册侧边栏面板
const sidebarBeeHiveCommand = new SideBarBeeHiveCommand()
const sidebarBeeHivePackageAnalysis = new SideBarBeeHivePackageAnalysis()
vscode.window.registerTreeDataProvider(
'BeeHive-Command',
sidebarBeeHiveCommand
)
vscode.window.registerTreeDataProvider(
'BeeHive-PackageAnalysis',
sidebarBeeHivePackageAnalysis
)
//注册命令
vscode.commands.registerCommand('BeeHive-Command.openChild', (args) => {
console.log('[BeeHive-Command.openChild] 当前选中的是:', args)
vscode.window.showInformationMessage(args)
})
vscode.commands.registerCommand(
'BeeHive-PackageAnalysis.openChild',
(args) => {
console.log('[BeeHive-PackageAnalysis.openChild] 当前选中的是:', args)
vscode.window.showInformationMessage(args)
}
)
}
然后在入口文件 extenstion.ts
添加该文件
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('your extension "sugar-demo-vscode" is now active!')
require('./beehive-sidebar')(context) // demo8 自定义侧边栏入口和面板
}
export function deactivate() {}
如果需要点击左侧侧边栏的节点时触发内容,只需要在 arguments
里面回传一些内容,然后做对应的业务操作即可
在我们的面板内容还没定义时,我们想要显示一些缺省内容提示?通过文档,我们可以知道 View Welcome content
"contributes": {
"viewsWelcome": [
{
"view": "BeeHive-Command",
"contents": "提高你的效率,释放你的双手~"
}
]
}
✅ nodejs
✅ npm
✅ yeoman
✅ generator-code
为了降低开发门槛,微软做了一个 Yeoman 代码生成命令,可以很方便的生成插件开发需要的模板代码,可以通过以下命令安装:
npm install -g yo generator-code
当上面的安装完毕之后,只需要进入你开发目录文件夹,通过脚手架生成一个开发 vscode 插件的项目。
yo code
根据提示信息填写相关内容
这时候我们的 vscode 插件项目就 OK 了~ 接下来我们看看如何运行起来!
项目中两个重要的文件我们需要看一下:extension.ts 和 package.json
该文件是入口文件,下面我们写一段简单的 demo 代码
此段代码意思为:注册一个 beehive.toastDemo 事件,当触发此事件会显示一段 message
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
console.log('your extension "sugar-demo-vscode" is now active!')
let disposable = vscode.commands.registerCommand('beehive.toastDemo', () => {
vscode.window.showInformationMessage('toastDemo touched !')
})
context.subscriptions.push(disposable)
}
export function deactivate() {}
该文件配置项太多,建议去官方文档翻阅
关键是 : activationEvents 和 contributes 这两个属性,关于 activationEvents 的意思和注意已经在上图说明
下面讲一下 contributes 相关信息,contributes 配置项是整个插件的贡献点,也就是说这个插件有哪些功能。contributes 字段可以设置的 key 也基本显示了 vscode 插件可以做什么。
点击此 Debug Icon,或者是 vscode 菜单栏:Run -> Start Debugging
当我们点击下 Run Extension 时,会开一个本地 vscode 窗口
我们在新开的 vscode 窗口中输入 : cmd + shift + P
然后输入我们注册的事件:beehive.toastDemo
,然后我们按下回车,就会执行我们写的事件回调了!
至此,我们的第一个简单 Demo 完成!
输入一个前缀,会得到一个或多个提示,然后回车带出很多代码。
需要修改 package.json 中的 snippets 的配置
// package.json
"snippets": [
{
"language": "html",
"path": "./src/snippets/html.json"
}
]
然后添加一个 html.json 配置
{
"PDK": {
"prefix": ["PDK", "PD", "PK", "DK"],
"body": ["<PDK>", "${1}", "</PDK>"],
"description": "彭道宽自定义的snippets"
}
}
关于每个字段,可以通过官方文档了解:create-your-own-snippets
上面我们是设置语言为 : html,所以在运行插件,并保证插件被激活,在规定的语言html中,输入 prefix 相关的关键词,就可以啦
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.