views.ts 28.2 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';
S
Sandeep Somavarapu 已提交
7
import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
8
import { IViewsService, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry } from 'vs/workbench/common/views';
S
Sandeep Somavarapu 已提交
9
import { Registry } from 'vs/platform/registry/common/platform';
B
Benjamin Pasero 已提交
10
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
11
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
S
Sandeep Somavarapu 已提交
12
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
J
Joao Moreno 已提交
13
import { Event, Emitter } from 'vs/base/common/event';
14
import { sortedDiff, firstIndex, move, isNonEmptyArray } from 'vs/base/common/arrays';
S
Sandeep Somavarapu 已提交
15
import { isUndefinedOrNull, isUndefined } from 'vs/base/common/types';
S
Sandeep Somavarapu 已提交
16
import { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
S
Sandeep Somavarapu 已提交
17 18 19
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 已提交
20
import { values } from 'vs/base/common/map';
J
Joao Moreno 已提交
21 22
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { toggleClass, addClass } from 'vs/base/browser/dom';
23
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
24
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
25

26
function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event<IViewDescriptor[]> {
J
Joao Moreno 已提交
27
	return Event.chain(event)
28
		.map(({ views, viewContainer }) => viewContainer === container ? views : [])
29 30 31 32
		.filter(views => views.length > 0)
		.event;
}

33 34 35 36 37 38 39
function filterViewMoveEvent(container: ViewContainer, event: Event<{ from: ViewContainer, to: ViewContainer, views: IViewDescriptor[] }>): Event<{ added?: IViewDescriptor[], removed?: IViewDescriptor[] }> {
	return Event.chain(event)
		.map(({ views, from, to }) => from === container ? { removed: views } : to === container ? { added: views } : {})
		.filter(({ added, removed }) => isNonEmptyArray(added) || isNonEmptyArray(removed))
		.event;
}

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 70 71 72 73 74 75 76
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;
}

77
class ViewDescriptorCollection extends Disposable implements IViewDescriptorCollection {
78 79 80 81

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

S
Sandeep Somavarapu 已提交
82 83
	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;
84

S
Sandeep Somavarapu 已提交
85
	get activeViewDescriptors(): IViewDescriptor[] {
86 87 88 89 90
		return this.items
			.filter(i => i.active)
			.map(i => i.viewDescriptor);
	}

S
Sandeep Somavarapu 已提交
91 92 93 94
	get allViewDescriptors(): IViewDescriptor[] {
		return this.items.map(i => i.viewDescriptor);
	}

95
	constructor(
S
Sandeep Somavarapu 已提交
96
		container: ViewContainer,
97
		@IContextKeyService private readonly contextKeyService: IContextKeyService
98 99
	) {
		super();
100 101
		const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
		const onRelevantViewsRegistered = filterViewRegisterEvent(container, viewsRegistry.onViewsRegistered);
102 103
		this._register(onRelevantViewsRegistered(this.onViewsRegistered, this));

104
		const onRelevantViewsMoved = filterViewMoveEvent(container, viewsRegistry.onDidChangeContainer);
105 106 107 108 109 110 111 112 113
		this._register(onRelevantViewsMoved(({ added, removed }) => {
			if (isNonEmptyArray(added)) {
				this.onViewsRegistered(added);
			}
			if (isNonEmptyArray(removed)) {
				this.onViewsDeregistered(removed);
			}
		}));

114
		const onRelevantViewsDeregistered = filterViewRegisterEvent(container, viewsRegistry.onViewsDeregistered);
115 116
		this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this));

J
Joao Moreno 已提交
117
		const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys));
118 119
		this._register(onRelevantContextChange(this.onContextChanged, this));

120
		this.onViewsRegistered(viewsRegistry.getViews(container));
121 122
	}

123
	private onViewsRegistered(viewDescriptors: IViewDescriptor[]): void {
S
Sandeep Somavarapu 已提交
124
		const added: IViewDescriptor[] = [];
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140

		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 已提交
141
				added.push(viewDescriptor);
142 143 144
			}
		}

S
Sandeep Somavarapu 已提交
145 146
		if (added.length) {
			this._onDidChange.fire({ added, removed: [] });
147 148 149
		}
	}

150
	private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): void {
S
Sandeep Somavarapu 已提交
151
		const removed: IViewDescriptor[] = [];
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

		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 已提交
170
				removed.push(viewDescriptor);
171 172 173
			}
		}

S
Sandeep Somavarapu 已提交
174 175
		if (removed.length) {
			this._onDidChange.fire({ added: [], removed });
176 177 178
		}
	}

179
	private onContextChanged(event: IContextKeyChangeEvent): void {
S
Sandeep Somavarapu 已提交
180 181
		const removed: IViewDescriptor[] = [];
		const added: IViewDescriptor[] = [];
182 183 184 185 186

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

			if (item.active !== active) {
S
Sandeep Somavarapu 已提交
187 188 189 190 191
				if (active) {
					added.push(item.viewDescriptor);
				} else {
					removed.push(item.viewDescriptor);
				}
192 193 194 195 196
			}

			item.active = active;
		}

S
Sandeep Somavarapu 已提交
197 198
		if (added.length || removed.length) {
			this._onDidChange.fire({ added, removed });
199 200 201 202 203 204 205 206 207
		}
	}

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

export interface IViewState {
S
Sandeep Somavarapu 已提交
208 209 210
	visibleGlobal: boolean | undefined;
	visibleWorkspace: boolean | undefined;
	collapsed: boolean | undefined;
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
	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[] {
S
Sandeep Somavarapu 已提交
229
		return this.viewDescriptors.filter(v => this.isViewDescriptorVisible(v));
230 231
	}

232 233
	private _onDidAdd = this._register(new Emitter<IAddedViewDescriptorRef[]>());
	readonly onDidAdd: Event<IAddedViewDescriptorRef[]> = this._onDidAdd.event;
234

235 236
	private _onDidRemove = this._register(new Emitter<IViewDescriptorRef[]>());
	readonly onDidRemove: Event<IViewDescriptorRef[]> = this._onDidRemove.event;
237 238 239 240

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

S
Sandeep Somavarapu 已提交
241 242 243
	private _onDidChangeViewState = this._register(new Emitter<IViewDescriptorRef>());
	protected readonly onDidChangeViewState: Event<IViewDescriptorRef> = this._onDidChangeViewState.event;

244
	constructor(
S
Sandeep Somavarapu 已提交
245
		container: ViewContainer,
246
		viewsService: IViewsService,
247
		protected viewStates = new Map<string, IViewState>(),
248 249
	) {
		super();
250
		const viewDescriptorCollection = viewsService.getViewDescriptors(container);
251

S
Sandeep Somavarapu 已提交
252 253 254 255
		if (viewDescriptorCollection) {
			this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors)));
			this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors);
		}
256 257 258
	}

	isVisible(id: string): boolean {
S
Sandeep Somavarapu 已提交
259
		const viewDescriptor = this.viewDescriptors.filter(v => v.id === id)[0];
260

S
Sandeep Somavarapu 已提交
261
		if (!viewDescriptor) {
262 263 264
			throw new Error(`Unknown view ${id}`);
		}

S
Sandeep Somavarapu 已提交
265
		return this.isViewDescriptorVisible(viewDescriptor);
266 267
	}

268
	setVisible(id: string, visible: boolean, size?: number): void {
269 270 271 272 273 274
		const { visibleIndex, viewDescriptor, state } = this.find(id);

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

S
Sandeep Somavarapu 已提交
275
		if (this.isViewDescriptorVisible(viewDescriptor) === visible) {
276 277 278
			return;
		}

S
Sandeep Somavarapu 已提交
279 280 281 282 283
		if (viewDescriptor.workspace) {
			state.visibleWorkspace = visible;
		} else {
			state.visibleGlobal = visible;
		}
284

285 286 287 288
		if (typeof size === 'number') {
			state.size = size;
		}

289
		if (visible) {
S
Sandeep Somavarapu 已提交
290
			this._onDidAdd.fire([{ index: visibleIndex, viewDescriptor, size: state.size, collapsed: !!state.collapsed }]);
291
		} else {
292
			this._onDidRemove.fire([{ index: visibleIndex, viewDescriptor }]);
293 294 295 296 297 298 299 300 301 302
		}
	}

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

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

S
Sandeep Somavarapu 已提交
303
		return !!state.collapsed;
304 305 306
	}

	setCollapsed(id: string, collapsed: boolean): void {
S
Sandeep Somavarapu 已提交
307 308 309 310 311
		const { index, state, viewDescriptor } = this.find(id);
		if (state.collapsed !== collapsed) {
			state.collapsed = collapsed;
			this._onDidChangeViewState.fire({ viewDescriptor, index });
		}
312 313 314 315 316 317 318 319 320 321 322 323 324
	}

	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 {
S
Sandeep Somavarapu 已提交
325 326 327 328 329
		const { index, state, viewDescriptor } = this.find(id);
		if (state.size !== size) {
			state.size = size;
			this._onDidChangeViewState.fire({ viewDescriptor, index });
		}
330 331 332 333 334 335 336 337 338 339 340 341
	}

	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++) {
342
			const state = this.viewStates.get(this.viewDescriptors[index].id)!;
343 344 345 346 347 348 349 350 351
			state.order = index;
		}

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

S
Sandeep Somavarapu 已提交
352 353 354 355 356
	private isViewDescriptorVisible(viewDescriptor: IViewDescriptor): boolean {
		const viewState = this.viewStates.get(viewDescriptor.id);
		if (!viewState) {
			throw new Error(`Unknown view ${viewDescriptor.id}`);
		}
S
Sandeep Somavarapu 已提交
357
		return viewDescriptor.workspace ? !!viewState.visibleWorkspace : !!viewState.visibleGlobal;
S
Sandeep Somavarapu 已提交
358 359
	}

360 361 362 363
	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);
364 365 366
			if (!state) {
				throw new Error(`View state for ${id} not found`);
			}
367 368 369 370 371

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

S
Sandeep Somavarapu 已提交
372
			if (viewDescriptor.workspace ? state.visibleWorkspace : state.visibleGlobal) {
373 374 375 376 377 378 379 380 381 382 383 384
				visibleIndex++;
			}
		}

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

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

385 386 387 388 389 390 391 392 393 394 395 396 397
		return (this.getViewOrder(a) - this.getViewOrder(b)) || this.getGroupOrderResult(a, b) || (a.id < b.id ? -1 : 1);
	}

	private getGroupOrderResult(a: IViewDescriptor, b: IViewDescriptor) {
		if (!a.group || !b.group) {
			return 0;
		}

		if (a.group === b.group) {
			return 0;
		}

		return a.group < b.group ? -1 : 1;
398 399 400 401 402 403
	}

	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;
404 405 406 407 408 409 410 411 412 413 414 415
	}

	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 已提交
416 417
			const viewState = this.viewStates.get(viewDescriptor.id);
			if (viewState) {
S
Sandeep Somavarapu 已提交
418
				// set defaults if not set
S
Sandeep Somavarapu 已提交
419 420 421 422 423
				if (viewDescriptor.workspace) {
					viewState.visibleWorkspace = isUndefinedOrNull(viewState.visibleWorkspace) ? !viewDescriptor.hideByDefault : viewState.visibleWorkspace;
				} else {
					viewState.visibleGlobal = isUndefinedOrNull(viewState.visibleGlobal) ? !viewDescriptor.hideByDefault : viewState.visibleGlobal;
				}
S
Sandeep Somavarapu 已提交
424
				viewState.collapsed = isUndefinedOrNull(viewState.collapsed) ? !!viewDescriptor.collapsed : viewState.collapsed;
S
Sandeep Somavarapu 已提交
425
			} else {
426
				this.viewStates.set(viewDescriptor.id, {
S
Sandeep Somavarapu 已提交
427 428
					visibleGlobal: !viewDescriptor.hideByDefault,
					visibleWorkspace: !viewDescriptor.hideByDefault,
M
Matt Bierner 已提交
429
					collapsed: !!viewDescriptor.collapsed
430 431 432 433 434 435 436 437 438 439
				});
			}
		}

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

440
		const toRemove: { index: number, viewDescriptor: IViewDescriptor }[] = [];
M
Matt Bierner 已提交
441
		const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean }[] = [];
442

443 444 445 446 447 448 449
		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];

S
Sandeep Somavarapu 已提交
450
				if (this.isViewDescriptorVisible(viewDescriptor)) {
451
					toRemove.push({ index: startIndex++, viewDescriptor });
452 453 454
				}
			}

455
			for (const viewDescriptor of splice.toInsert) {
456
				const state = this.viewStates.get(viewDescriptor.id)!;
457

S
Sandeep Somavarapu 已提交
458
				if (this.isViewDescriptorVisible(viewDescriptor)) {
S
Sandeep Somavarapu 已提交
459
					toAdd.push({ index: startIndex++, viewDescriptor, size: state.size, collapsed: !!state.collapsed });
460 461 462 463 464 465
				}
			}
		}

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

466 467 468 469 470 471 472 473
		if (toRemove.length) {
			this._onDidRemove.fire(toRemove);
		}

		if (toAdd.length) {
			this._onDidAdd.fire(toAdd);
		}
	}
474 475
}

S
Sandeep Somavarapu 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488
interface IStoredWorkspaceViewState {
	collapsed: boolean;
	isHidden: boolean;
	size?: number;
	order?: number;
}

interface IStoredGlobalViewState {
	id: string;
	isHidden: boolean;
	order?: number;
}

489 490
export class PersistentContributableViewsModel extends ContributableViewsModel {

S
Sandeep Somavarapu 已提交
491 492
	private readonly workspaceViewsStateStorageId: string;
	private readonly globalViewsStateStorageId: string;
493

494
	private storageService: IStorageService;
495 496

	constructor(
S
Sandeep Somavarapu 已提交
497
		container: ViewContainer,
498
		viewletStateStorageId: string,
499
		@IViewsService viewsService: IViewsService,
500
		@IStorageService storageService: IStorageService,
501
	) {
S
Sandeep Somavarapu 已提交
502 503
		const globalViewsStateStorageId = `${viewletStateStorageId}.hidden`;
		const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, globalViewsStateStorageId, storageService);
504

505
		super(container, viewsService, viewStates);
506

S
Sandeep Somavarapu 已提交
507 508
		this.workspaceViewsStateStorageId = viewletStateStorageId;
		this.globalViewsStateStorageId = globalViewsStateStorageId;
509
		this.storageService = storageService;
510

S
Sandeep Somavarapu 已提交
511 512 513 514 515 516
		this._register(Event.any(
			this.onDidAdd,
			this.onDidRemove,
			Event.map(this.onDidMove, ({ from, to }) => [from, to]),
			Event.map(this.onDidChangeViewState, viewDescriptorRef => [viewDescriptorRef]))
			(viewDescriptorRefs => this.saveViewsStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
517 518
	}

S
Sandeep Somavarapu 已提交
519
	private saveViewsStates(viewDescriptors: IViewDescriptor[]): void {
S
Sandeep Somavarapu 已提交
520 521 522 523 524
		this.saveWorkspaceViewsStates();
		this.saveGlobalViewsStates();
	}

	private saveWorkspaceViewsStates(): void {
525
		const storedViewsStates: { [id: string]: IStoredWorkspaceViewState } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
526 527 528
		for (const viewDescriptor of this.viewDescriptors) {
			const viewState = this.viewStates.get(viewDescriptor.id);
			if (viewState) {
S
Sandeep Somavarapu 已提交
529 530 531 532 533 534
				storedViewsStates[viewDescriptor.id] = {
					collapsed: !!viewState.collapsed,
					isHidden: !viewState.visibleWorkspace,
					size: viewState.size,
					order: viewDescriptor.workspace && viewState ? viewState.order : undefined
				};
535 536
			}
		}
B
Benjamin Pasero 已提交
537

538
		if (Object.keys(storedViewsStates).length > 0) {
S
Sandeep Somavarapu 已提交
539
			this.storageService.store(this.workspaceViewsStateStorageId, JSON.stringify(storedViewsStates), StorageScope.WORKSPACE);
B
Benjamin Pasero 已提交
540
		} else {
S
Sandeep Somavarapu 已提交
541
			this.storageService.remove(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE);
B
Benjamin Pasero 已提交
542
		}
543
	}
544

S
Sandeep Somavarapu 已提交
545 546 547 548 549 550 551 552 553
	private saveGlobalViewsStates(): void {
		const storedViewsVisibilityStates = PersistentContributableViewsModel.loadGlobalViewsState(this.globalViewsStateStorageId, this.storageService, StorageScope.GLOBAL);
		for (const viewDescriptor of this.viewDescriptors) {
			const viewState = this.viewStates.get(viewDescriptor.id);
			storedViewsVisibilityStates.set(viewDescriptor.id, {
				id: viewDescriptor.id,
				isHidden: viewState && viewDescriptor.canToggleVisibility ? !viewState.visibleGlobal : false,
				order: !viewDescriptor.workspace && viewState ? viewState.order : undefined
			});
S
Sandeep Somavarapu 已提交
554
		}
S
Sandeep Somavarapu 已提交
555
		this.storageService.store(this.globalViewsStateStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
S
Sandeep Somavarapu 已提交
556 557
	}

558

S
Sandeep Somavarapu 已提交
559
	private static loadViewsStates(workspaceViewsStateStorageId: string, globalViewsStateStorageId: string, storageService: IStorageService): Map<string, IViewState> {
560
		const viewStates = new Map<string, IViewState>();
S
Sandeep Somavarapu 已提交
561 562 563 564 565 566 567
		const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState }>JSON.parse(storageService.get(workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
		for (const id of Object.keys(workspaceViewsStates)) {
			const workspaceViewState = workspaceViewsStates[id];
			viewStates.set(id, {
				visibleGlobal: undefined,
				visibleWorkspace: isUndefined(workspaceViewState.isHidden) ? undefined : !workspaceViewState.isHidden,
				collapsed: workspaceViewState.collapsed,
S
Sandeep Somavarapu 已提交
568 569
				order: workspaceViewState.order,
				size: workspaceViewState.size
S
Sandeep Somavarapu 已提交
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
			});
		}

		// Migrate to `viewletStateStorageId`
		const workspaceVisibilityStates = this.loadGlobalViewsState(globalViewsStateStorageId, storageService, StorageScope.WORKSPACE);
		if (workspaceVisibilityStates.size > 0) {
			for (const { id, isHidden } of values(workspaceVisibilityStates)) {
				let viewState = viewStates.get(id);
				// Not migrated to `viewletStateStorageId`
				if (viewState) {
					if (isUndefined(viewState.visibleWorkspace)) {
						viewState.visibleWorkspace = !isHidden;
					}
				} else {
					viewStates.set(id, {
						collapsed: undefined,
						visibleGlobal: undefined,
						visibleWorkspace: !isHidden,
					});
				}
S
Sandeep Somavarapu 已提交
590
			}
S
Sandeep Somavarapu 已提交
591
			storageService.remove(globalViewsStateStorageId, StorageScope.WORKSPACE);
S
Sandeep Somavarapu 已提交
592
		}
S
Sandeep Somavarapu 已提交
593 594 595 596

		const globalViewsStates = this.loadGlobalViewsState(globalViewsStateStorageId, storageService, StorageScope.GLOBAL);
		for (const { id, isHidden, order } of values(globalViewsStates)) {
			let viewState = viewStates.get(id);
597
			if (viewState) {
S
Sandeep Somavarapu 已提交
598 599 600 601
				viewState.visibleGlobal = !isHidden;
				if (!isUndefined(order)) {
					viewState.order = order;
				}
S
Sandeep Somavarapu 已提交
602
			} else {
S
Sandeep Somavarapu 已提交
603 604 605 606 607 608
				viewStates.set(id, {
					visibleGlobal: !isHidden,
					order,
					collapsed: undefined,
					visibleWorkspace: undefined,
				});
609 610 611
			}
		}
		return viewStates;
612 613
	}

S
Sandeep Somavarapu 已提交
614 615
	private static loadGlobalViewsState(globalViewsStateStorageId: string, storageService: IStorageService, scope: StorageScope): Map<string, IStoredGlobalViewState> {
		const storedValue = <Array<string | IStoredGlobalViewState>>JSON.parse(storageService.get(globalViewsStateStorageId, scope, '[]'));
616
		let hasDuplicates = false;
S
Sandeep Somavarapu 已提交
617
		const storedGlobalViewsState = storedValue.reduce((result, storedState) => {
B
Benjamin Pasero 已提交
618
			if (typeof storedState === 'string' /* migration */) {
619
				hasDuplicates = hasDuplicates || result.has(storedState);
B
Benjamin Pasero 已提交
620 621
				result.set(storedState, { id: storedState, isHidden: true });
			} else {
622
				hasDuplicates = hasDuplicates || result.has(storedState.id);
B
Benjamin Pasero 已提交
623 624 625
				result.set(storedState.id, storedState);
			}
			return result;
S
Sandeep Somavarapu 已提交
626
		}, new Map<string, IStoredGlobalViewState>());
627 628

		if (hasDuplicates) {
S
Sandeep Somavarapu 已提交
629
			storageService.store(globalViewsStateStorageId, JSON.stringify(values(storedGlobalViewsState)), scope);
630 631
		}

S
Sandeep Somavarapu 已提交
632
		return storedGlobalViewsState;
S
Sandeep Somavarapu 已提交
633
	}
634
}
S
Sandeep Somavarapu 已提交
635

S
Sandeep Somavarapu 已提交
636
export class ViewsService extends Disposable implements IViewsService {
S
Sandeep Somavarapu 已提交
637

638
	_serviceBrand!: ServiceIdentifier<any>;
S
Sandeep Somavarapu 已提交
639

S
Sandeep Somavarapu 已提交
640
	private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable }>;
641
	private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
S
Sandeep Somavarapu 已提交
642
	private readonly activeViewContextKeys: Map<string, IContextKey<boolean>>;
643

S
Sandeep Somavarapu 已提交
644
	constructor(
645 646
		@IViewletService private readonly viewletService: IViewletService,
		@IContextKeyService private readonly contextKeyService: IContextKeyService
S
Sandeep Somavarapu 已提交
647
	) {
648
		super();
S
Sandeep Somavarapu 已提交
649

S
Sandeep Somavarapu 已提交
650
		this.viewDescriptorCollections = new Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable }>();
651
		this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
S
Sandeep Somavarapu 已提交
652 653
		this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();

654 655
		const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
		const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
656
		viewContainersRegistry.all.forEach(viewContainer => {
657
			this.onDidRegisterViews(viewContainer, viewsRegistry.getViews(viewContainer));
658 659
			this.onDidRegisterViewContainer(viewContainer);
		});
660 661 662
		this._register(viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views)));
		this._register(viewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views)));
		this._register(viewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); }));
663 664 665 666
		this._register(toDisposable(() => {
			this.viewDisposable.forEach(disposable => disposable.dispose());
			this.viewDisposable.clear();
		}));
S
Sandeep Somavarapu 已提交
667
		this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer)));
S
Sandeep Somavarapu 已提交
668 669 670 671 672
		this._register(viewContainersRegistry.onDidDeregister(viewContainer => this.onDidDeregisterViewContainer(viewContainer)));
		this._register(toDisposable(() => {
			this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose());
			this.viewDescriptorCollections.clear();
		}));
S
Sandeep Somavarapu 已提交
673 674
	}

S
Sandeep Somavarapu 已提交
675 676 677
	getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null {
		const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(container);
		return viewDescriptorCollectionItem ? viewDescriptorCollectionItem.viewDescriptorCollection : null;
678 679
	}

680
	async openView(id: string, focus: boolean): Promise<IView | null> {
681
		const viewContainer = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).getViewContainer(id);
682 683
		if (viewContainer) {
			const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id);
684
			if (viewletDescriptor) {
685 686 687 688
				const viewlet = await this.viewletService.openViewlet(viewletDescriptor.id, focus) as IViewsViewlet | null;
				if (viewlet && viewlet.openView) {
					return viewlet.openView(id, focus);
				}
689
			}
S
Sandeep Somavarapu 已提交
690
		}
691 692

		return null;
S
Sandeep Somavarapu 已提交
693 694
	}

S
Sandeep Somavarapu 已提交
695
	private onDidRegisterViewContainer(viewContainer: ViewContainer): void {
S
Sandeep Somavarapu 已提交
696 697
		const viewDescriptorCollection = new ViewDescriptorCollection(viewContainer, this.contextKeyService);
		const disposables: IDisposable[] = [viewDescriptorCollection];
S
Sandeep Somavarapu 已提交
698 699

		this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] });
S
Sandeep Somavarapu 已提交
700
		viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables);
S
Sandeep Somavarapu 已提交
701

S
Sandeep Somavarapu 已提交
702 703 704 705 706 707 708 709 710
		this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: toDisposable(() => dispose(disposables)) });
	}

	private onDidDeregisterViewContainer(viewContainer: ViewContainer): void {
		const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer);
		if (viewDescriptorCollectionItem) {
			viewDescriptorCollectionItem.disposable.dispose();
			this.viewDescriptorCollections.delete(viewContainer);
		}
S
Sandeep Somavarapu 已提交
711 712 713 714 715 716 717
	}

	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));
	}

718 719 720
	private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void {
		const viewlet = this.viewletService.getViewlet(container.id);
		for (const viewDescriptor of views) {
721
			const disposables: IDisposable[] = [];
S
Sandeep Somavarapu 已提交
722
			const command: ICommandAction = {
S
Sandeep Somavarapu 已提交
723
				id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`,
S
Sandeep Somavarapu 已提交
724
				title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) },
S
Sandeep Somavarapu 已提交
725 726 727 728
				category: viewlet ? viewlet.name : localize('view category', "View"),
			};
			const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`);

729
			disposables.push(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true)));
S
Sandeep Somavarapu 已提交
730

731
			disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
S
Sandeep Somavarapu 已提交
732 733
				command,
				when
734
			}));
S
Sandeep Somavarapu 已提交
735 736 737 738 739 740 741 742 743 744 745 746 747

			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
				});
			}
748 749 750 751 752 753 754 755 756 757 758 759

			this.viewDisposable.set(viewDescriptor, toDisposable(() => dispose(disposables)));
		}
	}

	private onDidDeregisterViews(views: IViewDescriptor[]): void {
		for (const view of views) {
			const disposable = this.viewDisposable.get(view);
			if (disposable) {
				disposable.dispose();
				this.viewDisposable.delete(view);
			}
S
Sandeep Somavarapu 已提交
760 761 762 763 764 765 766 767 768 769 770 771
		}
	}

	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;
	}
772
}
J
Joao Moreno 已提交
773 774 775 776 777 778 779 780 781 782 783 784

export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable {
	addClass(container, 'file-icon-themable-tree');
	addClass(container, 'show-file-icons');

	const onDidChangeFileIconTheme = (theme: IFileIconTheme) => {
		toggleClass(container, 'align-icons-and-twisties', theme.hasFileIcons && !theme.hasFolderIcons);
		toggleClass(container, 'hide-arrows', theme.hidesExplorerArrows === true);
	};

	onDidChangeFileIconTheme(themeService.getFileIconTheme());
	return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme);
785 786
}

S
Sandeep Somavarapu 已提交
787
registerSingleton(IViewsService, ViewsService);