Skip to content

Commit

Permalink
Contribute explorer views
Browse files Browse the repository at this point in the history
- Adopt proposed API to contribute explorer views
- Adopt extension host and main to contribute explorer views
- Implement contributable views in explorer viewlet
  • Loading branch information
sandy081 committed May 5, 2017
1 parent 8b4a17c commit ccae6f9
Show file tree
Hide file tree
Showing 12 changed files with 620 additions and 157 deletions.
24 changes: 16 additions & 8 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,20 +530,20 @@ declare module 'vscode' {
export namespace window {

/**
* Create a new [TreeView](#TreeView) instance.
* Create a new explorer view.
*
* @param viewId A unique id that identifies the view.
* @param provider A [TreeDataProvider](#TreeDataProvider).
* @return An instance of [TreeView](#TreeView).
* @param id View id.
* @param name View name.
* @param dataProvider A [TreeDataProvider](#TreeDataProvider).
* @return An instance of [View](#View).
*/
export function createTreeView<T>(viewId: string, provider: TreeDataProvider<T>): TreeView<T>;
export function createExplorerView<T>(id: string, name: string, dataProvider: TreeDataProvider<T>): View<T>;
}

/**
* An source control is able to provide [resource states](#SourceControlResourceState)
* to the editor and interact with the editor in several source control related ways.
* A view to interact with nodes
*/
export interface TreeView<T> {
export interface View<T> {

/**
* Refresh the given nodes
Expand Down Expand Up @@ -602,6 +602,14 @@ declare module 'vscode' {
*/
getHasChildren?(node: T): boolean;

/**
* Provider a context key to be set for the node. This can be used to describe actions for each node.
*
* @param node The node from which the provider computes context key.
* @return A context key.
*/
getContextKey?(node: T): string;

/**
* Get the command to execute when `node` is clicked.
*
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostTreeView } from 'vs/workbench/api/node/extHostTreeView';
import { ExtHostExplorerView } from 'vs/workbench/api/node/extHostExplorerView';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen';
import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress';
Expand Down Expand Up @@ -111,7 +111,7 @@ export function createApiFactory(
const extHostDocumentSaveParticipant = col.define(ExtHostContext.ExtHostDocumentSaveParticipant).set<ExtHostDocumentSaveParticipant>(new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace)));
const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set<ExtHostEditors>(new ExtHostEditors(threadService, extHostDocumentsAndEditors));
const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set<ExtHostCommands>(new ExtHostCommands(threadService, extHostHeapService));
const extHostTreeView = col.define(ExtHostContext.ExtHostTreeView).set<ExtHostTreeView>(new ExtHostTreeView(threadService, extHostCommands));
const extHostExplorerView = col.define(ExtHostContext.ExtHostExplorerView).set<ExtHostExplorerView>(new ExtHostExplorerView(threadService, extHostCommands));
const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set<ExtHostConfiguration>(new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), initData.configuration));
const extHostDiagnostics = col.define(ExtHostContext.ExtHostDiagnostics).set<ExtHostDiagnostics>(new ExtHostDiagnostics(threadService));
const languageFeatures = col.define(ExtHostContext.ExtHostLanguageFeatures).set<ExtHostLanguageFeatures>(new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
Expand Down Expand Up @@ -369,8 +369,8 @@ export function createApiFactory(
sampleFunction: proposedApiFunction(extension, () => {
return extHostMessageService.showMessage(Severity.Info, 'Hello Proposed Api!', {}, []);
}),
createTreeView: proposedApiFunction(extension, (providerId: string, provider: vscode.TreeDataProvider<any>): vscode.TreeView<any> => {
return extHostTreeView.createTreeView(providerId, provider);
createExplorerView: proposedApiFunction(extension, (id: string, name: string, provider: vscode.TreeDataProvider<any>): vscode.View<any> => {
return extHostExplorerView.createExplorerView(id, name, provider);
})
};

Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/api/node/extHost.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { MainThreadDiagnostics } from './mainThreadDiagnostics';
import { MainThreadDocuments } from './mainThreadDocuments';
import { MainThreadEditors } from './mainThreadEditors';
import { MainThreadErrors } from './mainThreadErrors';
import { MainThreadTreeView } from './mainThreadTreeView';
import { MainThreadExplorerView } from './mainThreadExplorerView';
import { MainThreadLanguageFeatures } from './mainThreadLanguageFeatures';
import { MainThreadLanguages } from './mainThreadLanguages';
import { MainThreadMessageService } from './mainThreadMessageService';
Expand Down Expand Up @@ -74,7 +74,7 @@ export class ExtHostContribution implements IWorkbenchContribution {
col.define(MainContext.MainThreadDocuments).set(this.instantiationService.createInstance(MainThreadDocuments, documentsAndEditors));
col.define(MainContext.MainThreadEditors).set(this.instantiationService.createInstance(MainThreadEditors, documentsAndEditors));
col.define(MainContext.MainThreadErrors).set(create(MainThreadErrors));
col.define(MainContext.MainThreadExplorers).set(create(MainThreadTreeView));
col.define(MainContext.MainThreadExplorerViews).set(create(MainThreadExplorerView));
col.define(MainContext.MainThreadLanguageFeatures).set(create(MainThreadLanguageFeatures));
col.define(MainContext.MainThreadLanguages).set(create(MainThreadLanguages));
col.define(MainContext.MainThreadMessageService).set(create(MainThreadMessageService));
Expand Down
33 changes: 15 additions & 18 deletions src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quick
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IApplyEditsOptions, IUndoStopOptions, TextEditorRevealType, ITextEditorConfigurationUpdate, IResolvedTextEditorConfiguration, ISelectionChangeEvent } from './mainThreadEditor';

import { InternalTreeNodeContent } from 'vs/workbench/parts/explorers/common/treeExplorerViewModel';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorModel';
import { IPosition } from 'vs/editor/common/core/position';
Expand Down Expand Up @@ -155,14 +154,17 @@ export abstract class MainThreadEditorsShape {
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]> { throw ni(); }
}

export abstract class MainThreadTreeViewShape {
$registerTreeDataProvider(providerId: string): void { throw ni(); }
$refresh(providerId: string, node: InternalTreeNodeContent): void { throw ni(); }
export interface ITreeNode {
id: string;
label: string;
hasChildren: boolean;
clickCommand: string;
contextKey: string;
}

export abstract class MainThreadTreeShape {
$registerTreeExplorerNodeProvider(providerId: string, node: InternalTreeNodeContent): void { throw ni(); }
$refresh(providerId: string, node: InternalTreeNodeContent): void { throw ni(); }
export abstract class MainThreadExplorerViewShape {
$registerView(id: string, name: string): void { throw ni(); }
$refresh(viewId: string, node: ITreeNode): void { throw ni(); }
}

export abstract class MainThreadErrorsShape {
Expand Down Expand Up @@ -366,15 +368,10 @@ export abstract class ExtHostDocumentsAndEditorsShape {
}


export abstract class ExtHostTreeViewShape {
$provideRootNode(providerId: string): TPromise<InternalTreeNodeContent> { throw ni(); };
$resolveChildren(providerId: string, node: InternalTreeNodeContent): TPromise<InternalTreeNodeContent[]> { throw ni(); }
$getInternalCommand(providerId: string, node: InternalTreeNodeContent): TPromise<modes.Command> { throw ni(); }
}

export abstract class ExtHostTreeShape {
$resolveChildren(providerId: string, node: InternalTreeNodeContent): TPromise<InternalTreeNodeContent[]> { throw ni(); }
$getInternalCommand(providerId: string, node: InternalTreeNodeContent): TPromise<modes.Command> { throw ni(); }
export abstract class ExtHostExplorerViewShape {
$provideRootNode(viewId: string): TPromise<ITreeNode> { throw ni(); };
$resolveChildren(viewId: string, node: ITreeNode): TPromise<ITreeNode[]> { throw ni(); }
$getInternalCommand(viewId: string, node: ITreeNode): TPromise<modes.Command> { throw ni(); }
}

export abstract class ExtHostExtensionServiceShape {
Expand Down Expand Up @@ -465,7 +462,7 @@ export const MainContext = {
MainThreadDocuments: createMainId<MainThreadDocumentsShape>('MainThreadDocuments', MainThreadDocumentsShape),
MainThreadEditors: createMainId<MainThreadEditorsShape>('MainThreadEditors', MainThreadEditorsShape),
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors', MainThreadErrorsShape),
MainThreadExplorers: createMainId<MainThreadTreeViewShape>('MainThreadTreeView', MainThreadTreeViewShape),
MainThreadExplorerViews: createMainId<MainThreadExplorerViewShape>('MainThreadExplorerView', MainThreadExplorerViewShape),
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures', MainThreadLanguageFeaturesShape),
MainThreadLanguages: createMainId<MainThreadLanguagesShape>('MainThreadLanguages', MainThreadLanguagesShape),
MainThreadMessageService: createMainId<MainThreadMessageServiceShape>('MainThreadMessageService', MainThreadMessageServiceShape),
Expand All @@ -490,7 +487,7 @@ export const ExtHostContext = {
ExtHostDocuments: createExtId<ExtHostDocumentsShape>('ExtHostDocuments', ExtHostDocumentsShape),
ExtHostDocumentSaveParticipant: createExtId<ExtHostDocumentSaveParticipantShape>('ExtHostDocumentSaveParticipant', ExtHostDocumentSaveParticipantShape),
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors', ExtHostEditorsShape),
ExtHostTreeView: createExtId<ExtHostTreeViewShape>('ExtHostTreeView', ExtHostTreeViewShape),
ExtHostExplorerView: createExtId<ExtHostExplorerViewShape>('ExtHostExplorerView', ExtHostExplorerViewShape),
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService', ExtHostFileSystemEventServiceShape),
ExtHostHeapService: createExtId<ExtHostHeapServiceShape>('ExtHostHeapMonitor', ExtHostHeapServiceShape),
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures', ExtHostLanguageFeaturesShape),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,28 @@
'use strict';

import { localize } from 'vs/nls';
import { TreeView, TreeDataProvider } from 'vscode';
import { View, TreeDataProvider } from 'vscode';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { TPromise } from 'vs/base/common/winjs.base';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { MainContext, ExtHostTreeViewShape, MainThreadTreeViewShape } from './extHost.protocol';
import { InternalTreeNode } from 'vs/workbench/parts/explorers/common/treeExplorerViewModel';
import { MainContext, ExtHostExplorerViewShape, MainThreadExplorerViewShape, ITreeNode } from './extHost.protocol';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { asWinJsPromise } from 'vs/base/common/async';
import * as modes from 'vs/editor/common/modes';

class InternalTreeNodeImpl implements InternalTreeNode {
class TreeNodeImpl implements ITreeNode {

readonly id: string;
label: string;
hasChildren: boolean;
clickCommand: string = null;
contextKey: string;

constructor(readonly providerId: string, node: any, provider: TreeDataProvider<any>) {
this.id = defaultGenerator.nextId();
this.label = provider.getLabel ? provider.getLabel(node) : node.toString();
this.hasChildren = provider.getHasChildren ? provider.getHasChildren(node) : true;
this.contextKey = provider.getContextKey ? provider.getContextKey(node) : null;
if (provider.getClickCommand) {
const command = provider.getClickCommand(node);
if (command) {
Expand All @@ -35,13 +36,13 @@ class InternalTreeNodeImpl implements InternalTreeNode {
}
}

export class ExtHostTreeView extends ExtHostTreeViewShape {
private _proxy: MainThreadTreeViewShape;
export class ExtHostExplorerView extends ExtHostExplorerViewShape {
private _proxy: MainThreadExplorerViewShape;

private _extNodeProviders: { [providerId: string]: TreeDataProvider<any> };
private _extViews: Map<string, TreeView<any>> = new Map<string, TreeView<any>>();
private _extNodeMaps: { [providerId: string]: { [id: string]: InternalTreeNode } };
private _mainNodesMap: Map<string, Map<any, InternalTreeNode>>;
private _extViews: Map<string, View<any>> = new Map<string, View<any>>();
private _extNodeMaps: { [providerId: string]: { [id: string]: ITreeNode } };
private _mainNodesMap: Map<string, Map<any, ITreeNode>>;
private _childrenNodesMap: Map<string, Map<any, any[]>>;

constructor(
Expand All @@ -50,11 +51,11 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
) {
super();

this._proxy = threadService.get(MainContext.MainThreadExplorers);
this._proxy = threadService.get(MainContext.MainThreadExplorerViews);

this._extNodeProviders = Object.create(null);
this._extNodeMaps = Object.create(null);
this._mainNodesMap = new Map<string, Map<any, InternalTreeNode>>();
this._mainNodesMap = new Map<string, Map<any, ITreeNode>>();
this._childrenNodesMap = new Map<string, Map<any, any[]>>();

commands.registerArgumentProcessor({
Expand All @@ -68,39 +69,39 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
});
}

createTreeView<T>(providerId: string, provider: TreeDataProvider<T>): TreeView<T> {
this._proxy.$registerTreeDataProvider(providerId);
this._extNodeProviders[providerId] = provider;
this._mainNodesMap.set(providerId, new Map<any, InternalTreeNode>());
this._childrenNodesMap.set(providerId, new Map<any, any>());
createExplorerView<T>(viewId: string, viewName: string, provider: TreeDataProvider<T>): View<T> {
this._proxy.$registerView(viewId, viewName);
this._extNodeProviders[viewId] = provider;
this._mainNodesMap.set(viewId, new Map<any, ITreeNode>());
this._childrenNodesMap.set(viewId, new Map<any, any>());

const treeView: TreeView<T> = {
const treeView: View<T> = {
refresh: (node: T) => {
const mainThreadNode = this._mainNodesMap.get(providerId).get(node);
this._proxy.$refresh(providerId, mainThreadNode);
const mainThreadNode = this._mainNodesMap.get(viewId).get(node);
this._proxy.$refresh(viewId, mainThreadNode);
},
dispose: () => {
delete this._extNodeProviders[providerId];
delete this._extNodeProviders[providerId];
this._mainNodesMap.delete(providerId);
this._childrenNodesMap.delete(providerId);
this._extViews.delete(providerId);
delete this._extNodeProviders[viewId];
delete this._extNodeProviders[viewId];
this._mainNodesMap.delete(viewId);
this._childrenNodesMap.delete(viewId);
this._extViews.delete(viewId);
}
};
this._extViews.set(providerId, treeView);
this._extViews.set(viewId, treeView);
return treeView;
}

$provideRootNode(providerId: string): TPromise<InternalTreeNode> {
$provideRootNode(providerId: string): TPromise<ITreeNode> {
const provider = this._extNodeProviders[providerId];
if (!provider) {
const errMessage = localize('treeExplorer.notRegistered', 'No TreeExplorerNodeProvider with id \'{0}\' registered.', providerId);
return TPromise.wrapError<InternalTreeNode>(errMessage);
return TPromise.wrapError<ITreeNode>(errMessage);
}

return asWinJsPromise(() => provider.provideRootNode()).then(extRootNode => {
const extNodeMap: { [id: string]: InternalTreeNode } = Object.create(null);
const internalRootNode = new InternalTreeNodeImpl(providerId, extRootNode, provider);
const extNodeMap: { [id: string]: ITreeNode } = Object.create(null);
const internalRootNode = new TreeNodeImpl(providerId, extRootNode, provider);

extNodeMap[internalRootNode.id] = extRootNode;
this._extNodeMaps[providerId] = extNodeMap;
Expand All @@ -110,15 +111,15 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
return internalRootNode;
}, err => {
const errMessage = localize('treeExplorer.failedToProvideRootNode', 'TreeExplorerNodeProvider \'{0}\' failed to provide root node.', providerId);
return TPromise.wrapError<InternalTreeNode>(errMessage);
return TPromise.wrapError<ITreeNode>(errMessage);
});
}

$resolveChildren(providerId: string, mainThreadNode: InternalTreeNode): TPromise<InternalTreeNode[]> {
$resolveChildren(providerId: string, mainThreadNode: ITreeNode): TPromise<ITreeNode[]> {
const provider = this._extNodeProviders[providerId];
if (!provider) {
const errMessage = localize('treeExplorer.notRegistered', 'No TreeExplorerNodeProvider with id \'{0}\' registered.', providerId);
return TPromise.wrapError<InternalTreeNode[]>(errMessage);
return TPromise.wrapError<ITreeNode[]>(errMessage);
}

const extNodeMap = this._extNodeMaps[providerId];
Expand All @@ -133,7 +134,7 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {

return asWinJsPromise(() => provider.resolveChildren(extNode)).then(children => {
return children.map(extChild => {
const internalChild = new InternalTreeNodeImpl(providerId, extChild, provider);
const internalChild = new TreeNodeImpl(providerId, extChild, provider);
extNodeMap[internalChild.id] = extChild;
this._mainNodesMap.get(providerId).set(extChild, internalChild);
return internalChild;
Expand All @@ -142,7 +143,7 @@ export class ExtHostTreeView extends ExtHostTreeViewShape {
}

// Convert the command on the ExtHost side so we can pass the original externalNode to the registered handler
$getInternalCommand(providerId: string, mainThreadNode: InternalTreeNode): TPromise<modes.Command> {
$getInternalCommand(providerId: string, mainThreadNode: ITreeNode): TPromise<modes.Command> {
const commandConverter = this.commands.converter;

if (mainThreadNode.clickCommand) {
Expand Down
Loading

0 comments on commit ccae6f9

Please sign in to comment.