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,以允许同时拖拽多个项目。

相关推荐
WDeLiang12 小时前
Flutter 环境搭建
flutter·ios·visual studio code
叶小秋2 天前
用了 Trae 后,感觉离京东外卖不远了
前端·visual studio code·trae
从文处安3 天前
VS Code 中 Tailwind CSS 类名自动补全和语法高亮失效
visual studio code
我怎么能这么帅气3 天前
从零开始开发一个简单的VS Code插件(2)Git推送插件实战 - 简化Git推送Gerrit并指定reviewer流程
visual studio code
我怎么能这么帅气3 天前
从零开始开发一个简单的VS Code插件(3)产品化之路 - 从代码到市场发布
visual studio code
几米哥3 天前
Augment Code全面解析:新晋AI编程助手全面提升开发效率
aigc·visual studio code
demo007x3 天前
二、从 0 开始构建一个代码库-技术选型
visual studio code·cursor·trae
我怎么能这么帅气4 天前
从零开始开发一个简单的VS Code插件(1)初识插件开发 - 打造你的第一个自动化命令
visual studio code
VT.馒头4 天前
【Cursor】AI 赋能的编程神器使用全攻略:安装、配置与无限使用技巧
人工智能·aigc·visual studio code·cursor·ai agent
萌萌哒草头将军4 天前
VsCode Colipot 🚗 + MCP Tools ✈️ = 让你的编程体验直接起飞 🚀🚀🚀
前端·visual studio code·mcp