Vscode 插件开发 - TreeView

树状视图很多插件都可能用到,通常作为一个简单的列表使用,比如管理项目依赖、展示搜索结果。那么 Vscode 的 TreeView 具体怎么玩呢?

基础操作

1.创建视图

在 package.json 的 contributes.views 节点下定义视图:

json 复制代码
{
  "name": "custom-view-samples",
  ...
  "contributes": {
    "views": {
      "my-tree-view": [
        {
          "id": "myTreeView",
          "name": "My Tree View"
        }
      ]
    }
  },
  ...
}

创建视图:

less 复制代码
vscode.window.createTreeView('myTreeView', {});

2.实现树节点

首先实现树节点的数据结构:

typescript 复制代码
class MyTreeItem extends vscode.TreeItem {
  constructor(
    public readonly label: string,
    public readonly collapsibleState: vscode.TreeItemCollapsibleState
  ) {
    super(label, collapsibleState);
    this.tooltip = `MyTreeItem - ${this.label}`;
    this.iconPath = new vscode.ThemeIcon('file');    this.command = {      title: 'Click on me',      command: 'myExtension.myTree.onClick',      arguments: [ { label } ]    };  }
}

vscode.TreeItem 预定义了诸多属性,允许我们控制 TreeItem 的外观与行为:

  • label:用于节点展示文本

  • iconPath:用于设置节点图标,可通过 new vscode.ThemeIcon('file'); 引用 Vscode 内置图标

  • description:节点描述,设置之后将展示在 label 内容之后

  • tooltip:节点提示,当鼠标悬停时展示

  • command:用于设置鼠标点击时绑定的操作,这是一个对象:

  • title:名称

  • command:命令 ID

  • arguments:参数数组

  • collapsibleState:控制节点是否能够展开

  • contextValue:用来控制树状视图的特殊行为,与 action(视图绑定的操作)产生联动,在定义 action 时,通过判断节点 contextValue 来实现在指定节点上绑定操作,如:

    // package.json "contributes": { "menus": { "view/item/context": [ { "command": "myExtension.myTree.deleteNode", "when": "viewItem == nodeA" // nodeA 为 contextValue } ] }}

3.为视图提供数据

通过实现vscode.TreeDataProvider为树状视图提供数据,主要实现以下方法:

  • getChildren(element?: T): ProviderResult<T[]>;

在加载根节点时,入参为undefined;入参有值时,表示加载该节点的子节点。T 是泛型,需要替换为自己实现的树节点类型。如:

kotlin 复制代码
  getChildren(element?: MyTreeItem): vscode.ProviderResult<MyTreeItem[]> {
    if (element) {
      return Promise.resolve(this.loadItemsOfRootNode());
    } else {
      return Promise.resolve(this.loadItemsUnder(element));
    }
  }

4.注册 TreeDataProvider

通过调用 registerTreeDataProvider 注册视图数据 Provider:

dart 复制代码
vscode.window.registerTreeDataProvider(
  'myTreeDataProvider', // 视图 ID
  new MyTreeDataProvider()
);

或者在创建视图时绑定:

php 复制代码
vscode.window.createTreeView('myTreeView', {  treeDataProvider: new MyTreeDataProvider()});

5.刷新视图

要实现视图刷新,我们需要实现 TreeDataProvider 的事件触发器属性:

  • _onDidChangeTreeData

  • onDidChangeTreeData

这两个属性一个 public 可见,一个 private 可见。实现如下:

typescript 复制代码
private _onDidChangeTreeData: vscode.EventEmitter<Dependency | undefined | null | void> = new vscode.EventEmitter<Dependency | undefined | null | void>();
readonly onDidChangeTreeData: vscode.Event<Dependency | undefined | null | void> = this._onDidChangeTreeData.event;

我们可以通过 _onDidChangeTreeData 触发刷新:

kotlin 复制代码
this._onDidChangeTreeData.fire();

Vscode 在监听到该事件后会重新渲染视图。其他 module 可以访问 onDidChangeTreeData 属性监听刷新事件:

javascript 复制代码
myTreeDataProvider.onDidChangeTreeData(() => { console.log('do something') });

进阶

1.实现拖拽

首先实现 vscode.TreeDragAndDropController,定义如下:

typescript 复制代码
export interface TreeDragAndDropController<T> {  readonly dropMimeTypes: readonly string[];  readonly dragMimeTypes: readonly string[];  handleDrag?(source: readonly T[], dataTransfer: DataTransfer, token: CancellationToken): Thenable<void> | void;  handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable<void> | void;}

dropMimeTypesdragMimeTypes 的值是与树状视图的 id 关联的,也就是用来告诉 Vscode 该控制器只处理从 dragMimeTypes 指定的视图 A 拖拽到 dropMimeTypes 指定的视图 B,如:

csharp 复制代码
dropMimeTypes: readonly string[] = ['application/vnd.code.tree.myExtension_myTreeView'];dragMimeTypes: readonly string[] = ['application/vnd.code.tree.myExtension_myTreeView'];

这里的 'application/vnd.code.tree.myExtension_myTreeView' 对应了 package.json 里定义的:

json 复制代码
"views": {  "automan": [    {      "id": "myExtension.myTreeView",      "name": "MyTreeView"    }  ]},

注意命名转换规则,vscode 对此要求比较严格但官方文档解释又很简略:

  • 点号转换为下划线
  • 点好分割出的每个词必须首字母小写

接下来需要实现 handleDrophandleDrag 的逻辑。handleDrag 为拖拽开始时要做的动作,在这个方法里我们能知道拖拽开始于哪个元素,同时我们需要将数据使用 MIME_TYPE 进行标记、以传递给 handleDrop 方法:

php 复制代码
handleDrag(source: readonly MyTreeItem[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Thenable<void> | void {  dataTransfer.set(MIME_TYPE, new vscode.DataTransferItem(source));}

接着在 handleDrop 中取出数据,实现移动逻辑:

php 复制代码
handleDrop(target: MyTreeItem | undefined, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Thenable<void> | void {  const transferItem = dataTransfer.get(MIME_TYPE);  if (!transferItem) {    return;  }
  ...
}

将控制器绑定到视图上:

php 复制代码
vscode.window.createTreeView('myTreeView', {  treeDataProvider: new MyTreeDataProvider(),  showCollapseAll: true,  canSelectMany: true,  dragAndDropController: new MyTreeDragAndDropController()});

在创建视图的同时设置 canSelectMany,以允许同时拖拽多个项目。

相关推荐
mango大侠2 天前
Ubuntu24.04 安装 visual studio code
ide·vscode·编辑器·visual studio code
故苏呦2 天前
Visual Studio Code 快捷键
visual studio code·visual studio
White graces5 天前
掌握HTML, 从零开始的网页设计
开发语言·前端·windows·edge·html·visual studio code
码农老起5 天前
常用代码开发工具技术分享
git·docker·github·visual studio code·visual studio
草梅友仁8 天前
2024 年第 51 周草梅周报:Windsurf,比 Cursor 更好用的 AI 编辑器
aigc·visual studio code·bun
子洋18 天前
Mac 下 vscode 更新报错
前端·javascript·visual studio code
Eric_见嘉22 天前
Cursor 会被打败:使用 Windsurf 一键生成 2048 小游戏
前端·aigc·visual studio code
巫师练法术23 天前
VScode下构建python的虚拟环境
visual studio code
Yang.991 个月前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
WXDWIN.1 个月前
C++语言之模版与类型转换
c语言·开发语言·c++·visualstudio·visual studio code