From 94730b8b48fcd17eb3c5253c694499c47050ec9e Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 16 Nov 2016 15:49:54 -0800 Subject: [PATCH 1/2] Tree explorer usage doc --- tree-explorer-sample/USAGE.md | 169 ++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 tree-explorer-sample/USAGE.md diff --git a/tree-explorer-sample/USAGE.md b/tree-explorer-sample/USAGE.md new file mode 100644 index 00000000..2f12dc38 --- /dev/null +++ b/tree-explorer-sample/USAGE.md @@ -0,0 +1,169 @@ +# Example - Tree Explorer + +**Note**: + +**This is a proposed API. It's experimental in nature and subject to change or removal without notice.** + +**Currently, this API is only available when developing extensions using [Insiders](https://code.visualstudio.com/insiders) version of VS Code. Packaged extensions using this API won't run. Please refrain from publishing any extensions using this API.** + +The Tree Explorer API allows you to create customized Tree Explorers, similar to the File Explorer that comes with VS Code. A Tree Explorer extension adds an icon to the Activity Bar and creates a custom View when the icon is clicked. + +From a high level, extension defines an `explorer` contribution, where it specifies a View label, an icon and a provider id. During the activation, extension calls `vscode.window.registerTreeExplorerNodeProvider` with the specified provider id. VS Code then asks the registered provider to provide necessary information for rendering the Tree Explorer View. + +The remaining document assumes that you are familiar with normal [extension development](/docs/extensions/overview.md) for VS Code. + +## Example - Dependency Tree Explorer + +We'll show you an example of a Dependency Tree Explorer: + +![tree-explorer](tree-explorer.png) + +To get it running, do the following: + +- Clone this repository +- Go to `tree-explorer-sample` +- Install dependencies with `npm install` +- Run `Launch Extension` in the Debug View. + +By default the Explorer wouldn't be displayed on Activity Bar. Find "View: Toggle Custom Explorer" in Command Palette and choose your Explorer to enable it. + +### The Structure of Tree Explorer extension + +First, let's take a look at `package.json`. There are a few interesting bits: + +```json +{ + "enableProposedApi": true, + "contributes": { + "explorer": { + "treeLabel": "Dependencies", + "icon": "media/dep.svg", + "treeExplorerNodeProviderId": "depTree" + } + } +} +``` + +As this API is still in preview, `enableProposedApi` tells VS Code we acknowledge its experimental nature and would like to play with it. `contributes.explorer` specifies a label that will appear at the top of the View, the path to a svg icon that will appear at the Activity Bar, and a provider id which we'll use in `vscode.window.registerTreeExplorerNodeProvider`. + +`typings/vscode.proposed.d.ts` defines the preview API, and is pulled from VS Code's GitHub repository: https://github.com/Microsoft/vscode/blob/master/src/vs/vscode.proposed.d.ts + +Let's jump to `src/extension.ts` and take a look at the code. + +### The Implementation of a TreeExplorerNodeProvider + +A `TreeExplorerNodeProvider` implements two required methods and three optional methods: + +```ts +interface TreeExplorerNodeProvider { + provideRootNode(): T | Thenable; + resolveChildren(node: T): T[] | Thenable; + + getLabel?(node: T): string; + getHasChildren?(node: T): boolean; + getClickCommand?(node: T): string; +} +``` + +The type `T` is the class of each node. `provideRootNode` provides the invisible node that represents the root of the tree. `resolveChildren` will be invoked when an expandable node is clicked upon, or invoked with the root node as argument when the Activity Bar icon is clicked. + +The three optional methods are used for extracting relevant information used for rendering the tree. `getLabel` returns the label for each node, `getHasChildren` determines if each node is expandable, and `getClickCommand` returns a string that represents the command that should be executed when a node is clicked upon. When these functions are not provided, VS Code will use these default values: + +- `node.toString()` as `label` +- `true` as `hasChildren` +- `null` as `clickCommand` + +In our dependency example, the node is defined as an union type: + +```ts +type DepNode = Root // Root node + | Node // A dependency installed to `node_modules` + | Leaf // A dependency not present in `node_modules` + ; +``` + +A `Node` is a dependency that still has `dependencies` or `devDependencies` in its `package.json`. A `Leaf` is a dependency that depends on no other packages. + +In our `DepNodeProvider`: + +```ts +provideRootNode(): DepNode { + return new Root(); +} + +resolveChildren(node: DepNode): Thenable { + return new Promise((resolve) => { + switch(node.kind) { + case 'root': + const packageJsonPath = path.join(this.workspaceRoot, 'package.json'); + if (this.fileExists(packageJsonPath)) { + resolve(this.getDepsInPackageJson(packageJsonPath)); + } else { + vscode.window.showInformationMessage('Workspace has no package.json'); + resolve([]); + } + break; + case 'node': + resolve(this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', node.moduleName, 'package.json'))); + break; + case 'leaf': + resolve([]); + } + }); +} +``` + +`provideRootNode` simply provides a `Root` node. `resolveChildren` behaves differently accordingly to the type of the node to resolves children for: + +- `Root`: read `package.json` in workspace root, and return its `dependencies` and `devDependencies`. +- `Node`: read `package.json` in `${workspaceRoot}/node_modules/${node.moduleName}`, and return its `dependencies` and `devDependencies`. +- `Leaf`: `Leaf` isn't expandable and this case shouldn't happen. We return an empty array. + +We also implement `getLabel`, `getHasChildren`, `getClickCommand` to: + +- Let each `Node` and `Leaf` display its `moduleName` as the label on the tree. + ```ts + getLabel(node: DepNode): string { + return node.kind === 'root' ? '' : node.moduleName; + } + ``` +- Make `Leaf` unexpandable. + ```ts + getHasChildren(node: DepNode): boolean { + return node.kind !== 'leaf'; + } + ``` +- Invoke `extension.openPackageOnNpm` when a `Leaf` is clicked. + ```ts + getClickCommand(node: DepNode): string { + return node.kind === 'leaf' ? 'extension.openPackageOnNpm' : null; + } + ``` + +## Implementation Considerations + +#### Icon + +To ensure UI consistency with other Views, you should provide a 32x32 SVG icon with transparent background as `explorer.icon` in package.json. + +#### Long running `resolveChildren` + +If `resolveChildren` could take very long, you should let it return a promise, and VS Code will show a progress bar as the promise gets resolved. + +#### Error handling + +We suggest that you handle errors promptly and notify the user using `vscode.window.showInformationMessage` if needed. In our Dependency example, we check for package.json in the root of the workspace. If it doesn't exist, we'll show an information message `Workspace has no package.json`. + +## Common Questions + +**Q: Will it be possible to access the DOM and add an arbitrary UI using this API?** + +**A:** No. + +**Q: Can I add an Outline View/Symbol Tree View using this API?** + +**A:** We plan to include an API where language services could register a provider for Outline View items, similar to the existing [`DocumentSymbolProvider`](https://code.visualstudio.com/docs/extensionAPI/vscode-api#DocumentSymbolProvider). Please refrain from using this API to make Outline Views. + +**Q: Can I have functionality X?** + +**A:** We realize this API in its current form is limiting, and we are planning to evolve it. You can track our progress in this [GitHub issue](https://github.com/Microsoft/vscode/issues/15485). If you have ideas about how we could improve this API, please feel free to chime in. \ No newline at end of file From 0cec98c418018e97da819f8dc77e285100e096d1 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 16 Nov 2016 15:57:54 -0800 Subject: [PATCH 2/2] Update links and small fixes --- tree-explorer-sample/USAGE.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tree-explorer-sample/USAGE.md b/tree-explorer-sample/USAGE.md index 2f12dc38..88ddebdc 100644 --- a/tree-explorer-sample/USAGE.md +++ b/tree-explorer-sample/USAGE.md @@ -10,7 +10,7 @@ The Tree Explorer API allows you to create customized Tree Explorers, similar to From a high level, extension defines an `explorer` contribution, where it specifies a View label, an icon and a provider id. During the activation, extension calls `vscode.window.registerTreeExplorerNodeProvider` with the specified provider id. VS Code then asks the registered provider to provide necessary information for rendering the Tree Explorer View. -The remaining document assumes that you are familiar with normal [extension development](/docs/extensions/overview.md) for VS Code. +The remaining document assumes that you are familiar with normal [extension development](https://code.visualstudio.com/docs/extensions/overview) for VS Code. ## Example - Dependency Tree Explorer @@ -21,11 +21,11 @@ We'll show you an example of a Dependency Tree Explorer: To get it running, do the following: - Clone this repository -- Go to `tree-explorer-sample` +- `cd tree-explorer-sample` - Install dependencies with `npm install` -- Run `Launch Extension` in the Debug View. +- Run `Launch Extension` in the Debug View -By default the Explorer wouldn't be displayed on Activity Bar. Find "View: Toggle Custom Explorer" in Command Palette and choose your Explorer to enable it. +By default the Explorer wouldn't be displayed on Activity Bar. Find `View: Toggle Custom Explorer` in Command Palette and choose your Explorer to enable it. ### The Structure of Tree Explorer extension @@ -44,7 +44,11 @@ First, let's take a look at `package.json`. There are a few interesting bits: } ``` -As this API is still in preview, `enableProposedApi` tells VS Code we acknowledge its experimental nature and would like to play with it. `contributes.explorer` specifies a label that will appear at the top of the View, the path to a svg icon that will appear at the Activity Bar, and a provider id which we'll use in `vscode.window.registerTreeExplorerNodeProvider`. +- `enableProposedApi` tells VS Code we acknowledge its experimental nature and would like to play with it. +- `contributes.explorer` specifies + - A label that will appear at the top of the View + - The path to a svg icon that will appear at the Activity Bar + - A provider id which we'll use in `vscode.window.registerTreeExplorerNodeProvider` `typings/vscode.proposed.d.ts` defines the preview API, and is pulled from VS Code's GitHub repository: https://github.com/Microsoft/vscode/blob/master/src/vs/vscode.proposed.d.ts @@ -115,25 +119,30 @@ resolveChildren(node: DepNode): Thenable { `provideRootNode` simply provides a `Root` node. `resolveChildren` behaves differently accordingly to the type of the node to resolves children for: -- `Root`: read `package.json` in workspace root, and return its `dependencies` and `devDependencies`. +- `Root`: read `package.json` in `${workspaceRoot}`, and return its `dependencies` and `devDependencies`. - `Node`: read `package.json` in `${workspaceRoot}/node_modules/${node.moduleName}`, and return its `dependencies` and `devDependencies`. - `Leaf`: `Leaf` isn't expandable and this case shouldn't happen. We return an empty array. We also implement `getLabel`, `getHasChildren`, `getClickCommand` to: - Let each `Node` and `Leaf` display its `moduleName` as the label on the tree. + ```ts getLabel(node: DepNode): string { return node.kind === 'root' ? '' : node.moduleName; } ``` + - Make `Leaf` unexpandable. + ```ts getHasChildren(node: DepNode): boolean { return node.kind !== 'leaf'; } ``` + - Invoke `extension.openPackageOnNpm` when a `Leaf` is clicked. + ```ts getClickCommand(node: DepNode): string { return node.kind === 'leaf' ? 'extension.openPackageOnNpm' : null;