The Tree View API allows plug-ins to render content in sidebar, which is displayed in the shape of a Tree
Tree View API basics
We introduced the Tree View API with an example that uses a Tree View to show all node.js dependencies in the current folder. You can see the complete code for this example at tree-view-sample
Configuration package. Json
First you contribute to VS Code to know that you “contribute” a view by contributing. Here is a preliminary configuration of package.json:
{
"name": "helloworld"."displayName": "HelloWorld"."description": ""."version": "0.0.1"."engines": {
"vscode": "^ 1.56.0"
},
"categories": [
"Other"]."activationEvents": ["onView:nodeDependencies"]."main": "./extension.js"."contributes": {
"views": {
"explorer": [{
"id": "nodeDependencies"."name": "Node Dependencies"}}},"scripts": {
"lint": "eslint ."."pretest": "npm run lint"."test": "node ./test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^ 1.56.0"."@types/glob": "^ 7.1.3." "."@types/mocha": "^ 8.0.4"."@types/node": "14.x"."eslint": "^ 7.19.0"."glob": "^ 7.1.6." "."mocha": "^ 8.2.1." "."typescript": "^ 4.1.3." "."vscode-test": "^ 1.5.0." "}}Copy the code
It is important to deactivate the plug-in only when the user needs it, as in the example in this article, we can deactivate the plug-in while the user is using the plug-in view. VS Code provides an onView:${viewId} event to tell the application which view the user is currently opening. In package.json, we can register an activation event “activationEvents”: [“onView:nodeDependencies”].
Generate the data
The second step is to use the TreeDataProvider to generate node.js dependent data for the tree view. Two methods need to be implemented:
getChildren(element? : T): ProviderResult<T[]>
: Returns the child node of the specified node (root if not specified)getTreeItem(element: T): TreeItem | Thenable<TreeItem>
: Returns the UI node used for display in the view
Whenever the user opens the tree view, getChildren is automatically called (with no arguments) and you can return the first level of the tree view from there. In the example, we use TreeItemCollapsibleState. Collapsed (folding), TreeItemCollapsibleState. Expanded (expand), TreeItemCollapsibleState. None (no child node, Does not trigger the getChildren method) controls the collapse state of the node. Here is an example implementation of the TreeDataProvider:
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
export class NodeDependenciesProvider implements vscode.TreeDataProvider<Dependency> {
constructor(private workspaceRoot: string) { }
getTreeItem(element: Dependency): vscode.TreeItem {
returnelement; } 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 */
private getDepsInPackageJson(packageJsonPath: string): Dependency[] {
if (this.pathExists(packageJsonPath)) {
const toDep = (moduleName: string, version: string): Dependency= > {
const depPackageJsonPath = path.join(this.workspaceRoot, 'node_modules', moduleName, 'package.json');
let collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
if (this.pathExists(depPackageJsonPath)) {
const depPackageJson = JSON.parse(fs.readFileSync(depPackageJsonPath, 'utf-8'));
// If the dependencies package is already installed (node_modules has content) and the package itself has dependencies or devDependencies, set it to expandable
if((! depPackageJson.dependencies ||Object.keys(depPackageJson.dependencies).length === 0) && (! depPackageJson.devDependencies ||Object.keys(depPackageJson.devDependencies).length === 0)) { collapsibleState = vscode.TreeItemCollapsibleState.None; }}return new Dependency(moduleName, version, collapsibleState);
};
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 = `The ${this.label}-The ${this.version}`;
this.description = this.version;
}
iconPath = {
light: path.join(__filename, '.. '.'.. '.'resources'.'light'.'dependency.svg'),
dark: path.join(__filename, '.. '.'.. '.'resources'.'dark'.'dependency.svg')}; }Copy the code
Registered TreeDataProvider
The third step is to feed the generated dependency data to the view, which can be done in two ways:
-
Vscode. Window. RegisterTreeDataProvider: tree register data provider, you need to provide the view ID and data provider object
vscode.window.registerTreeDataProvider( 'nodeDependencies'.new NodeDependenciesProvider(vscode.workspace.rootPath) ); Copy the code
-
Vscode. Window. CreateTreeView: through the view ID and data provider to create visual tree view, it’s ability to provide access to the tree view, if you need to use the TreeView apis, you can use createTreeView way
vscode.window.createTreeView('nodeDependencies', { treeDataProvider: new NodeDependenciesProvider(vscode.workspace.rootPath) }); Copy the code
Now that a plug-in with basic target functionality is complete, you can see the following in action:
See tree-view-test v1 for a complete example of the above code
Update view content
On the command line
Currently completed, the plugin has only the most basic functions, and the dependency data cannot be updated once displayed. It would be handy to have a refresh button in the view. To do this, we need to use the onDidChangeTreeData event:
onDidChangeTreeData? : Event<T | undefined | null | void>
: executed when the dependency data changes and you want to update the tree view
Add the following code to the provider:
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;
refresh(): void {
this._onDidChangeTreeData.fire();
}
Copy the code
Now that we have the update function, but have not called it, we can define an update command in package.json:
"commands": [{"command": "nodeDependencies.refreshEntry"."title": "Refresh Dependence"."icon": {
"light": "resources/light/refresh.svg"."dark": "resources/dark/refresh.svg"}}]Copy the code
Then register the command:
vscode.commands.registerCommand('nodeDependencies.refreshEntry'.() = >
nodeDependenciesProvider.refresh()
);
Copy the code
At this point we’ll see when we executeRefresh Dependence
After the command,Node.js
The dependent tree view is updated:
In button mode
On the basis of the above, it would be more intuitive and friendly to add a button to the view. In package.json we add:
"menus": {
"view/title": [{"command": "nodeDependencies.refreshEntry"."when": "view == nodeDependencies"."group": "navigation"]}},Copy the code
When we hover the mouse over the view, we see the Refresh button. It works the same way as the Refresh Dependence command:
The group attribute is used to sort and categorize menu items. The group with the value of navigation is used to place the top. If not set, the refresh button will be hidden in the “… In, the effect is as follows:
See tree-view-test v2 for a complete example of the above code
Add to View Container
Creating a View container
View the container contains a series of features in view of the Activity Bar or the Panel, if you want to own a view custom plug-in container, we can use contributes. ViewsContainers in package. Json in registration:
"contributes": {
"viewsContainers": {
"activitybar": [{
"id": "package-explorer"."title": "Package Explorer"."icon": "media/dep.svg"}}}]Copy the code
Or you can do the configuration under the Panel field
"contributes": {
"viewsContainers": {
"panel": [{
"id": "package-explorer"."title": "Package Explorer"."icon": "media/dep.svg"}}}]Copy the code
Bind the view to the view container
This can be done in package.json using contributes. Views
"contributes": {
"views": {
"package-explorer": [{
"id": "nodeDependencies"."name": "Node Dependencies"."icon": "media/dep.svg"."contextualTitle": "Package Explorer"}}}]Copy the code
Note that a view can be set to the visibility property, which has three values: Visible, collapsed, and hidden. These values only take effect when the workbench is opened for the first time, and then depend on user control. If you have many views in your view container, you can use this property to make your interface cleaner
Now we can see the view container and tree view on the left:
See tree-view-test V3 for a complete example of the above code
View behavior interpretation
The view’s behavior is attached to the view’s inline ICONS, which can be found on each node in the tree view, as well as on the title bar at the top of the tree view, which we can configure in package.json:
view/title
: in the view title bar, available"group": "navigation"
To ensure its priorityview/item/context
: is on the tree node"group": "inline"
Make it appear inline
All of the above can be controlled by the when clause
If we wanted to achieve the above effect, we could do it with the following code:
{
"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": "view == nodeDependencies && viewItem == dependency"}]}}}Copy the code
We can use treeItem. contextValue data in the WHEN field to control the display of the corresponding behavior
See tree-view-test V4 for a complete example of the above code
View welcome content
We can add a welcome content to display when the view content is initialized or empty:
"contributes": {
"viewsWelcome": [{
"view": "nodeDependencies"."contents": "Found no reliance on the content, [more] (https://www.npmjs.com/). \ n [add dependent on] (command: nodeDependencies addEntry)"
}]
Copy the code
Contributes. ViewsWelcome. Contents support link, if the link list on a line, will be rendered as buttons. Each viewsWelcome supports the when clause
See tree-view-test V5 for a complete example of the above code
Related articles
-
VS Code Plug-in Development Tutorial (1) Overview
-
VS Code plug-in Development Tutorial (2
-
VS Code Plug-in Development Tutorial (3
-
VS Code Plug-in Development Tutorial (4
-
VS Code plug-in development tutorial (5) Using Command
-
VS Code Plugin development Tutorial (6) Color Theme overview
-
VS Code plug-in development tutorial (7) Tree View
-
VS Code Plug-in Development Tutorial (8) Webview
-
Build a Custom Editor
-
VS Code Plug-in Development Tutorial (10
-
Language Server Extension Guide Language Server Extension Guide