树视图 API
树视图 API 允许扩展在 Visual Studio Code 的侧边栏中显示内容。此内容以树的形式组织,并符合 VS Code 的内置视图的样式。
例如,内置的引用搜索视图扩展将引用搜索结果显示为一个单独的视图。

所有引用查找的结果显示在引用: 结果树视图中,该视图位于引用视图容器中。
本指南将教您如何编写一个扩展,该扩展向Visual Studio Code贡献树视图和视图容器。
树视图 API 基础知识
为了解释TreeView API,我们将构建一个名为Node Dependencies的示例扩展。此扩展将使用TreeView显示当前文件夹中的所有Node.js依赖项。添加TreeView的步骤是在你的package.json创建一个树数据提供者, 并注册树数据提供者您可以在此处找到此示例扩展的完整源代码树视图示例 在 vscode-extension-samples GitHub 代码库中。
package.json 贡献
首先,您需要让 VS Code 知道您正在贡献一个视图,使用 contributes.views 贡献点 package.json输入:.
这是package.json对于我们扩展的第一个版本:
{
"name": "custom-view-samples",
"displayName": "Custom view Samples",
"description": "Samples for VS Code's view API",
"version": "0.0.1",
"publisher": "alexr00",
"engines": {
"vscode": "^1.74.0"
},
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"views": {
"explorer": [
{
"id": "nodeDependencies",
"name": "Node Dependencies"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./"
},
"devDependencies": {
"@types/node": "^10.12.21",
"@types/vscode": "^1.42.0",
"typescript": "^3.5.1",
"tslint": "^5.12.1"
}
}
注意:如果您的扩展针对的是 VS Code 1.74 之前的版本,则必须明确列出
在查看:节点依赖在激活事件输入:.
您必须为视图指定一个标识符和名称,并且您可以为以下位置做出贡献:
探险者:侧边栏中的资源管理器视图调试运行和调试视图在侧栏中scm源代码控制视图在侧栏中测试测试浏览器侧边栏中的测试浏览器视图- 自定义视图容器
树数据提供者
第二步是向您注册的视图提供数据,以便 VS Code 可以在视图中显示数据。为此,您应首先实现TreeDataProvider。我们的树数据提供者将提供节点依赖数据,但您可以有一个提供其他类型数据的数据提供商。
在这个 API 中,有两个必要的方法你需要实现:
getChildren(element?: T): ProviderResult<T[]>- 实现此功能以返回给定的子项元素或根(如果未传递元素)。getTreeItem(element: T): TreeItem | Thenable<TreeItem>- 实现此功能以返回在视图中显示的元素的UI表示 (TreeItem)。
当用户打开树视图时,获取子项方法将被调用而没有一个元素从那里,你的树数据提供者应该返回顶级树项目。在我们的示例中,可折叠状态顶级树项目的数量是树项折叠状态.折叠这意味着顶级树项目将显示为折叠。设置可折叠状态至树项折叠状态.展开将使树项目显示为展开。离开可折叠状态将其默认为树项折叠状态无表示该树项没有子项。获取子项不会被调用用于树项目可折叠状态的树项折叠状态无输入:.
这是一个示例树数据提供者提供节点依赖数据的实现:
导入 * 作为 vscode 从 'vscode';
导入 * 作为 fs 从 'fs';
导入 * 作为 path 从 'path';
export class NodeDependenciesProvider implements vscode.TreeDataProvider<Dependency> {
constructor(private workspaceRoot: string) {}
getTreeItem(element: Dependency): vscode.TreeItem {
return element;
}
getChildren(element?: Dependency): Thenable<Dependency[]> {
if (!this.workspaceRoot) {
vscode.window.showInformationMessage('No dependency in empty workspace');
return Promise.resolve([]);
}
if (element) {
return Promise.resolve(
this.getDepsInPackageJson(
path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json')
)
);
} else {
const packageJsonPath = path.join(this.workspaceRoot, 'package.json');
if (this.pathExists(packageJsonPath)) {
return Promise.resolve(this.getDepsInPackageJson(packageJsonPath));
} else {
vscode.window.showInformationMessage('Workspace has no package.json');
return Promise.resolve([]);
}
}
}
/**
* Given the path to package.json, read all its dependencies and devDependencies.
*/
private getDepsInPackageJson(packageJsonPath: string): Dependency[] {
if (this.pathExists(packageJsonPath)) {
const toDep = (moduleName: string, version: string): Dependency => {
if (this.pathExists(path.join(this.workspaceRoot, 'node_modules', moduleName))) {
return new Dependency(
moduleName,
version,
vscode.TreeItemCollapsibleState.Collapsed
);
} else {
return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.None);
}
};
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const deps = packageJson.dependencies
? Object.keys(packageJson.dependencies).map(dep =>
toDep(dep, packageJson.dependencies[dep])
)
: [];
const devDeps = packageJson.devDependencies
? Object.keys(packageJson.devDependencies).map(dep =>
toDep(dep, packageJson.devDependencies[dep])
)
: [];
return deps.concat(devDeps);
} else {
return [];
}
}
private pathExists(p: string): boolean {
try {
fs.accessSync(p);
} catch (err) {
return false;
}
return true;
}
}
class Dependency extends vscode.TreeItem {
constructor(
public readonly label: string,
private version: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState
) {
super(label, collapsibleState);
this.tooltip = `${this.label}-${this.version}`;
this.description = this.version;
}
```plaintext
iconPath = {
light: path.join(__filename, '..', '..', 'resources', 'light', 'dependency.svg'),
dark: path.join(__filename, '..', '..', 'resources', 'dark', 'dependency.svg')
};
```
注册树数据提供者
第三步是将上述数据提供者注册到你的视图中。
这可以通过以下两种方式实现:
-
vscode.window.registerTreeDataProvider- 通过提供注册视图ID和上述数据提供者来注册树数据提供者。const rootPath = vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined; vscode.window.registerTreeDataProvider( 'nodeDependencies', new NodeDependenciesProvider(rootPath) ); -
vscodeWindows创建树视图- 通过提供注册的视图ID和上述数据提供者来创建树视图。这将提供对TreeView的访问,您可以使用它来执行其他视图操作。使用创建树视图,如果你需要树视图应用程序编程接口。vscode.Windows.创建树视图('nodeDependencies', { treeDataProvider: new 节点依赖项提供者(根路径) });
这是该扩展的运行效果:

更新树视图内容
我们的节点依赖视图很简单,一旦数据显示出来,就不会更新。然而,在视图中有一个刷新按钮,并根据当前的节点依赖视图内容更新,这将是有用的。package.json为了做到这一点,我们可以使用onDidChangeTreeData事件。
onDidChangeTreeData?: Event<T | undefined | null | void>- 如果您的树数据可以更改,并且您希望更新树视图,请实现这一点。
请将以下内容添加到你的节点依赖提供者输入:.
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.事件;
刷新(): 无 {
this._onDidChangeTreeData.fire();
}
现在我们有一个刷新方法,但没有人调用它。我们可以添加一个命令来调用刷新。
在贡献你的一部分package.json,添加:
"commands": [
{
"command": "nodeDependencies.refreshEntry",
"title": "刷新",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
]
并在你的扩展激活中注册该命令:
import * as vscode from 'vscode';
import { NodeDependenciesProvider } from './nodeDependencies';
export function activate(context: vscode.ExtensionContext) {
const rootPath =
vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0
? vscode.workspace.workspaceFolders[0].uri.fsPath
: undefined;
const nodeDependenciesProvider```plaintext
= new NodeDependenciesProvider(rootPath);
vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider);
vscode.commands.registerCommand('nodeDependencies.refreshEntry', () =>
nodeDependenciesProvider.refresh()
);
}
```
现在我们有一个命令可以刷新节点依赖项视图,但在视图上添加一个按钮会更好。我们已经添加了一个图标到命令,这样当我们将其添加到视图时,它将显示该图标。
在贡献你的一部分package.json,添加:
"menus": {
"view/title": [
{
"command": "nodeDependencies.refreshEntry",
"when": "view == nodeDependencies",
"group": "navigation"
},
]
}
激活
重要的是,您的扩展仅在用户需要您的扩展提供的功能时才激活。在这种情况下,您应该考虑仅在用户开始使用视图时激活您的扩展。当您的扩展声明一个视图贡献时,VS Code 会自动为您做到这一点。VS Code 会发出一个激活事件onView:${viewId} (在查看:节点依赖例如,当用户打开视图时。
注意:对于 VS Code 版本早于 1.74.0 的情况,您必须在
package.json在 VS Code 中激活你的扩展在这个视图上:"激活事件": [ "监听页面:节点依赖", ],
查看容器
视图容器包含在活动栏或面板中显示的视图列表,以及内置的视图容器。内置的视图容器示例包括源代码控制和资源管理器。

要贡献一个视图容器,您应先使用contributes.viewsContainers贡献点在package.json输入:.
您必须指定以下必填字段:
身份证- 您正在创建的新视图容器的ID。标题- 显示在视图容器顶部的名称。图标- 一个将在活动栏中显示在视图容器中的图像。
"贡献": {
"视图容器": {
"活动栏": [
{
"id": "包资源管理器",
"标题": "包资源管理器",
"图标": "媒体/依赖.svg"
}
]
}
}
或者,您可以通过将其放置在面板下以贡献此视图面板节点。
"贡献": {
"视图容器": {
"面板": [
{
"id": "包资源管理器",
"标题": "包资源管理器",
"图标": "媒体/依赖.svg"
}
]
}
}
贡献视图到视图容器
一旦你创建了一个视图容器,你就可以使用contributes.views 贡献点在package.json输入:.
"contributes": {
"views": {
"package-explorer": [
{
"id": "nodeDependencies",
"name": "节点依赖关系",
"icon": "media/dep.svg",
"contextualTitle": "包管理器"
}
]
}
}
视图也可以有一个可选的能见度属性可以设置为可见,折叠,或隐藏此属性仅在首次以此视图打开工作区时被 VS Code 尊重。之后,可见性将设置为用户所选择的任何内容。如果你有一个包含许多视图的视图容器,或者你的视图对你的扩展的每个用户来说都不实用,请考虑将视图设置为折叠或隐藏. A隐藏视图将出现在视图容器的“视图”菜单中:

查看操作
操作可以在您的单独树项目、树项目上下文菜单以及视图标题顶部的视图中作为内联图标显示。操作是您通过向您的贡献来设置的命令。package.json输入:.
要为这三个地方做贡献,您可以在您的 package.json 中使用以下菜单贡献点:
查看/标题- 显示在视图标题中的操作位置。主要或内联操作使用"组": "导航"其余的是次要行动,这些在请输入具体的网页文本内容,以便我进行翻译。菜单。查看/项目/上下文- 显示树项目操作的位置。内联操作使用"组": "内联"其余的是次要行动,这些在请输入具体的网页文本内容,以便我进行翻译。菜单。
您可以使用一个when子句来控制这些操作的可见性。

示例:
"contributes": {
"commands": [
{
"command": "nodeDependencies.refreshEntry",
"title": "Refresh",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "nodeDependencies.addEntry",
"title": "Add"
},
{
"command": "nodeDependencies.editEntry",
"title": "Edit",
"icon": {
"light": "resources/light/edit.svg",
"dark": "resources/dark/edit.svg"
}
},
{
"command": "nodeDependencies.deleteEntry",
"title": "Delete"
}
],
"menus": {
"view/title": [
{
"command": "nodeDependencies.refreshEntry",
"when": "view == nodeDependencies",
"group": "navigation"
},
{
"command": "nodeDependencies.addEntry",
"when": "view == nodeDependencies"
}
],
"view/item/context": [
{
"command": "nodeDependencies.editEntry",
"when": "view == nodeDependencies && viewItem == dependency",
"group": "inline"
},
{
"command": "nodeDependencies.deleteEntry",
"when": "视图 == 节点依赖 && 视图项 == 依赖"
}
]
}
}
默认情况下,操作按字母顺序排列。要指定不同的排序顺序,请添加@然后输入您希望发送到群组的订单。例如,导航@3将导致该操作显示为第三位导航组。
您可以进一步分离项目中的请输入具体的网页文本内容,以便我进行翻译。通过创建不同的组来组织菜单。这些组名是任意的,并且按组名的字母顺序排序。
注意: 如果您想显示特定树项的操作,可以通过定义树项的上下文来实现 树项上下文值并且您可以为键指定上下文值查看商品在当表达式。
示例:
"contributes": {
"menus": {
"view/item/context": [
{
"command": "nodeDependencies.deleteEntry",
"when": "view == nodeDependencies && viewItem == dependency"
}
]
}
}
欢迎内容
如果您的视图可以为空,或者如果您想向另一个扩展的空视图添加欢迎内容,您可以贡献欢迎浏览内容。一个空视图是一个没有内容的视图。TreeView.消息和一棵空树。
"contributes": {
"viewsWelcome": [
{
"view": "nodeDependencies",
"contents": "没有找到节点依赖 [了解更多](https://www.npmjs.com/).\n[添加依赖](command:nodeDependencies.addEntry)"
}
]
}

欢迎内容支持链接。通常情况下,单独一行的链接是一个按钮。每个欢迎内容也可以包含一个当 条款。要查看更多示例,请参阅 内置的 Git 扩展。
树数据提供者
扩展 writers 应该通过编程方式注册一个TreeDataProvider来填充视图中的数据。
vscode.Windows.注册树数据提供者('nodeDependencies', 新的 DepNodeProvider());
见 nodeDependencies.ts 在 树视图示例实施。
树视图
如果您希望以编程方式对视图执行一些用户界面操作,您可以使用Windows.创建树视图而不是Windows注册树数据提供者这将提供视图访问,您可以使用它来执行视图操作。
vscode.Windows.创建树视图('ftp资源管理器', {
树数据提供者: new Ftp树数据提供者()
});
见 ftpExplorer.ts 在 树视图示例实施。