views.ts 22.5 KB
Newer Older
S
Sandeep Somavarapu 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

S
Sandeep Somavarapu 已提交
6
import 'vs/css!./media/views';
7
import { Disposable } from 'vs/base/common/lifecycle';
S
Sandeep Somavarapu 已提交
8
import { TPromise } from 'vs/base/common/winjs.base';
9
import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IView, IViewDescriptorCollection } from 'vs/workbench/common/views';
S
Sandeep Somavarapu 已提交
10 11
import { Registry } from 'vs/platform/registry/common/platform';
import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
B
Benjamin Pasero 已提交
12
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
S
Sandeep Somavarapu 已提交
13
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
14
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
S
Sandeep Somavarapu 已提交
15
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
16 17 18
import { Event, chain, filterEvent, Emitter } from 'vs/base/common/event';
import { sortedDiff, firstIndex, move } from 'vs/base/common/arrays';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
S
Sandeep Somavarapu 已提交
19
import { isUndefinedOrNull } from 'vs/base/common/types';
S
Sandeep Somavarapu 已提交
20
import { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
S
Sandeep Somavarapu 已提交
21 22 23
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { localize } from 'vs/nls';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
B
Benjamin Pasero 已提交
24
import { values } from 'vs/base/common/map';
25

S
Sandeep Somavarapu 已提交
26
function filterViewEvent(container: ViewContainer, event: Event<IViewDescriptor[]>): Event<IViewDescriptor[]> {
27
	return chain(event)
S
Sandeep Somavarapu 已提交
28
		.map(views => views.filter(view => view.container === container))
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
		.filter(views => views.length > 0)
		.event;
}

class CounterSet<T> implements IReadableSet<T> {

	private map = new Map<T, number>();

	add(value: T): CounterSet<T> {
		this.map.set(value, (this.map.get(value) || 0) + 1);
		return this;
	}

	delete(value: T): boolean {
		let counter = this.map.get(value) || 0;

		if (counter === 0) {
			return false;
		}

		counter--;

		if (counter === 0) {
			this.map.delete(value);
		} else {
			this.map.set(value, counter);
		}

		return true;
	}

	has(value: T): boolean {
		return this.map.has(value);
	}
}

interface IViewItem {
	viewDescriptor: IViewDescriptor;
	active: boolean;
}

70
class ViewDescriptorCollection extends Disposable implements IViewDescriptorCollection {
71 72 73 74

	private contextKeys = new CounterSet<string>();
	private items: IViewItem[] = [];

S
Sandeep Somavarapu 已提交
75 76
	private _onDidChange: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>());
	readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> = this._onDidChange.event;
77

S
Sandeep Somavarapu 已提交
78
	get activeViewDescriptors(): IViewDescriptor[] {
79 80 81 82 83 84
		return this.items
			.filter(i => i.active)
			.map(i => i.viewDescriptor);
	}

	constructor(
S
Sandeep Somavarapu 已提交
85
		container: ViewContainer,
86 87 88
		@IContextKeyService private contextKeyService: IContextKeyService
	) {
		super();
S
Sandeep Somavarapu 已提交
89
		const onRelevantViewsRegistered = filterViewEvent(container, ViewsRegistry.onViewsRegistered);
90 91
		this._register(onRelevantViewsRegistered(this.onViewsRegistered, this));

S
Sandeep Somavarapu 已提交
92
		const onRelevantViewsDeregistered = filterViewEvent(container, ViewsRegistry.onViewsDeregistered);
93 94 95 96 97
		this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this));

		const onRelevantContextChange = filterEvent(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys));
		this._register(onRelevantContextChange(this.onContextChanged, this));

S
Sandeep Somavarapu 已提交
98
		this.onViewsRegistered(ViewsRegistry.getViews(container));
99 100 101
	}

	private onViewsRegistered(viewDescriptors: IViewDescriptor[]): any {
S
Sandeep Somavarapu 已提交
102
		const added: IViewDescriptor[] = [];
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

		for (const viewDescriptor of viewDescriptors) {
			const item = {
				viewDescriptor,
				active: this.isViewDescriptorActive(viewDescriptor) // TODO: should read from some state?
			};

			this.items.push(item);

			if (viewDescriptor.when) {
				for (const key of viewDescriptor.when.keys()) {
					this.contextKeys.add(key);
				}
			}

			if (item.active) {
S
Sandeep Somavarapu 已提交
119
				added.push(viewDescriptor);
120 121 122
			}
		}

S
Sandeep Somavarapu 已提交
123 124
		if (added.length) {
			this._onDidChange.fire({ added, removed: [] });
125 126 127 128
		}
	}

	private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): any {
S
Sandeep Somavarapu 已提交
129
		const removed: IViewDescriptor[] = [];
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

		for (const viewDescriptor of viewDescriptors) {
			const index = firstIndex(this.items, i => i.viewDescriptor.id === viewDescriptor.id);

			if (index === -1) {
				continue;
			}

			const item = this.items[index];
			this.items.splice(index, 1);

			if (viewDescriptor.when) {
				for (const key of viewDescriptor.when.keys()) {
					this.contextKeys.delete(key);
				}
			}

			if (item.active) {
S
Sandeep Somavarapu 已提交
148
				removed.push(viewDescriptor);
149 150 151
			}
		}

S
Sandeep Somavarapu 已提交
152 153
		if (removed.length) {
			this._onDidChange.fire({ added: [], removed });
154 155 156 157
		}
	}

	private onContextChanged(event: IContextKeyChangeEvent): any {
S
Sandeep Somavarapu 已提交
158 159
		const removed: IViewDescriptor[] = [];
		const added: IViewDescriptor[] = [];
160 161 162 163 164

		for (const item of this.items) {
			const active = this.isViewDescriptorActive(item.viewDescriptor);

			if (item.active !== active) {
S
Sandeep Somavarapu 已提交
165 166 167 168 169
				if (active) {
					added.push(item.viewDescriptor);
				} else {
					removed.push(item.viewDescriptor);
				}
170 171 172 173 174
			}

			item.active = active;
		}

S
Sandeep Somavarapu 已提交
175 176
		if (added.length || removed.length) {
			this._onDidChange.fire({ added, removed });
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
		}
	}

	private isViewDescriptorActive(viewDescriptor: IViewDescriptor): boolean {
		return !viewDescriptor.when || this.contextKeyService.contextMatchesRules(viewDescriptor.when);
	}
}

export interface IViewState {
	visible: boolean;
	collapsed: boolean;
	order?: number;
	size?: number;
}

export interface IViewDescriptorRef {
	viewDescriptor: IViewDescriptor;
	index: number;
}

export interface IAddedViewDescriptorRef extends IViewDescriptorRef {
	collapsed: boolean;
	size?: number;
}

export class ContributableViewsModel extends Disposable {

	readonly viewDescriptors: IViewDescriptor[] = [];
	get visibleViewDescriptors(): IViewDescriptor[] {
		return this.viewDescriptors.filter(v => this.viewStates.get(v.id).visible);
	}

209 210
	private _onDidAdd = this._register(new Emitter<IAddedViewDescriptorRef[]>());
	readonly onDidAdd: Event<IAddedViewDescriptorRef[]> = this._onDidAdd.event;
211

212 213
	private _onDidRemove = this._register(new Emitter<IViewDescriptorRef[]>());
	readonly onDidRemove: Event<IViewDescriptorRef[]> = this._onDidRemove.event;
214 215 216 217 218

	private _onDidMove = this._register(new Emitter<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }>());
	readonly onDidMove: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }> = this._onDidMove.event;

	constructor(
S
Sandeep Somavarapu 已提交
219
		container: ViewContainer,
220
		viewsService: IViewsService,
221
		protected viewStates = new Map<string, IViewState>(),
222 223
	) {
		super();
224
		const viewDescriptorCollection = viewsService.getViewDescriptors(container);
225

S
Sandeep Somavarapu 已提交
226 227
		this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors)));
		this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	}

	isVisible(id: string): boolean {
		const state = this.viewStates.get(id);

		if (!state) {
			throw new Error(`Unknown view ${id}`);
		}

		return state.visible;
	}

	setVisible(id: string, visible: boolean): void {
		const { visibleIndex, viewDescriptor, state } = this.find(id);

		if (!viewDescriptor.canToggleVisibility) {
			throw new Error(`Can't toggle this view's visibility`);
		}

		if (state.visible === visible) {
			return;
		}

		state.visible = visible;

		if (visible) {
254
			this._onDidAdd.fire([{ index: visibleIndex, viewDescriptor, size: state.size, collapsed: state.collapsed }]);
255
		} else {
256
			this._onDidRemove.fire([{ index: visibleIndex, viewDescriptor }]);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
		}
	}

	isCollapsed(id: string): boolean {
		const state = this.viewStates.get(id);

		if (!state) {
			throw new Error(`Unknown view ${id}`);
		}

		return state.collapsed;
	}

	setCollapsed(id: string, collapsed: boolean): void {
		const { state } = this.find(id);
		state.collapsed = collapsed;
	}

	getSize(id: string): number | undefined {
		const state = this.viewStates.get(id);

		if (!state) {
			throw new Error(`Unknown view ${id}`);
		}

		return state.size;
	}

	setSize(id: string, size: number): void {
		const { state } = this.find(id);
		state.size = size;
	}

	move(from: string, to: string): void {
		const fromIndex = firstIndex(this.viewDescriptors, v => v.id === from);
		const toIndex = firstIndex(this.viewDescriptors, v => v.id === to);

		const fromViewDescriptor = this.viewDescriptors[fromIndex];
		const toViewDescriptor = this.viewDescriptors[toIndex];

		move(this.viewDescriptors, fromIndex, toIndex);

		for (let index = 0; index < this.viewDescriptors.length; index++) {
			const state = this.viewStates.get(this.viewDescriptors[index].id);
			state.order = index;
		}

		this._onDidMove.fire({
			from: { index: fromIndex, viewDescriptor: fromViewDescriptor },
			to: { index: toIndex, viewDescriptor: toViewDescriptor }
		});
	}

	private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState } {
		for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) {
			const viewDescriptor = this.viewDescriptors[i];
			const state = this.viewStates.get(viewDescriptor.id);

			if (viewDescriptor.id === id) {
				return { index: i, visibleIndex, viewDescriptor, state };
			}

			if (state.visible) {
				visibleIndex++;
			}
		}

		throw new Error(`view descriptor ${id} not found`);
	}

	private compareViewDescriptors(a: IViewDescriptor, b: IViewDescriptor): number {
		if (a.id === b.id) {
			return 0;
		}

332 333 334 335 336 337 338
		return (this.getViewOrder(a) - this.getViewOrder(b)) || (a.id < b.id ? -1 : 1);
	}

	private getViewOrder(viewDescriptor: IViewDescriptor): number {
		const viewState = this.viewStates.get(viewDescriptor.id);
		const viewOrder = viewState && typeof viewState.order === 'number' ? viewState.order : viewDescriptor.order;
		return typeof viewOrder === 'number' ? viewOrder : Number.MAX_VALUE;
339 340 341 342 343 344 345 346 347 348 349 350
	}

	private onDidChangeViewDescriptors(viewDescriptors: IViewDescriptor[]): void {
		const ids = new Set<string>();

		for (const viewDescriptor of this.viewDescriptors) {
			ids.add(viewDescriptor.id);
		}

		viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this));

		for (const viewDescriptor of viewDescriptors) {
S
Sandeep Somavarapu 已提交
351 352
			const viewState = this.viewStates.get(viewDescriptor.id);
			if (viewState) {
S
Sandeep Somavarapu 已提交
353 354 355
				// set defaults if not set
				viewState.visible = isUndefinedOrNull(viewState.visible) ? !viewDescriptor.hideByDefault : viewState.visible;
				viewState.collapsed = isUndefinedOrNull(viewState.collapsed) ? !!viewDescriptor.collapsed : viewState.collapsed;
S
Sandeep Somavarapu 已提交
356
			} else {
357
				this.viewStates.set(viewDescriptor.id, {
S
Sandeep Somavarapu 已提交
358
					visible: !viewDescriptor.hideByDefault,
359
					collapsed: viewDescriptor.collapsed
360 361 362 363 364 365 366 367 368 369
				});
			}
		}

		const splices = sortedDiff<IViewDescriptor>(
			this.viewDescriptors,
			viewDescriptors,
			this.compareViewDescriptors.bind(this)
		).reverse();

370 371 372
		const toRemove: { index: number, viewDescriptor: IViewDescriptor }[] = [];
		const toAdd: { index: number, viewDescriptor: IViewDescriptor, size: number, collapsed: boolean }[] = [];

373 374 375 376 377 378 379 380 381
		for (const splice of splices) {
			const startViewDescriptor = this.viewDescriptors[splice.start];
			let startIndex = startViewDescriptor ? this.find(startViewDescriptor.id).visibleIndex : this.viewDescriptors.length;

			for (let i = 0; i < splice.deleteCount; i++) {
				const viewDescriptor = this.viewDescriptors[splice.start + i];
				const { state } = this.find(viewDescriptor.id);

				if (state.visible) {
382
					toRemove.push({ index: startIndex++, viewDescriptor });
383 384 385 386 387 388 389 390
				}
			}

			for (let i = 0; i < splice.toInsert.length; i++) {
				const viewDescriptor = splice.toInsert[i];
				const state = this.viewStates.get(viewDescriptor.id);

				if (state.visible) {
391
					toAdd.push({ index: startIndex++, viewDescriptor, size: state.size, collapsed: state.collapsed });
392 393 394 395 396 397
				}
			}
		}

		this.viewDescriptors.splice(0, this.viewDescriptors.length, ...viewDescriptors);

398 399 400 401 402 403 404 405
		if (toRemove.length) {
			this._onDidRemove.fire(toRemove);
		}

		if (toAdd.length) {
			this._onDidAdd.fire(toAdd);
		}
	}
406 407 408 409 410
}

export class PersistentContributableViewsModel extends ContributableViewsModel {

	private viewletStateStorageId: string;
411 412
	private readonly hiddenViewsStorageId: string;

413
	private storageService: IStorageService;
414 415 416
	private contextService: IWorkspaceContextService;

	constructor(
S
Sandeep Somavarapu 已提交
417
		container: ViewContainer,
418
		viewletStateStorageId: string,
419
		@IViewsService viewsService: IViewsService,
420
		@IStorageService storageService: IStorageService,
421 422
		@IWorkspaceContextService contextService: IWorkspaceContextService
	) {
423
		const hiddenViewsStorageId = `${viewletStateStorageId}.hidden`;
424
		const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, hiddenViewsStorageId, storageService, contextService);
425

426
		super(container, viewsService, viewStates);
427 428

		this.viewletStateStorageId = viewletStateStorageId;
429
		this.hiddenViewsStorageId = hiddenViewsStorageId;
430
		this.storageService = storageService;
431
		this.contextService = contextService;
432

S
Sandeep Somavarapu 已提交
433 434
		this._register(this.onDidAdd(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
		this._register(this.onDidRemove(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
435
		this._register(this.storageService.onWillSaveState(() => this.saveViewsStates()));
436 437
	}

B
Benjamin Pasero 已提交
438
	private saveViewsStates(): void {
439 440 441 442 443 444 445
		const storedViewsStates: { [id: string]: { collapsed: boolean, size: number, order: number } } = {};
		for (const viewDescriptor of this.viewDescriptors) {
			const viewState = this.viewStates.get(viewDescriptor.id);
			if (viewState) {
				storedViewsStates[viewDescriptor.id] = { collapsed: viewState.collapsed, size: viewState.size, order: viewState.order };
			}
		}
B
Benjamin Pasero 已提交
446
		this.storageService.store(this.viewletStateStorageId, JSON.stringify(storedViewsStates), StorageScope.WORKSPACE);
447
	}
448

S
Sandeep Somavarapu 已提交
449
	private saveVisibilityStates(viewDescriptors: IViewDescriptor[]): void {
450
		const storedViewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(this.hiddenViewsStorageId, this.storageService, this.contextService);
S
Sandeep Somavarapu 已提交
451
		for (const viewDescriptor of viewDescriptors) {
452 453
			if (viewDescriptor.canToggleVisibility) {
				const viewState = this.viewStates.get(viewDescriptor.id);
B
Benjamin Pasero 已提交
454
				storedViewsVisibilityStates.set(viewDescriptor.id, { id: viewDescriptor.id, isHidden: viewState ? !viewState.visible : void 0 });
455 456
			}
		}
B
Benjamin Pasero 已提交
457
		this.storageService.store(this.hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
458 459
	}

460
	private static loadViewsStates(viewletStateStorageId: string, hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map<string, IViewState> {
461
		const viewStates = new Map<string, IViewState>();
462 463
		const storedViewsStates = JSON.parse(storageService.get(viewletStateStorageId, contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL, '{}'));
		const viewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(hiddenViewsStorageId, storageService, contextService);
B
Benjamin Pasero 已提交
464
		for (const { id, isHidden } of values(viewsVisibilityStates)) {
465 466 467
			const viewState = storedViewsStates[id];
			if (viewState) {
				viewStates.set(id, <IViewState>{ ...viewState, ...{ visible: !isHidden } });
S
Sandeep Somavarapu 已提交
468 469 470
			} else {
				// New workspace
				viewStates.set(id, <IViewState>{ ...{ visible: !isHidden } });
471 472 473 474
			}
		}
		for (const id of Object.keys(storedViewsStates)) {
			if (!viewStates.has(id)) {
S
Sandeep Somavarapu 已提交
475
				viewStates.set(id, <IViewState>{ ...storedViewsStates[id] });
476 477 478
			}
		}
		return viewStates;
479 480
	}

B
Benjamin Pasero 已提交
481
	private static loadViewsVisibilityState(hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map<string, { id: string, isHidden: boolean }> {
482
		const storedVisibilityStates = <Array<string | { id: string, isHidden: boolean }>>JSON.parse(storageService.get(hiddenViewsStorageId, StorageScope.GLOBAL, '[]'));
B
Benjamin Pasero 已提交
483 484 485 486 487 488 489 490 491

		return storedVisibilityStates.reduce((result, storedState) => {
			if (typeof storedState === 'string' /* migration */) {
				result.set(storedState, { id: storedState, isHidden: true });
			} else {
				result.set(storedState.id, storedState);
			}
			return result;
		}, new Map<string, { id: string, isHidden: boolean }>());
S
Sandeep Somavarapu 已提交
492
	}
493
}
S
Sandeep Somavarapu 已提交
494

495 496
const SCM_VIEWLET_ID = 'workbench.view.scm';

S
Sandeep Somavarapu 已提交
497
export class ViewsService extends Disposable implements IViewsService {
S
Sandeep Somavarapu 已提交
498 499 500

	_serviceBrand: any;

501
	private readonly viewDescriptorCollections: Map<ViewContainer, IViewDescriptorCollection>;
S
Sandeep Somavarapu 已提交
502
	private readonly activeViewContextKeys: Map<string, IContextKey<boolean>>;
503

S
Sandeep Somavarapu 已提交
504
	constructor(
S
Sandeep Somavarapu 已提交
505 506
		@ILifecycleService private lifecycleService: ILifecycleService,
		@IViewletService private viewletService: IViewletService,
507
		@IStorageService private storageService: IStorageService,
508
		@IContextKeyService private contextKeyService: IContextKeyService
S
Sandeep Somavarapu 已提交
509
	) {
510
		super();
S
Sandeep Somavarapu 已提交
511

512
		this.viewDescriptorCollections = new Map<ViewContainer, IViewDescriptorCollection>();
S
Sandeep Somavarapu 已提交
513 514 515 516 517
		this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();

		this.onDidRegisterViews(ViewsRegistry.getAllViews());
		this._register(ViewsRegistry.onViewsRegistered(views => this.onDidRegisterViews(views)));

S
Sandeep Somavarapu 已提交
518 519 520
		const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
		viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer));
		this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer)));
B
Benjamin Pasero 已提交
521
		this._register(Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).onDidRegister(viewlet => this.viewletService.setViewletEnablement(viewlet.id, this.storageService.getBoolean(`viewservice.${viewlet.id}.enablement`, StorageScope.WORKSPACE, viewlet.id !== TEST_VIEW_CONTAINER_ID))));
S
Sandeep Somavarapu 已提交
522 523
	}

524 525 526 527
	getViewDescriptors(container: ViewContainer): IViewDescriptorCollection {
		return this.viewDescriptorCollections.get(container);
	}

S
Sandeep Somavarapu 已提交
528
	openView(id: string, focus: boolean): TPromise<IView> {
S
Sandeep Somavarapu 已提交
529 530
		const viewDescriptor = ViewsRegistry.getView(id);
		if (viewDescriptor) {
S
Sandeep Somavarapu 已提交
531
			const viewletDescriptor = this.viewletService.getViewlet(viewDescriptor.container.id);
532 533 534 535
			if (viewletDescriptor) {
				return this.viewletService.openViewlet(viewletDescriptor.id)
					.then((viewlet: IViewsViewlet) => {
						if (viewlet && viewlet.openView) {
S
Sandeep Somavarapu 已提交
536
							return viewlet.openView(id, focus);
537
						}
S
Sandeep Somavarapu 已提交
538
						return null;
539 540
					});
			}
S
Sandeep Somavarapu 已提交
541
		}
542
		return Promise.resolve(null);
S
Sandeep Somavarapu 已提交
543 544
	}

S
Sandeep Somavarapu 已提交
545
	private onDidRegisterViewContainer(viewContainer: ViewContainer): void {
S
Sandeep Somavarapu 已提交
546 547
		const viewDescriptorCollection = this.registerViewDescriptorCollection(viewContainer);

548 549
		// TODO: @Joao Remove this after moving SCM Viewlet to ViewContainerViewlet - https://github.com/Microsoft/vscode/issues/49054
		if (viewContainer.id !== SCM_VIEWLET_ID) {
S
Sandeep Somavarapu 已提交
550
			this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.updateViewletEnablement(viewContainer, viewDescriptorCollection)));
551 552
			this.lifecycleService.when(LifecyclePhase.Eventually).then(() => this.updateViewletEnablement(viewContainer, viewDescriptorCollection));
		}
S
Sandeep Somavarapu 已提交
553 554
	}

S
Sandeep Somavarapu 已提交
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
	private registerViewDescriptorCollection(viewContainer: ViewContainer): IViewDescriptorCollection {
		const viewDescriptorCollection = this._register(new ViewDescriptorCollection(viewContainer, this.contextKeyService));

		this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] });
		this._register(viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed)));

		this.viewDescriptorCollections.set(viewContainer, viewDescriptorCollection);
		return viewDescriptorCollection;
	}

	private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[] }): void {
		added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true));
		removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false));
	}

	private onDidRegisterViews(viewDescriptors: IViewDescriptor[]): void {
		for (const viewDescriptor of viewDescriptors) {
			const viewlet = this.viewletService.getViewlet(viewDescriptor.container.id);
S
Sandeep Somavarapu 已提交
573
			const command: ICommandAction = {
S
Sandeep Somavarapu 已提交
574
				id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`,
S
Sandeep Somavarapu 已提交
575
				title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) },
S
Sandeep Somavarapu 已提交
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
				category: viewlet ? viewlet.name : localize('view category', "View"),
			};
			const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`);

			CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true));

			MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
				command,
				when
			});

			if (viewDescriptor.focusCommand && viewDescriptor.focusCommand.keybindings) {
				KeybindingsRegistry.registerKeybindingRule({
					id: command.id,
					when,
					weight: KeybindingWeight.WorkbenchContrib,
					primary: viewDescriptor.focusCommand.keybindings.primary,
					secondary: viewDescriptor.focusCommand.keybindings.secondary,
					linux: viewDescriptor.focusCommand.keybindings.linux,
					mac: viewDescriptor.focusCommand.keybindings.mac,
					win: viewDescriptor.focusCommand.keybindings.win
				});
			}
		}
	}

	private getOrCreateActiveViewContextKey(viewDescriptor: IViewDescriptor): IContextKey<boolean> {
		const activeContextKeyId = `${viewDescriptor.id}.active`;
		let contextKey = this.activeViewContextKeys.get(activeContextKeyId);
		if (!contextKey) {
			contextKey = new RawContextKey(activeContextKeyId, false).bindTo(this.contextKeyService);
			this.activeViewContextKeys.set(activeContextKeyId, contextKey);
		}
		return contextKey;
	}

	private updateViewletEnablement(viewContainer: ViewContainer, viewDescriptorCollection: IViewDescriptorCollection): void {
		const enabled = viewDescriptorCollection.activeViewDescriptors.length > 0;
S
Sandeep Somavarapu 已提交
614
		this.viewletService.setViewletEnablement(viewContainer.id, enabled);
B
Benjamin Pasero 已提交
615
		this.storageService.store(`viewservice.${viewContainer.id}.enablement`, enabled, StorageScope.WORKSPACE);
S
Sandeep Somavarapu 已提交
616
	}
617
}