Skip to content

Commit 125ac04

Browse files
committed
Refactor custom views:
- Custom view service to get tree item viewer - Custom view service to register the data provider
1 parent abf0a5b commit 125ac04

8 files changed

Lines changed: 155 additions & 119 deletions

File tree

src/vs/workbench/api/browser/viewsExtensionPoint.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
88
import { forEach } from 'vs/base/common/collections';
99
import { IJSONSchema } from 'vs/base/common/jsonSchema';
1010
import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
11-
import { ViewLocation, ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views';
11+
import { ViewLocation, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench/common/views';
1212
import { CustomTreeViewPanel } from 'vs/workbench/browser/parts/views/customView';
1313
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
1414
import { coalesce, } from 'vs/base/common/arrays';
@@ -102,14 +102,15 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyV
102102
const registeredViews = ViewsRegistry.getViews(location);
103103
const viewIds = [];
104104
const viewDescriptors = coalesce(entry.value.map(item => {
105-
const viewDescriptor = <IViewDescriptor>{
105+
const viewDescriptor = <ICustomViewDescriptor>{
106106
id: item.id,
107107
name: item.name,
108108
ctor: CustomTreeViewPanel,
109109
location,
110110
when: ContextKeyExpr.deserialize(item.when),
111111
canToggleVisibility: true,
112-
collapsed: true
112+
collapsed: true,
113+
treeItemView: true
113114
};
114115

115116
// validate

src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
99
import { Disposable } from 'vs/base/common/lifecycle';
1010
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
1111
import { IMessageService, Severity } from 'vs/platform/message/common/message';
12-
import { ViewsRegistry, ITreeViewDataProvider, ITreeItem } from 'vs/workbench/common/views';
12+
import { ITreeViewDataProvider, ITreeItem, ICustomViewsService } from 'vs/workbench/common/views';
1313
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
1414
import { assign } from 'vs/base/common/objects';
1515

@@ -20,27 +20,24 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
2020

2121
constructor(
2222
extHostContext: IExtHostContext,
23+
@ICustomViewsService private viewsService: ICustomViewsService,
2324
@IMessageService private messageService: IMessageService
2425
) {
2526
super();
2627
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
2728
}
2829

29-
$registerView(treeViewId: string): void {
30-
ViewsRegistry.registerTreeViewDataProvider(treeViewId, this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.messageService)));
30+
$registerTreeViewDataProvider(treeViewId: string): void {
31+
const dataProvider = this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.messageService));
32+
this.viewsService.registerTreeViewDataProvider(treeViewId, dataProvider);
3133
}
3234

3335
$refresh(treeViewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): void {
34-
const treeViewDataProvider: TreeViewDataProvider = <TreeViewDataProvider>ViewsRegistry.getTreeViewDataProvider(treeViewId);
35-
if (treeViewDataProvider) {
36-
treeViewDataProvider.refresh(itemsToRefresh);
36+
const treeViewer = this.viewsService.getTreeItemViewer(treeViewId);
37+
if (treeViewer && treeViewer.dataProvider) {
38+
(<TreeViewDataProvider>treeViewer.dataProvider).refresh(itemsToRefresh);
3739
}
3840
}
39-
40-
dispose(): void {
41-
ViewsRegistry.deregisterTreeViewDataProviders();
42-
super.dispose();
43-
}
4441
}
4542

4643
type TreeItemHandle = string;
@@ -111,10 +108,6 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
111108
}
112109
}
113110

114-
dispose(): void {
115-
this._onDispose.fire();
116-
}
117-
118111
private postGetElements(elements: ITreeItem[]): ITreeItem[] {
119112
const result = [];
120113
if (elements) {
@@ -139,4 +132,8 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
139132
assign(current, treeItem);
140133
}
141134
}
135+
136+
dispose(): void {
137+
this._onDispose.fire();
138+
}
142139
}

src/vs/workbench/api/node/extHost.protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ export interface MainThreadEditorsShape extends IDisposable {
214214
}
215215

216216
export interface MainThreadTreeViewsShape extends IDisposable {
217-
$registerView(treeViewId: string): void;
217+
$registerTreeViewDataProvider(treeViewId: string): void;
218218
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): void;
219219
}
220220

src/vs/workbench/api/node/extHostTreeViews.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class ExtHostTreeView<T> extends Disposable {
8787

8888
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
8989
super();
90-
this.proxy.$registerView(viewId);
90+
this.proxy.$registerTreeViewDataProvider(viewId);
9191
if (dataProvider.onDidChangeTreeData) {
9292
this._register(debounceEvent<T, T[]>(dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
9393
}

src/vs/workbench/browser/parts/views/customView.ts

Lines changed: 86 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress';
2222
import { ITree, IDataSource, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
2323
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
2424
import { ActionItem, ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
25-
import { ViewsRegistry, TreeItemCollapsibleState, ITreeItem, ITreeViewDataProvider, TreeViewItemHandleArg } from 'vs/workbench/common/views';
26-
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
25+
import { TreeItemCollapsibleState, ITreeItem, TreeViewItemHandleArg, ITreeItemViewer, ICustomViewsService, ITreeViewDataProvider, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench/common/views';
2726
import { IViewletViewOptions, IViewOptions, FileIconThemableWorkbenchTree, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
2827
import { ICommandService } from 'vs/platform/commands/common/commands';
2928
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
@@ -33,8 +32,48 @@ import { basename } from 'vs/base/common/paths';
3332
import { FileKind } from 'vs/platform/files/common/files';
3433
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
3534
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
35+
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
36+
37+
export class CustomViewsService implements ICustomViewsService {
3638

37-
class TreeViewer extends Disposable {
39+
_serviceBrand: any;
40+
41+
private viewers: Map<string, ITreeItemViewer> = new Map<string, ITreeItemViewer>();
42+
43+
constructor(
44+
@IInstantiationService private instantiationService: IInstantiationService
45+
) {
46+
}
47+
48+
getTreeItemViewer(id: string): ITreeItemViewer {
49+
let viewer = this.viewers.get(id);
50+
if (!viewer) {
51+
viewer = this.createViewer(id);
52+
if (viewer) {
53+
this.viewers.set(id, viewer);
54+
}
55+
}
56+
return viewer;
57+
}
58+
59+
registerTreeViewDataProvider(id: string, dataProvider: ITreeViewDataProvider): void {
60+
const treeViewer = this.getTreeItemViewer(id);
61+
if (treeViewer) {
62+
treeViewer.dataProvider = dataProvider;
63+
dataProvider.onDispose(() => treeViewer.dataProvider = null);
64+
}
65+
}
66+
67+
private createViewer(id: string): ITreeItemViewer {
68+
const viewDeescriptor = <ICustomViewDescriptor>ViewsRegistry.getView(id);
69+
if (viewDeescriptor && viewDeescriptor.treeItemView) {
70+
return this.instantiationService.createInstance(TreeItemViewer, id);
71+
}
72+
return null;
73+
}
74+
}
75+
76+
export class TreeItemViewer extends Disposable implements ITreeItemViewer {
3877

3978
private isVisible: boolean = false;
4079
private activated: boolean = false;
@@ -43,6 +82,7 @@ class TreeViewer extends Disposable {
4382
private treeInputPromise: TPromise<void>;
4483
private elementsToRefresh: ITreeItem[] = [];
4584

85+
private _dataProvider: ITreeViewDataProvider;
4686
private dataProviderElementChangeListener: IDisposable;
4787

4888
constructor(
@@ -52,6 +92,37 @@ class TreeViewer extends Disposable {
5292
super();
5393
}
5494

95+
get dataProvider(): ITreeViewDataProvider {
96+
return this._dataProvider;
97+
}
98+
99+
set dataProvider(dataProvider: ITreeViewDataProvider) {
100+
this._dataProvider = dataProvider;
101+
if (this.dataProviderElementChangeListener) {
102+
this.dataProviderElementChangeListener.dispose();
103+
}
104+
if (this.dataProvider) {
105+
this.dataProviderElementChangeListener = this._register(this.dataProvider.onDidChange(element => this.refresh(element)));
106+
this.refresh(null);
107+
}
108+
}
109+
110+
refresh(elements: ITreeItem[]): TPromise<void> {
111+
if (this.tree) {
112+
if (!elements) {
113+
const root: ITreeItem = this.tree.getInput();
114+
root.children = null; // reset children
115+
elements = [root];
116+
}
117+
if (this.isVisible) {
118+
return this.doRefresh(elements);
119+
} else {
120+
this.elementsToRefresh.push(...elements);
121+
}
122+
}
123+
return TPromise.as(null);
124+
}
125+
55126
setTree(tree: ITree): void {
56127
this.tree = tree;
57128
this.setInput();
@@ -118,61 +189,15 @@ class TreeViewer extends Disposable {
118189
private setInput(): TPromise<void> {
119190
if (this.tree) {
120191
if (!this.treeInputPromise) {
121-
if (this.listenToDataProvider()) {
122-
this.treeInputPromise = this.tree.setInput(new Root());
123-
} else {
124-
this.treeInputPromise = new TPromise<void>((c, e) => {
125-
this._register(ViewsRegistry.onTreeViewDataProviderRegistered(id => {
126-
if (this.id === id) {
127-
if (this.listenToDataProvider()) {
128-
this.tree.setInput(new Root()).then(() => c(null));
129-
}
130-
}
131-
}));
132-
});
133-
}
192+
this.treeInputPromise = this.tree.setInput(new Root());
134193
}
135194
return this.treeInputPromise;
136195
}
137196
return TPromise.as(null);
138197
}
139198

140-
private listenToDataProvider(): boolean {
141-
let dataProvider = ViewsRegistry.getTreeViewDataProvider(this.id);
142-
if (dataProvider) {
143-
if (this.dataProviderElementChangeListener) {
144-
this.dataProviderElementChangeListener.dispose();
145-
}
146-
this.dataProviderElementChangeListener = this._register(dataProvider.onDidChange(element => this.refresh(element)));
147-
const disposable = dataProvider.onDispose(() => {
148-
this.dataProviderElementChangeListener.dispose();
149-
this.tree.setInput(new Root());
150-
disposable.dispose();
151-
});
152-
return true;
153-
}
154-
return false;
155-
}
156-
157-
private refresh(elements: ITreeItem[]): void {
158-
if (this.tree) {
159-
if (!elements) {
160-
const root: ITreeItem = this.tree.getInput();
161-
root.children = null; // reset children
162-
elements = [root];
163-
}
164-
if (this.isVisible) {
165-
this.doRefresh(elements);
166-
} else {
167-
this.elementsToRefresh.push(...elements);
168-
}
169-
}
170-
}
171-
172-
private doRefresh(elements: ITreeItem[]): void {
173-
for (const element of elements) {
174-
this.tree.refresh(element);
175-
}
199+
private doRefresh(elements: ITreeItem[]): TPromise<void> {
200+
return TPromise.join(elements.map(e => this.tree.refresh(e))) as TPromise;
176201
}
177202
}
178203

@@ -181,7 +206,7 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
181206
private menus: Menus;
182207
private treeContainer: HTMLElement;
183208
private tree: WorkbenchTree;
184-
private treeViewer: TreeViewer;
209+
private treeViewer: TreeItemViewer;
185210

186211
constructor(
187212
options: IViewletViewOptions,
@@ -191,12 +216,13 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
191216
@IInstantiationService private instantiationService: IInstantiationService,
192217
@IThemeService private themeService: IWorkbenchThemeService,
193218
@ICommandService private commandService: ICommandService,
194-
@IConfigurationService configurationService: IConfigurationService
219+
@IConfigurationService configurationService: IConfigurationService,
220+
@ICustomViewsService customViewsService: ICustomViewsService,
195221
) {
196222
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService, configurationService);
197223
this.menus = this.instantiationService.createInstance(Menus, this.id);
198224
this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables);
199-
this.treeViewer = this.instantiationService.createInstance(TreeViewer, this.id);
225+
this.treeViewer = <TreeItemViewer>customViewsService.getTreeItemViewer(this.id);
200226
this.disposables.push(this.treeViewer);
201227
this.updateTreeVisibility();
202228
}
@@ -213,7 +239,7 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
213239
renderBody(container: HTMLElement): void {
214240
this.treeContainer = DOM.append(container, DOM.$('.tree-explorer-viewlet-tree-view'));
215241
const actionItemProvider = (action: IAction) => this.getActionItem(action);
216-
const dataSource = this.instantiationService.createInstance(TreeDataSource, this.id);
242+
const dataSource = this.instantiationService.createInstance(TreeDataSource, this.treeViewer);
217243
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, this.menus, actionItemProvider);
218244
const controller = this.instantiationService.createInstance(TreeController, this.id, this.menus);
219245
this.tree = this.instantiationService.createInstance(FileIconThemableWorkbenchTree,
@@ -301,7 +327,7 @@ class Root implements ITreeItem {
301327
class TreeDataSource implements IDataSource {
302328

303329
constructor(
304-
private id: string,
330+
private treeItemViewer: ITreeItemViewer,
305331
@IProgressService private progressService: IProgressService
306332
) {
307333
}
@@ -311,7 +337,7 @@ class TreeDataSource implements IDataSource {
311337
}
312338

313339
public hasChildren(tree: ITree, node: ITreeItem): boolean {
314-
if (!this.getDataProvider()) {
340+
if (!this.treeItemViewer.dataProvider) {
315341
return false;
316342
}
317343
return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded;
@@ -322,9 +348,8 @@ class TreeDataSource implements IDataSource {
322348
return TPromise.as(node.children);
323349
}
324350

325-
const dataProvider = this.getDataProvider();
326-
if (dataProvider) {
327-
const promise = node instanceof Root ? dataProvider.getElements() : dataProvider.getChildren(node);
351+
if (this.treeItemViewer.dataProvider) {
352+
const promise = node instanceof Root ? this.treeItemViewer.dataProvider.getElements() : this.treeItemViewer.dataProvider.getChildren(node);
328353
this.progressService.showWhile(promise, 100);
329354
return promise.then(children => {
330355
node.children = children;
@@ -342,10 +367,6 @@ class TreeDataSource implements IDataSource {
342367
public getParent(tree: ITree, node: any): TPromise<any> {
343368
return TPromise.as(null);
344369
}
345-
346-
private getDataProvider(): ITreeViewDataProvider {
347-
return ViewsRegistry.getTreeViewDataProvider(this.id);
348-
}
349370
}
350371

351372
interface ITreeExplorerTemplateData {

0 commit comments

Comments
 (0)