【JS】基于区块开发(三):VSCode插件

基于区块开发(三):VSCode插件

dabai发布于 今天 09:52

欢迎关注我的公众号睿Talk,获取我最新的文章:
【JS】基于区块开发(三):VSCode插件

一、前言

在 上一篇文章 中,我介绍了服务于区块开发的命令行工具是如何实现的,本文将沿着区块开发这一主题继续讲解 VSCode 插件的实现方式。

二、区块列表展示

如果刚接触 VSCode 插件开发,可以先看看我之前写的 VS Code插件开发介绍。

为了方便用户使用,我希望有一个专门的 tab 页分类列出所有的区块,先看一下效果:
【JS】基于区块开发(三):VSCode插件

要在左侧工具栏添加 tab,需要先在 package.json 文件中配置一个 View Container 和一个 View

"contributes": {

"viewsContainers": {

"activitybar": [

{

"id": "tce-block",

"title": "TCE Block",

"icon": "media/block.svg"

}

]

},

"views": {

"tce-block": [

{

"id": "tceBlock",

"name": "TCE Block",

"icon": "media/dep.svg",

"contextualTitle": "TCE Block"

}

]

}

}

这里指定了 tab 的位置放在左侧工具栏 activitybar,另一个可选项是 panel,在编辑器底部(终端)的位置。然后再给刚添加的这个 View Container 指定一个 View,通过 tce-block 这个 ID 进行关联。

下一步就是定制 View 的显示内容了。由于显示的内容就是一棵目录树,所以用到了 VSCode 插件开发中内置的 Tree View API。下面我们来定义一棵树,关键是实现 vscode.TreeDataProvider 这一接口:

class BlockProvider implements vscode.TreeDataProvider<Block> {

constructor(private workspaceRoot: string) {}

getTreeItem(element: Block): vscode.TreeItem {

return element;

}

async getChildren(element?: Block) {

if (!this.workspaceRoot) {

return [];

}

// 不是根目录,返回元素的children

if (element) {

if (element.children.length > 0) {

return element.children;

}

return null;

}

// 根目录,构造树形数据结构

else {

const resp = await fetchData<BlockCategories>(

'http://xxx.com/block-categories.json'

);

if (!resp) {

vscode.window.showErrorMessage('获取区块列表失败');

return [];

}

const { blocks } = resp;

return toBlock(blocks);

}

}

}

这里的重点是实现 getChildren 方法,返回树的数据结构。这里有 2 种情况:

  • 参数 element 为空时,说明是根目录,需要构造出树的第一层数据结构(数组)。
  • 参数 element 非空时,返回子节点数组。

toBlock 函数的作用是构造出树的所有节点。我设计的树只有 2 层,第一层是区块分类,第二层是区块实例:

function toBlock(categories: BlockItem[]): Block[] {

// 区块分类

return categories.map((category) => {

const { id, label, children } = category;

const categoryItem = new Block(

id,

label,

'',

vscode.TreeItemCollapsibleState.Collapsed

);

// 区块实例

categoryItem.children = children!.map((blockItem) => {

const { id: blockId, label: blockLabel, url } = blockItem;

const block = new Block(blockId, blockLabel, url!);

block.type = blockItem.type;

return block;

});

return categoryItem;

});

}

下面再来看树节点的定义,继承自 vscode.TreeItem

export class Block extends vscode.TreeItem {

children: Block[] = [];

type: number = 2;

constructor(

public readonly id: string,

public readonly label: string,

public readonly url: string,

public readonly collapsibleState?: vscode.TreeItemCollapsibleState

) {

super(label, collapsibleState);

this.id = id;

this.tooltip = `${this.label}区块`;

// 区块实例

if (url) {

this.contextValue = 'block'; // 控制操作按钮的显示隐藏

this.command = {

title: this.label, // 标题

command: 'tceBlock.openWebview', // 命令 ID

tooltip: this.label, // 鼠标覆盖时的小小提示框

arguments: [this], // 向 registerCommand 传递的参数。

};

}

}

}

Block 的定义是包含所有类型的节点的(区块分类和区块实例),所以需要根据构造函数传入的值来定义不同的行为,比如这里对于区块实例,会有 url 属性,点击他会打开一个 webview,这块在下一节详细讲解。

到此为止,树形结构已经能正常展示了。
【JS】基于区块开发(三):VSCode插件

三、预览区块

区块的预览本质上来说就是在 vscode 里面打开一个网页,这里就用到了 Webviews API。核心代码如下:

let currentPanel: vscode.WebviewPanel | undefined = undefined;

export function openWebView(url: string) {

if (currentPanel) {

currentPanel.dispose();

}

currentPanel = vscode.window.createWebviewPanel(

'tceBlock',

'TCE Block',

vscode.ViewColumn.One,

{

retainContextWhenHidden: true, // 控制是否保持webview面板的内容(iframe),即使面板不再可见。

enableScripts: true, // 下面的 html 页可以使用 Scripts

}

);

currentPanel.webview.html = getWebviewContent(url);

// Reset when the current panel is closed

currentPanel.onDidDispose(() => {

currentPanel = undefined;

}, null);

}

function getWebviewContent(url: string) {

return `<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Cat Coding</title>

<style>

html,

body {

margin: 0 !important;

padding: 0 !important;

width: 100%;

height: 100%;

}

#blockFrame {

width: 100%;

height: 100%;

}

</style>

</head>

<body>

<iframe id='blockFrame' scrolling="auto"></iframe>

</body>

</html>`;

}

代码很好理解,分为以下几步:

  • 新建一个 webviewPanel

  • 设置 webviewPanelhtml

  • html 中嵌入一个 iframe 来动态加载网页

这个操作会注册成 vscode 的一个命令,然后在点击区块实例的时候被调用:

vscode.commands.registerCommand('tceBlock.openWebview', (node: Block) => {

openWebView(node.url);

});

export class Block extends vscode.TreeItem {

constructor(...) {

...

// 区块实例

if (url) {

...

this.command = {

command: 'tceBlock.openWebview', // 执行上面定义的命令

arguments: [this], // 向 registerCommand 传递的参数

...

};

}

}

}

四、安装区块

通过区块列表安装

我们希望插入区块这个操作显示在区块实例的边上,当鼠标移动到对应区块时被激活:
【JS】基于区块开发(三):VSCode插件

这就需要在 package.json 文件中定义这个操作:

"contributes": {

"menus": {

"view/item/context": [

{

"command": "tceBlock.addBlock",

"when": "view == tceBlock && viewItem == block",

"group": "inline"

}

]

}

}

具体的区块插入代码跟 上一篇文章 大同小异,在此就不重复了。这里会用到一些 VSCode 的 API,如通过对话框的方式获取用户希望区块插入的位置:

const options: vscode.OpenDialogOptions = {

title: '请选择区块插入位置',

openLabel: '插入区块',

canSelectMany: false,

canSelectFiles: false,

canSelectFolders: true,

};

const fileUri = await vscode.window.showOpenDialog(options);

insertPath = fileUri[0].fsPath;

【JS】基于区块开发(三):VSCode插件

通过上下文菜单安装

为了省却选择区块安装目录的麻烦,还能直接在项目中通过上下文菜单的方式安装区块:
【JS】基于区块开发(三):VSCode插件

这需要在 package.json 中配置上下文菜单:

"contributes": {

"menus": {

"explorer/context": [

{

"command": "tceBlock.generateBlock",

"group": "1_modification"

}

]

}

}

选择插入区块后会提升选择区块实例:
【JS】基于区块开发(三):VSCode插件

这里用到了 VSCode 的另一个 API:

const blockNames: any[] = []

...

const blockItem = await vscode.window.showQuickPick(blockNames, {

placeHolder: '请选择要插入的区块',

});

五、总结

本文讲解了基于区块开发的 VSCode 插件的实现细节,主要功能是以树的形式展示区块列表、预览区块和安装区块。当中用到的 VSCode API 非常实用,可以用于开发读者自己设计的插件。

本文是本系列文章的终章,希望对你有所帮助,后会有期。

javascript前端blockreact.js

阅读 131更新于 今天 10:00

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议


睿Talk

记录个人和团队技术成长的点点滴滴

avatar

dabai

4.8k 声望

271 粉丝

0 条评论

得票时间

avatar

dabai

4.8k 声望

271 粉丝

宣传栏

欢迎关注我的公众号睿Talk,获取我最新的文章:
【JS】基于区块开发(三):VSCode插件

一、前言

在 上一篇文章 中,我介绍了服务于区块开发的命令行工具是如何实现的,本文将沿着区块开发这一主题继续讲解 VSCode 插件的实现方式。

二、区块列表展示

如果刚接触 VSCode 插件开发,可以先看看我之前写的 VS Code插件开发介绍。

为了方便用户使用,我希望有一个专门的 tab 页分类列出所有的区块,先看一下效果:
【JS】基于区块开发(三):VSCode插件

要在左侧工具栏添加 tab,需要先在 package.json 文件中配置一个 View Container 和一个 View

"contributes": {

"viewsContainers": {

"activitybar": [

{

"id": "tce-block",

"title": "TCE Block",

"icon": "media/block.svg"

}

]

},

"views": {

"tce-block": [

{

"id": "tceBlock",

"name": "TCE Block",

"icon": "media/dep.svg",

"contextualTitle": "TCE Block"

}

]

}

}

这里指定了 tab 的位置放在左侧工具栏 activitybar,另一个可选项是 panel,在编辑器底部(终端)的位置。然后再给刚添加的这个 View Container 指定一个 View,通过 tce-block 这个 ID 进行关联。

下一步就是定制 View 的显示内容了。由于显示的内容就是一棵目录树,所以用到了 VSCode 插件开发中内置的 Tree View API。下面我们来定义一棵树,关键是实现 vscode.TreeDataProvider 这一接口:

class BlockProvider implements vscode.TreeDataProvider<Block> {

constructor(private workspaceRoot: string) {}

getTreeItem(element: Block): vscode.TreeItem {

return element;

}

async getChildren(element?: Block) {

if (!this.workspaceRoot) {

return [];

}

// 不是根目录,返回元素的children

if (element) {

if (element.children.length > 0) {

return element.children;

}

return null;

}

// 根目录,构造树形数据结构

else {

const resp = await fetchData<BlockCategories>(

'http://xxx.com/block-categories.json'

);

if (!resp) {

vscode.window.showErrorMessage('获取区块列表失败');

return [];

}

const { blocks } = resp;

return toBlock(blocks);

}

}

}

这里的重点是实现 getChildren 方法,返回树的数据结构。这里有 2 种情况:

  • 参数 element 为空时,说明是根目录,需要构造出树的第一层数据结构(数组)。
  • 参数 element 非空时,返回子节点数组。

toBlock 函数的作用是构造出树的所有节点。我设计的树只有 2 层,第一层是区块分类,第二层是区块实例:

function toBlock(categories: BlockItem[]): Block[] {

// 区块分类

return categories.map((category) => {

const { id, label, children } = category;

const categoryItem = new Block(

id,

label,

'',

vscode.TreeItemCollapsibleState.Collapsed

);

// 区块实例

categoryItem.children = children!.map((blockItem) => {

const { id: blockId, label: blockLabel, url } = blockItem;

const block = new Block(blockId, blockLabel, url!);

block.type = blockItem.type;

return block;

});

return categoryItem;

});

}

下面再来看树节点的定义,继承自 vscode.TreeItem

export class Block extends vscode.TreeItem {

children: Block[] = [];

type: number = 2;

constructor(

public readonly id: string,

public readonly label: string,

public readonly url: string,

public readonly collapsibleState?: vscode.TreeItemCollapsibleState

) {

super(label, collapsibleState);

this.id = id;

this.tooltip = `${this.label}区块`;

// 区块实例

if (url) {

this.contextValue = 'block'; // 控制操作按钮的显示隐藏

this.command = {

title: this.label, // 标题

command: 'tceBlock.openWebview', // 命令 ID

tooltip: this.label, // 鼠标覆盖时的小小提示框

arguments: [this], // 向 registerCommand 传递的参数。

};

}

}

}

Block 的定义是包含所有类型的节点的(区块分类和区块实例),所以需要根据构造函数传入的值来定义不同的行为,比如这里对于区块实例,会有 url 属性,点击他会打开一个 webview,这块在下一节详细讲解。

到此为止,树形结构已经能正常展示了。
【JS】基于区块开发(三):VSCode插件

三、预览区块

区块的预览本质上来说就是在 vscode 里面打开一个网页,这里就用到了 Webviews API。核心代码如下:

let currentPanel: vscode.WebviewPanel | undefined = undefined;

export function openWebView(url: string) {

if (currentPanel) {

currentPanel.dispose();

}

currentPanel = vscode.window.createWebviewPanel(

'tceBlock',

'TCE Block',

vscode.ViewColumn.One,

{

retainContextWhenHidden: true, // 控制是否保持webview面板的内容(iframe),即使面板不再可见。

enableScripts: true, // 下面的 html 页可以使用 Scripts

}

);

currentPanel.webview.html = getWebviewContent(url);

// Reset when the current panel is closed

currentPanel.onDidDispose(() => {

currentPanel = undefined;

}, null);

}

function getWebviewContent(url: string) {

return `<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Cat Coding</title>

<style>

html,

body {

margin: 0 !important;

padding: 0 !important;

width: 100%;

height: 100%;

}

#blockFrame {

width: 100%;

height: 100%;

}

</style>

</head>

<body>

<iframe id='blockFrame' scrolling="auto"></iframe>

</body>

</html>`;

}

代码很好理解,分为以下几步:

  • 新建一个 webviewPanel

  • 设置 webviewPanelhtml

  • html 中嵌入一个 iframe 来动态加载网页

这个操作会注册成 vscode 的一个命令,然后在点击区块实例的时候被调用:

vscode.commands.registerCommand('tceBlock.openWebview', (node: Block) => {

openWebView(node.url);

});

export class Block extends vscode.TreeItem {

constructor(...) {

...

// 区块实例

if (url) {

...

this.command = {

command: 'tceBlock.openWebview', // 执行上面定义的命令

arguments: [this], // 向 registerCommand 传递的参数

...

};

}

}

}

四、安装区块

通过区块列表安装

我们希望插入区块这个操作显示在区块实例的边上,当鼠标移动到对应区块时被激活:
【JS】基于区块开发(三):VSCode插件

这就需要在 package.json 文件中定义这个操作:

"contributes": {

"menus": {

"view/item/context": [

{

"command": "tceBlock.addBlock",

"when": "view == tceBlock && viewItem == block",

"group": "inline"

}

]

}

}

具体的区块插入代码跟 上一篇文章 大同小异,在此就不重复了。这里会用到一些 VSCode 的 API,如通过对话框的方式获取用户希望区块插入的位置:

const options: vscode.OpenDialogOptions = {

title: '请选择区块插入位置',

openLabel: '插入区块',

canSelectMany: false,

canSelectFiles: false,

canSelectFolders: true,

};

const fileUri = await vscode.window.showOpenDialog(options);

insertPath = fileUri[0].fsPath;

【JS】基于区块开发(三):VSCode插件

通过上下文菜单安装

为了省却选择区块安装目录的麻烦,还能直接在项目中通过上下文菜单的方式安装区块:
【JS】基于区块开发(三):VSCode插件

这需要在 package.json 中配置上下文菜单:

"contributes": {

"menus": {

"explorer/context": [

{

"command": "tceBlock.generateBlock",

"group": "1_modification"

}

]

}

}

选择插入区块后会提升选择区块实例:
【JS】基于区块开发(三):VSCode插件

这里用到了 VSCode 的另一个 API:

const blockNames: any[] = []

...

const blockItem = await vscode.window.showQuickPick(blockNames, {

placeHolder: '请选择要插入的区块',

});

五、总结

本文讲解了基于区块开发的 VSCode 插件的实现细节,主要功能是以树的形式展示区块列表、预览区块和安装区块。当中用到的 VSCode API 非常实用,可以用于开发读者自己设计的插件。

本文是本系列文章的终章,希望对你有所帮助,后会有期。

以上是 【JS】基于区块开发(三):VSCode插件 的全部内容, 来源链接: utcz.com/a/113955.html

回到顶部