트리 뷰 API
트리 뷰 API를 사용하면 확장에서 Visual Studio Code의 사이드바에 콘텐츠를 표시할 수 있습니다. 이 콘텐츠는 트리 구조로 되어 있으며 VS Code의 내장 뷰 스타일에 따릅니다.
예를 들어, 내장된 참조 검색 뷰 확장은 참조 검색 결과를 별도의 뷰로 표시합니다.

모든 참조 찾기 결과는 참조 뷰 컨테이너에 있는 참조: 결과 트리 뷰에 표시됩니다.
이 가이드에서는 Visual Studio Code에 트리 뷰와 뷰 컨테이너를 기여하는 확장 프로그램을 작성하는 방법을 배웁니다.
트리 뷰 API 기본 사항
트리 뷰 API를 설명하기 위해 Node Dependencies라는 샘플 확장을 빌드할 것입니다. 이 확장은 트리 뷰를 사용하여 현재 폴더의 모든 Node.js 종속성을 표시합니다. 트리 뷰를 추가하는 단계는 package.json에 트리 뷰를 기여하고, TreeDataProvider를 만들고, TreeDataProvider를 등록하는 것입니다. 이 샘플 확장의 전체 소스 코드는 vscode-extension-samples GitHub 리포지토리의 tree-view-sample에서 찾을 수 있습니다.
package.json 기여
먼저 package.json의 contributes.views 기여 지점을 사용하여 뷰를 기여하고 있음을 VS Code에 알려야 합니다.
다음은 확장의 첫 번째 버전에 대한 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"
}
}
참고: 확장이 1.74 이전 버전의 VS Code를 대상으로 하는 경우
activationEvents에onView:nodeDependencies를 명시적으로 나열해야 합니다.
뷰의 식별자와 이름을 지정해야 하며 다음 위치에 기여할 수 있습니다.
explorer: 사이드바의 탐색기 뷰debug: 사이드바의 실행 및 디버그 뷰scm: 사이드바의 소스 제어 뷰test: 사이드바의 테스트 탐색기 뷰- 사용자 지정 뷰 컨테이너
트리 데이터 공급자
두 번째 단계는 등록한 뷰에 데이터를 제공하여 VS Code가 뷰에 데이터를 표시할 수 있도록 하는 것입니다. 이를 위해 먼저 TreeDataProvider를 구현해야 합니다. 저희 TreeDataProvider는 노드 종속성 데이터를 제공하지만, 다른 유형의 데이터를 제공하는 데이터 공급자를 가질 수 있습니다.
이 API에는 구현해야 하는 두 가지 필수 메서드가 있습니다.
getChildren(element?: T): ProviderResult<T[]>- 주어진element또는 루트(element가 전달되지 않은 경우)의 자식을 반환하려면 이를 구현합니다.getTreeItem(element: T): TreeItem | Thenable<TreeItem>- 뷰에 표시되는 요소의 UI 표현(TreeItem)을 반환하려면 이를 구현합니다.
사용자가 트리 뷰를 열면 getChildren 메서드가 element 없이 호출됩니다. 거기서부터 TreeDataProvider는 최상위 트리 항목을 반환해야 합니다. 저희 예시에서는 최상위 트리 항목의 collapsibleState가 TreeItemCollapsibleState.Collapsed로 설정되어 있어 최상위 트리 항목이 축소된 상태로 표시됩니다. collapsibleState를 TreeItemCollapsibleState.Expanded로 설정하면 트리 항목이 확장된 상태로 표시됩니다. collapsibleState를 기본값인 TreeItemCollapsibleState.None으로 두면 트리 항목에 자식이 없음을 나타냅니다. TreeItemCollapsibleState.None의 collapsibleState를 가진 트리 항목에 대해서는 getChildren이 호출되지 않습니다.
다음은 노드 종속성 데이터를 제공하는 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 {
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;
}
iconPath = {
light: path.join(__filename, '..', '..', 'resources', 'light', 'dependency.svg'),
dark: path.join(__filename, '..', '..', 'resources', 'dark', 'dependency.svg')
};
}
TreeDataProvider 등록
세 번째 단계는 위에서 설명한 데이터 공급자를 뷰에 등록하는 것입니다.
이 작업은 다음 두 가지 방법으로 수행할 수 있습니다.
-
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) ); -
vscode.window.createTreeView- 등록된 뷰 ID와 위에서 설명한 데이터 공급자를 제공하여 트리 뷰를 생성합니다. 이를 통해 TreeView에 액세스할 수 있으며, 이를 사용하여 다른 뷰 작업을 수행할 수 있습니다.TreeViewAPI가 필요한 경우createTreeView를 사용합니다.vscode.window.createTreeView('nodeDependencies', { treeDataProvider: new NodeDependenciesProvider(rootPath) });
확장이 작동하는 모습은 다음과 같습니다.

트리 뷰 콘텐츠 업데이트
저희 노드 종속성 뷰는 간단하며 데이터가 표시되면 업데이트되지 않습니다. 하지만 뷰에 새로 고침 버튼이 있고 package.json의 현재 내용으로 노드 종속성 뷰를 업데이트하면 유용할 것입니다. 이를 위해 onDidChangeTreeData 이벤트를 사용할 수 있습니다.
onDidChangeTreeData?: Event<T | undefined | null | void>- 트리 데이터가 변경될 수 있고 트리 뷰를 업데이트하려는 경우 이를 구현합니다.
NodeDependenciesProvider에 다음을 추가합니다.
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();
}
이제 새로 고침 메서드가 있지만 아무도 호출하지 않습니다. 새로 고침을 호출하는 명령을 추가할 수 있습니다.
package.json의 contributes 섹션에 다음을 추가합니다.
"commands": [
{
"command": "nodeDependencies.refreshEntry",
"title": "Refresh",
"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 = new NodeDependenciesProvider(rootPath);
vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider);
vscode.commands.registerCommand('nodeDependencies.refreshEntry', () =>
nodeDependenciesProvider.refresh()
);
}
이제 노드 종속성 뷰를 새로 고치는 명령이 있지만 뷰에 버튼이 있으면 더 좋을 것입니다. 이미 명령에 icon을 추가했으므로 뷰에 추가할 때 해당 아이콘과 함께 표시될 것입니다.
package.json의 contributes 섹션에 다음을 추가합니다.
"menus": {
"view/title": [
{
"command": "nodeDependencies.refreshEntry",
"when": "view == nodeDependencies",
"group": "navigation"
},
]
}
활성화
확장 프로그램은 사용자가 확장이 제공하는 기능을 필요로 할 때만 활성화되는 것이 중요합니다. 이 경우 사용자가 뷰를 사용하기 시작할 때만 확장을 활성화하는 것을 고려해야 합니다. VS Code는 확장 프로그램이 뷰 기여를 선언할 때 자동으로 이 작업을 수행합니다. VS Code는 사용자가 뷰를 열 때 onView:${viewId} (위 예시의 경우 onView:nodeDependencies) 활성화 이벤트를 발생시킵니다.
참고: 1.74.0 이전 버전의 VS Code의 경우, VS Code가 확장 프로그램을 이 뷰에서 활성화하도록 하려면
package.json에 이 활성화 이벤트를 명시적으로 등록해야 합니다."activationEvents": [ "onView:nodeDependencies", ],
뷰 컨테이너
뷰 컨테이너는 액티비티 바 또는 패널에 표시되는 뷰 목록을 포함하며, 내장 뷰 컨테이너와 함께 제공됩니다. 내장 뷰 컨테이너의 예로는 소스 제어 및 탐색기가 있습니다.

뷰 컨테이너를 기여하려면 먼저 package.json의 contributes.viewsContainers 기여 지점을 사용하여 등록해야 합니다.
다음 필수 필드를 지정해야 합니다.
id- 새로 만드는 뷰 컨테이너의 ID입니다.title- 뷰 컨테이너 상단에 표시될 이름입니다.icon- 액티비티 바에 있을 때 뷰 컨테이너에 표시될 이미지입니다.
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "package-explorer",
"title": "Package Explorer",
"icon": "media/dep.svg"
}
]
}
}
대안으로, panel 노드 아래에 배치하여 이 뷰를 패널에 기여할 수 있습니다.
"contributes": {
"viewsContainers": {
"panel": [
{
"id": "package-explorer",
"title": "Package Explorer",
"icon": "media/dep.svg"
}
]
}
}
뷰 컨테이너에 뷰 기여
뷰 컨테이너를 생성한 후에는 package.json의 contributes.views 기여 지점을 사용할 수 있습니다.
"contributes": {
"views": {
"package-explorer": [
{
"id": "nodeDependencies",
"name": "Node Dependencies",
"icon": "media/dep.svg",
"contextualTitle": "Package Explorer"
}
]
}
}
뷰에는 선택적으로 visibility 속성을 가질 수 있으며, 이를 visible, collapsed 또는 hidden으로 설정할 수 있습니다. 이 속성은 워크스페이스가 이 뷰를 처음 열 때 VS Code에서만 존중됩니다. 그 후에는 사용자가 선택한 대로 가시성이 설정됩니다. 뷰 컨테이너에 뷰가 많거나 확장 프로그램의 모든 사용자에게 뷰가 유용하지 않을 경우, collapsed 또는 hidden으로 설정하는 것을 고려하십시오. hidden 뷰는 뷰 컨테이너의 "뷰" 메뉴에 나타납니다.

뷰 작업
작업은 개별 트리 항목의 인라인 아이콘, 트리 항목 컨텍스트 메뉴, 뷰 상단의 뷰 제목 등에서 사용할 수 있습니다. 작업은 package.json에 기여를 추가하여 이러한 위치에 표시되도록 설정한 명령입니다.
이 세 곳에 기여하려면 package.json에서 다음 메뉴 기여 지점을 사용할 수 있습니다.
view/title- 뷰 제목에 작업을 표시하는 위치입니다. 기본 또는 인라인 작업은"group": "navigation"을 사용하고 나머지는...메뉴에 있는 보조 작업입니다.view/item/context- 트리 항목에 대한 작업을 표시하는 위치입니다. 인라인 작업은"group": "inline"을 사용하고 나머지는...메뉴에 있는 보조 작업입니다.
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": "view == nodeDependencies && viewItem == dependency"
}
]
}
}
기본적으로 작업은 알파벳순으로 정렬됩니다. 다른 정렬 순서를 지정하려면 그룹 뒤에 @와 원하는 순서를 추가합니다. 예를 들어, navigation@3은 작업을 navigation 그룹에서 세 번째로 표시되도록 합니다.
... 메뉴의 항목을 다른 그룹을 만들어 더 분리할 수 있습니다. 이러한 그룹 이름은 임의이며 그룹 이름별로 알파벳순으로 정렬됩니다.
참고: 특정 트리 항목에 대한 작업을 표시하려면 TreeItem.contextValue를 사용하여 트리 항목의 컨텍스트를 정의하면 되며, when 표현식의 키 viewItem에 대한 컨텍스트 값을 지정할 수 있습니다.
예시
"contributes": {
"menus": {
"view/item/context": [
{
"command": "nodeDependencies.deleteEntry",
"when": "view == nodeDependencies && viewItem == dependency"
}
]
}
}
환영 콘텐츠
뷰가 비어 있거나 다른 확장 프로그램의 빈 뷰에 환영 콘텐츠를 추가하려는 경우 viewsWelcome 콘텐츠를 기여할 수 있습니다. 빈 뷰는 TreeView.message가 없고 트리가 비어 있는 뷰입니다.
"contributes": {
"viewsWelcome": [
{
"view": "nodeDependencies",
"contents": "No node dependencies found [learn more](https://www.npmjs.com/).\n[Add Dependency](command:nodeDependencies.addEntry)"
}
]
}

환영 콘텐츠에서는 링크가 지원됩니다. 관례적으로 줄 자체에 있는 링크는 버튼입니다. 각 환영 콘텐츠에는 when 절을 포함할 수도 있습니다. 더 많은 예시는 내장 Git 확장을 참조하십시오.
TreeDataProvider
확장 작성자는 뷰의 데이터를 채우기 위해 프로그래밍 방식으로 TreeDataProvider를 등록해야 합니다.
vscode.window.registerTreeDataProvider('nodeDependencies', new DepNodeProvider());
구현은 tree-view-sample의 nodeDependencies.ts를 참조하십시오.
TreeView
프로그래밍 방식으로 뷰에서 UI 작업을 수행하려면 window.registerTreeDataProvider 대신 window.createTreeView를 사용할 수 있습니다. 이를 통해 뷰에 액세스할 수 있으며, 이를 사용하여 뷰 작업을 수행할 수 있습니다.
vscode.window.createTreeView('ftpExplorer', {
treeDataProvider: new FtpTreeDataProvider()
});
구현은 tree-view-sample의 ftpExplorer.ts를 참조하십시오.