scmViewlet.ts 12.2 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/scmViewlet';
J
Joao Moreno 已提交
7
import { localize } from 'vs/nls';
J
Joao Moreno 已提交
8
import { Event, Emitter } from 'vs/base/common/event';
J
Joao Moreno 已提交
9
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
10
import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
J
Joao Moreno 已提交
11
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
J
Joao Moreno 已提交
12
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
J
Joao Moreno 已提交
13
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
J
Joao Moreno 已提交
14
import { ICommandService } from 'vs/platform/commands/common/commands';
J
Joao Moreno 已提交
15
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
J
Joao Moreno 已提交
16 17 18 19 20
import { MenuItemAction } from 'vs/platform/actions/common/actions';
import { IAction, IActionViewItem } from 'vs/base/common/actions';
import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { SCMMenus } from './menus';
import { IThemeService } from 'vs/platform/theme/common/themeService';
B
Benjamin Pasero 已提交
21
import { IStorageService } from 'vs/platform/storage/common/storage';
J
Joao Moreno 已提交
22
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
23
import { INotificationService } from 'vs/platform/notification/common/notification';
24
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
J
Joao Moreno 已提交
25 26
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
27
import { IViewsRegistry, Extensions, IViewDescriptorService, IViewDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/common/views';
28
import { Registry } from 'vs/platform/registry/common/platform';
29
import { RepositoryPane, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPane';
J
Joao Moreno 已提交
30
import { MainPaneDescriptor, MainPane, IViewModel } from 'vs/workbench/contrib/scm/browser/mainPane';
J
Joao Moreno 已提交
31
import { ViewPaneContainer, IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
J
Joao Moreno 已提交
32
import { debounce } from 'vs/base/common/decorators';
J
Joao Moreno 已提交
33 34 35
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { addClass } from 'vs/base/browser/dom';
36

37 38 39 40 41
export interface ISpliceEvent<T> {
	index: number;
	deleteCount: number;
	elements: T[];
}
J
Joao Moreno 已提交
42

J
Joao Moreno 已提交
43 44
export class EmptyPane extends ViewPane {

J
Joao Moreno 已提交
45
	static readonly ID = 'workbench.scm';
J
Joao Moreno 已提交
46
	static readonly TITLE = localize('scm', "Source Control");
J
Joao Moreno 已提交
47 48 49 50 51 52 53 54 55 56 57

	constructor(
		options: IViewPaneOptions,
		@IKeybindingService keybindingService: IKeybindingService,
		@IContextMenuService contextMenuService: IContextMenuService,
		@IConfigurationService configurationService: IConfigurationService,
		@IContextKeyService contextKeyService: IContextKeyService,
		@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
		@IInstantiationService instantiationService: IInstantiationService,
		@IOpenerService openerService: IOpenerService,
		@IThemeService themeService: IThemeService,
58
		@ITelemetryService telemetryService: ITelemetryService,
J
Joao Moreno 已提交
59
	) {
60
		super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
J
Joao Moreno 已提交
61 62
	}

J
Joao Moreno 已提交
63
	shouldShowWelcome(): boolean {
J
Joao Moreno 已提交
64 65 66 67 68 69 70
		return true;
	}
}

export class EmptyPaneDescriptor implements IViewDescriptor {
	readonly id = EmptyPane.ID;
	readonly name = EmptyPane.TITLE;
71
	readonly containerIcon = 'codicon-source-control';
J
Joao Moreno 已提交
72 73 74 75 76 77 78 79
	readonly ctorDescriptor = new SyncDescriptor(EmptyPane);
	readonly canToggleVisibility = true;
	readonly hideByDefault = false;
	readonly order = -1000;
	readonly workspace = true;
	readonly when = ContextKeyExpr.equals('scm.providerCount', 0);
}

S
SteVen Batten 已提交
80
export class SCMViewPaneContainer extends ViewPaneContainer implements IViewModel {
J
Joao Moreno 已提交
81

J
Joao 已提交
82
	private menus: SCMMenus;
83
	private _repositories: ISCMRepository[] = [];
J
Joao Moreno 已提交
84

J
Joao Moreno 已提交
85
	private repositoryCountKey: IContextKey<number>;
J
Joao Moreno 已提交
86
	private viewDescriptors: RepositoryViewDescriptor[] = [];
J
Joao Moreno 已提交
87

88
	private readonly _onDidSplice = new Emitter<ISpliceEvent<ISCMRepository>>();
89 90
	readonly onDidSplice: Event<ISpliceEvent<ISCMRepository>> = this._onDidSplice.event;

J
Joao Moreno 已提交
91 92 93
	private _height: number | undefined = undefined;
	get height(): number | undefined { return this._height; }

J
Joao Moreno 已提交
94 95 96 97
	get repositories(): ISCMRepository[] {
		return this._repositories;
	}

J
Joao Moreno 已提交
98
	get visibleRepositories(): ISCMRepository[] {
99 100
		return this.panes.filter(pane => pane instanceof RepositoryPane)
			.map(pane => (pane as RepositoryPane).repository);
J
Joao Moreno 已提交
101 102
	}

J
Joao Moreno 已提交
103
	get onDidChangeVisibleRepositories(): Event<ISCMRepository[]> {
104
		const modificationEvent = Event.debounce(Event.any(this.viewContainerModel.onDidAddVisibleViewDescriptors, this.viewContainerModel.onDidRemoveVisibleViewDescriptors), () => null, 0);
J
Joao Moreno 已提交
105
		return Event.map(modificationEvent, () => this.visibleRepositories);
J
Joao Moreno 已提交
106
	}
107

J
Joao Moreno 已提交
108
	constructor(
109
		@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
J
Joao Moreno 已提交
110 111
		@ITelemetryService telemetryService: ITelemetryService,
		@ISCMService protected scmService: ISCMService,
112
		@IInstantiationService protected instantiationService: IInstantiationService,
J
Joao Moreno 已提交
113 114
		@IContextViewService protected contextViewService: IContextViewService,
		@IKeybindingService protected keybindingService: IKeybindingService,
115
		@INotificationService protected notificationService: INotificationService,
S
Sandeep Somavarapu 已提交
116
		@IContextMenuService protected contextMenuService: IContextMenuService,
J
Joao Moreno 已提交
117 118
		@IThemeService protected themeService: IThemeService,
		@ICommandService protected commandService: ICommandService,
B
Benjamin Pasero 已提交
119
		@IStorageService storageService: IStorageService,
J
Joao Moreno 已提交
120 121 122
		@IConfigurationService configurationService: IConfigurationService,
		@IExtensionService extensionService: IExtensionService,
		@IWorkspaceContextService protected contextService: IWorkspaceContextService,
J
Joao Moreno 已提交
123
		@IContextKeyService contextKeyService: IContextKeyService,
S
Sandeep Somavarapu 已提交
124
		@IViewDescriptorService viewDescriptorService: IViewDescriptorService
J
Joao Moreno 已提交
125
	) {
126
		super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
J
Joao 已提交
127 128

		this.menus = instantiationService.createInstance(SCMMenus, undefined);
M
Matt Bierner 已提交
129
		this._register(this.menus.onDidChangeTitle(this.updateTitleArea, this));
130

J
Joao Moreno 已提交
131
		const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
J
Joao Moreno 已提交
132

J
Joao Moreno 已提交
133
		viewsRegistry.registerViewWelcomeContent(EmptyPane.ID, {
J
Joao Moreno 已提交
134
			content: localize('no open repo', "No source control providers registered."),
J
Joao Moreno 已提交
135
			when: 'default'
J
Joao Moreno 已提交
136 137 138
		});

		viewsRegistry.registerViews([new EmptyPaneDescriptor()], this.viewContainer);
139
		viewsRegistry.registerViews([new MainPaneDescriptor(this)], this.viewContainer);
J
Joao Moreno 已提交
140

M
Matt Bierner 已提交
141
		this._register(configurationService.onDidChangeConfiguration(e => {
J
Joao Moreno 已提交
142
			if (e.affectsConfiguration('scm.alwaysShowProviders') && configurationService.getValue<boolean>('scm.alwaysShowProviders')) {
143
				this.viewContainerModel.setVisible(MainPane.ID, true);
J
Joao Moreno 已提交
144
			}
M
Matt Bierner 已提交
145
		}));
J
Joao Moreno 已提交
146

J
Joao Moreno 已提交
147
		this.repositoryCountKey = contextKeyService.createKey('scm.providerCount', 0);
J
Joao Moreno 已提交
148

149 150
		this._register(this.viewContainerModel.onDidAddVisibleViewDescriptors(this.onDidShowView, this));
		this._register(this.viewContainerModel.onDidRemoveVisibleViewDescriptors(this.onDidHideView, this));
J
Joao Moreno 已提交
151 152
	}

I
isidor 已提交
153 154
	create(parent: HTMLElement): void {
		super.create(parent);
J
Joao Moreno 已提交
155
		addClass(parent, 'scm-viewlet');
M
Matt Bierner 已提交
156 157
		this._register(this.scmService.onDidAddRepository(this.onDidAddRepository, this));
		this._register(this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this));
I
isidor 已提交
158
		this.scmService.repositories.forEach(r => this.onDidAddRepository(r));
159 160
	}

161 162 163
	private onDidAddRepository(repository: ISCMRepository): void {
		const index = this._repositories.length;
		this._repositories.push(repository);
J
Joao Moreno 已提交
164

J
Joao Moreno 已提交
165
		const viewDescriptor = new RepositoryViewDescriptor(repository, false);
166
		Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).registerViews([viewDescriptor], this.viewContainer);
J
Joao Moreno 已提交
167
		this.viewDescriptors.push(viewDescriptor);
J
Joao Moreno 已提交
168

169
		this._onDidSplice.fire({ index, deleteCount: 0, elements: [repository] });
J
Joao Moreno 已提交
170
		this.updateTitleArea();
171

J
Joao Moreno 已提交
172
		this.onDidChangeRepositories();
173 174 175 176 177 178 179 180 181
	}

	private onDidRemoveRepository(repository: ISCMRepository): void {
		const index = this._repositories.indexOf(repository);

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

182
		Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).deregisterViews([this.viewDescriptors[index]], this.viewContainer);
J
Joao Moreno 已提交
183

184
		this._repositories.splice(index, 1);
J
Joao Moreno 已提交
185 186
		this.viewDescriptors.splice(index, 1);

187
		this._onDidSplice.fire({ index, deleteCount: 1, elements: [] });
J
Joao Moreno 已提交
188
		this.updateTitleArea();
189

J
Joao Moreno 已提交
190
		this.onDidChangeRepositories();
191 192 193
	}

	private onDidChangeRepositories(): void {
J
Joao Moreno 已提交
194
		this.repositoryCountKey.set(this.repositories.length);
J
Joao Moreno 已提交
195
	}
J
Joao Moreno 已提交
196

J
Joao Moreno 已提交
197 198 199 200 201 202 203 204
	private onDidShowView(e: IAddedViewDescriptorRef[]): void {
		for (const ref of e) {
			if (ref.viewDescriptor instanceof RepositoryViewDescriptor) {
				ref.viewDescriptor.repository.setSelected(true);
			}
		}
	}

J
Joao Moreno 已提交
205
	private onDidHideView(e: IViewDescriptorRef[]): void {
J
Joao Moreno 已提交
206 207 208
		for (const ref of e) {
			if (ref.viewDescriptor instanceof RepositoryViewDescriptor) {
				ref.viewDescriptor.repository.setSelected(false);
J
Joao Moreno 已提交
209
			}
J
Joao Moreno 已提交
210 211 212 213 214 215 216
		}

		this.afterOnDidHideView();
	}

	@debounce(0)
	private afterOnDidHideView(): void {
217 218
		if (this.repositoryCountKey.get()! > 0 && this.viewDescriptors.every(d => !this.viewContainerModel.isVisible(d.id))) {
			this.viewContainerModel.setVisible(this.viewDescriptors[0].id, true);
J
Joao Moreno 已提交
219
		}
220 221
	}

J
Joao Moreno 已提交
222
	focus(): void {
J
Joao Moreno 已提交
223
		const repository = this.visibleRepositories[0];
J
Joao Moreno 已提交
224

J
Joao Moreno 已提交
225 226 227
		if (repository) {
			const pane = this.panes
				.filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined;
228

J
Joao Moreno 已提交
229 230
			if (pane) {
				pane.focus();
J
Joao Moreno 已提交
231 232 233
			} else {
				super.focus();
			}
J
Joao Moreno 已提交
234 235
		} else {
			super.focus();
236
		}
J
Joao Moreno 已提交
237 238
	}

J
Joao Moreno 已提交
239 240 241 242
	getOptimalWidth(): number {
		return 400;
	}

243 244 245
	getTitle(): string {
		const title = localize('source control', "Source Control");

J
Joao Moreno 已提交
246
		if (this.visibleRepositories.length === 1) {
J
Joao Moreno 已提交
247 248 249 250 251
			const [repository] = this.repositories;
			return localize('viewletTitle', "{0}: {1}", title, repository.provider.label);
		} else {
			return title;
		}
252 253
	}

254
	getActionViewItem(action: IAction): IActionViewItem | undefined {
255
		if (!(action instanceof MenuItemAction)) {
256
			return undefined;
257 258
		}

259
		return new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
J
Joao Moreno 已提交
260 261
	}

J
Joao Moreno 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	getActions(): IAction[] {
		if (this.repositories.length > 0) {
			return super.getActions();
		}

		return this.menus.getTitleActions();
	}

	getSecondaryActions(): IAction[] {
		if (this.repositories.length > 0) {
			return super.getSecondaryActions();
		}

		return this.menus.getTitleSecondaryActions();
	}

J
Joao Moreno 已提交
278 279 280
	getActionsContext(): any {
		if (this.visibleRepositories.length === 1) {
			return this.repositories[0].provider;
281
		}
J
Joao Moreno 已提交
282
	}
J
Joao Moreno 已提交
283 284

	setVisibleRepositories(repositories: ISCMRepository[]): void {
285
		const visibleViewDescriptors = this.viewContainerModel.visibleViewDescriptors;
J
Joao Moreno 已提交
286

287
		const toSetVisible = this.viewContainerModel.activeViewDescriptors
J
Joao Moreno 已提交
288 289 290 291 292 293 294 295 296 297
			.filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) > -1 && visibleViewDescriptors.indexOf(d) === -1);

		const toSetInvisible = visibleViewDescriptors
			.filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) === -1);

		let size: number | undefined;
		const oneToOne = toSetVisible.length === 1 && toSetInvisible.length === 1;

		for (const viewDescriptor of toSetInvisible) {
			if (oneToOne) {
298
				const pane = this.panes.filter(pane => pane.id === viewDescriptor.id)[0];
J
Joao Moreno 已提交
299

300 301
				if (pane) {
					size = this.getPaneSize(pane);
J
Joao Moreno 已提交
302 303 304
				}
			}

305
			this.viewContainerModel.setVisible(viewDescriptor.id, false);
J
Joao Moreno 已提交
306 307 308
		}

		for (const viewDescriptor of toSetVisible) {
309
			this.viewContainerModel.setVisible(viewDescriptor.id, true, size);
J
Joao Moreno 已提交
310 311
		}
	}
J
Joao Moreno 已提交
312
}