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

'use strict';

8 9 10 11
import { readFile } from 'vs/base/node/pfs';
import { asText } from 'vs/base/node/request';
import * as semver from 'semver';
import * as path from 'path';
12
import Event, { Emitter, chain } from 'vs/base/common/event';
13
import { index } from 'vs/base/common/arrays';
S
Sandeep Somavarapu 已提交
14
import { LinkedMap as Map } from 'vs/base/common/map';
J
Joao Moreno 已提交
15
import { assign } from 'vs/base/common/objects';
J
Joao Moreno 已提交
16
import { isUUID } from 'vs/base/common/uuid';
17
import { ThrottledDelayer } from 'vs/base/common/async';
J
Joao Moreno 已提交
18
import { isPromiseCanceledError } from 'vs/base/common/errors';
J
Joao Moreno 已提交
19 20
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
21
import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging';
J
Joao Moreno 已提交
22
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
J
Johannes Rieken 已提交
23 24
import {
	IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest,
S
Sandeep Somavarapu 已提交
25
	InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent
J
Johannes Rieken 已提交
26
} from 'vs/platform/extensionManagement/common/extensionManagement';
27
import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionTelemetry';
J
Joao Moreno 已提交
28 29
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
30
import { IMessageService } from 'vs/platform/message/common/message';
J
Joao Moreno 已提交
31
import Severity from 'vs/base/common/severity';
J
João Moreno 已提交
32
import URI from 'vs/base/common/uri';
33
import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
S
Sandeep Somavarapu 已提交
34
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
35
import { IURLService } from 'vs/platform/url/common/url';
36
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
37
import { IExtensionRuntimeService } from 'vs/platform/extensions/common/extensions';
38
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
J
Joao Moreno 已提交
39

J
Joao Moreno 已提交
40 41 42 43
interface IExtensionStateProvider {
	(extension: Extension): ExtensionState;
}

J
Joao Moreno 已提交
44 45
class Extension implements IExtension {

46
	public needsReload = false;
J
Joao Moreno 已提交
47

J
Joao Moreno 已提交
48
	constructor(
J
Joao Moreno 已提交
49
		private galleryService: IExtensionGalleryService,
J
Joao Moreno 已提交
50
		private stateProvider: IExtensionStateProvider,
J
Joao Moreno 已提交
51 52
		public local: ILocalExtension,
		public gallery: IGalleryExtension = null
J
Johannes Rieken 已提交
53
	) { }
J
Joao Moreno 已提交
54

J
Joao Moreno 已提交
55 56 57 58
	get type(): LocalExtensionType {
		return this.local ? this.local.type : null;
	}

J
Joao Moreno 已提交
59 60 61 62
	get name(): string {
		return this.local ? this.local.manifest.name : this.gallery.name;
	}

J
Joao Moreno 已提交
63 64 65 66 67 68 69 70
	get displayName(): string {
		if (this.local) {
			return this.local.manifest.displayName || this.local.manifest.name;
		}

		return this.gallery.displayName || this.gallery.name;
	}

71 72 73 74
	get identifier(): string {
		return `${this.publisher}.${this.name}`;
	}

J
Joao Moreno 已提交
75
	get publisher(): string {
J
Joao Moreno 已提交
76 77 78 79
		return this.local ? this.local.manifest.publisher : this.gallery.publisher;
	}

	get publisherDisplayName(): string {
J
Joao Moreno 已提交
80 81 82 83 84 85 86 87 88 89 90 91
		if (this.local) {
			if (this.local.metadata && this.local.metadata.publisherDisplayName) {
				return this.local.metadata.publisherDisplayName;
			}

			return this.local.manifest.publisher;
		}

		return this.gallery.publisherDisplayName || this.gallery.publisher;
	}

	get version(): string {
92
		return this.local ? this.local.manifest.version : this.gallery.version;
J
Joao Moreno 已提交
93 94
	}

J
Joao Moreno 已提交
95
	get latestVersion(): string {
96
		return this.gallery ? this.gallery.version : this.local.manifest.version;
J
Joao Moreno 已提交
97 98
	}

J
Joao Moreno 已提交
99 100 101 102
	get description(): string {
		return this.local ? this.local.manifest.description : this.gallery.description;
	}

103
	private get readmeUrl(): string {
J
Joao Moreno 已提交
104 105 106 107
		if (this.local && this.local.readmeUrl) {
			return this.local.readmeUrl;
		}

108
		return this.gallery && this.gallery.assets.readme;
J
Joao Moreno 已提交
109 110
	}

111
	private get changelogUrl(): string {
112 113 114 115
		if (this.local && this.local.changelogUrl) {
			return this.local.changelogUrl;
		}

116
		return this.gallery && this.gallery.assets.changelog;
117 118
	}

J
Joao Moreno 已提交
119
	get iconUrl(): string {
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
		return this.localIconUrl || this.galleryIconUrl || this.defaultIconUrl;
	}

	get iconUrlFallback(): string {
		return this.localIconUrl || this.galleryIconUrlFallback || this.defaultIconUrl;
	}

	private get localIconUrl(): string {
		return this.local && this.local.manifest.icon
			&& URI.file(path.join(this.local.path, this.local.manifest.icon)).toString();
	}

	private get galleryIconUrl(): string {
		return this.gallery && this.gallery.assets.icon;
	}

	private get galleryIconUrlFallback(): string {
		return this.gallery && this.gallery.assets.iconFallback;
	}
J
Joao Moreno 已提交
139

140 141
	private get defaultIconUrl(): string {
		return require.toUrl('./media/defaultIcon.png');
J
Joao Moreno 已提交
142
	}
J
Joao Moreno 已提交
143

144
	get licenseUrl(): string {
145
		return this.gallery && this.gallery.assets.license;
146 147
	}

J
Joao Moreno 已提交
148 149 150
	get state(): ExtensionState {
		return this.stateProvider(this);
	}
J
Joao Moreno 已提交
151 152 153 154 155 156 157 158 159 160 161 162

	get installCount(): number {
		return this.gallery ? this.gallery.installCount : null;
	}

	get rating(): number {
		return this.gallery ? this.gallery.rating : null;
	}

	get ratingCount(): number {
		return this.gallery ? this.gallery.ratingCount : null;
	}
J
Joao Moreno 已提交
163

164
	get outdated(): boolean {
165
		return this.gallery && this.type === LocalExtensionType.User && semver.gt(this.latestVersion, this.version);
J
Joao Moreno 已提交
166
	}
167

168
	get reload(): boolean {
169
		return this.needsReload;
170 171
	}

172 173 174 175
	get telemetryData(): any {
		const { local, gallery } = this;

		if (gallery) {
176
			return getGalleryExtensionTelemetryData(gallery);
177
		} else {
178
			return getLocalExtensionTelemetryData(local);
179 180
		}
	}
J
Joao Moreno 已提交
181 182 183 184 185 186 187 188 189 190 191 192

	getManifest(): TPromise<IExtensionManifest> {
		if (this.local) {
			return TPromise.as(this.local.manifest);
		}

		return this.galleryService.getAsset(this.gallery.assets.manifest)
			.then(asText)
			.then(raw => JSON.parse(raw) as IExtensionManifest);
	}

	getReadme(): TPromise<string> {
193
		const readmeUrl = this.readmeUrl;
J
Joao Moreno 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206

		if (!readmeUrl) {
			return TPromise.wrapError('not available');
		}

		const uri = URI.parse(readmeUrl);

		if (uri.scheme === 'file') {
			return readFile(uri.fsPath, 'utf8');
		}

		return this.galleryService.getAsset(readmeUrl).then(asText);
	}
207

J
Johannes Rieken 已提交
208
	get hasChangelog(): boolean {
209
		return !!(this.changelogUrl);
210 211
	}

J
Johannes Rieken 已提交
212
	getChangelog(): TPromise<string> {
213
		const changelogUrl = this.changelogUrl;
214 215 216 217 218 219 220 221 222 223 224 225 226

		if (!changelogUrl) {
			return TPromise.wrapError('not available');
		}

		const uri = URI.parse(changelogUrl);

		if (uri.scheme === 'file') {
			return readFile(uri.fsPath, 'utf8');
		}

		return TPromise.wrapError('not available');
	}
S
Sandeep Somavarapu 已提交
227

228
	get dependencies(): string[] {
S
Sandeep Somavarapu 已提交
229 230
		const { local, gallery } = this;
		if (gallery) {
231 232
			return gallery.properties.dependencies;
		}
S
Sandeep Somavarapu 已提交
233 234
		if (local && local.manifest.extensionDependencies) {
			return local.manifest.extensionDependencies;
S
Sandeep Somavarapu 已提交
235
		}
236
		return [];
S
Sandeep Somavarapu 已提交
237 238 239 240 241
	}
}

class ExtensionDependencies implements IExtensionDependencies {

S
Sandeep Somavarapu 已提交
242
	constructor(private _extension: IExtension, private _identifier: string, private _map: Map<string, IExtension>, private _dependent: IExtensionDependencies = null) { }
S
Sandeep Somavarapu 已提交
243 244

	get hasDependencies(): boolean {
245
		return this._extension ? this._extension.dependencies.length > 0 : false;
S
Sandeep Somavarapu 已提交
246 247 248 249 250 251
	}

	get extension(): IExtension {
		return this._extension;
	}

252 253 254 255
	get identifier(): string {
		return this._identifier;
	}

S
Sandeep Somavarapu 已提交
256 257 258 259 260
	get dependent(): IExtensionDependencies {
		return this._dependent;
	}

	get dependencies(): IExtensionDependencies[] {
261
		return this._extension.dependencies.map(d => new ExtensionDependencies(this._map.get(d), d, this._map, this));
S
Sandeep Somavarapu 已提交
262
	}
J
Joao Moreno 已提交
263 264
}

265 266 267 268
function stripVersion(id: string): string {
	return id.replace(/-\d+\.\d+\.\d+$/, '');
}

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
enum Operation {
	Installing,
	Updating,
	Uninstalling
}

interface IActiveExtension {
	id: string;
	operation: Operation;
	extension: Extension;
	start: Date;
}

function toTelemetryEventName(operation: Operation) {
	switch (operation) {
		case Operation.Installing: return 'extensionGallery:install';
		case Operation.Updating: return 'extensionGallery:update';
		case Operation.Uninstalling: return 'extensionGallery:uninstall';
	}

	return '';
}

292
export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
J
Joao Moreno 已提交
293

294
	private static SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
J
Joao Moreno 已提交
295

296
	_serviceBrand: any;
J
Joao Moreno 已提交
297
	private stateProvider: IExtensionStateProvider;
298 299
	private installing: IActiveExtension[] = [];
	private uninstalling: IActiveExtension[] = [];
300
	private installed: Extension[] = [];
301
	private syncDelayer: ThrottledDelayer<void>;
J
Joao Moreno 已提交
302
	private autoUpdateDelayer: ThrottledDelayer<void>;
303
	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
304

305
	// TODO: @sandy - Remove these when IExtensionRuntimeService exposes sync API to get extensions.
S
Sandeep Somavarapu 已提交
306 307 308
	private newlyInstalled: Extension[] = [];
	private unInstalled: Extension[] = [];

J
Joao Moreno 已提交
309
	private _onChange: Emitter<void> = new Emitter<void>();
J
Joao Moreno 已提交
310 311 312
	get onChange(): Event<void> { return this._onChange.event; }

	constructor(
J
Joao Moreno 已提交
313
		@IInstantiationService private instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
314
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
J
Joao Moreno 已提交
315
		@IExtensionManagementService private extensionService: IExtensionManagementService,
J
Joao Moreno 已提交
316
		@IExtensionGalleryService private galleryService: IExtensionGalleryService,
J
Joao Moreno 已提交
317
		@IConfigurationService private configurationService: IConfigurationService,
J
Joao Moreno 已提交
318
		@ITelemetryService private telemetryService: ITelemetryService,
319
		@IMessageService private messageService: IMessageService,
320
		@IURLService urlService: IURLService,
321
		@IExtensionRuntimeService private extensionRuntimeService: IExtensionRuntimeService,
322
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
J
Joao Moreno 已提交
323
	) {
J
Joao Moreno 已提交
324
		this.stateProvider = ext => this.getExtensionState(ext);
325

326 327 328 329
		extensionService.onInstallExtension(this.onInstallExtension, this, this.disposables);
		extensionService.onDidInstallExtension(this.onDidInstallExtension, this, this.disposables);
		extensionService.onUninstallExtension(this.onUninstallExtension, this, this.disposables);
		extensionService.onDidUninstallExtension(this.onDidUninstallExtension, this, this.disposables);
330

331
		this.syncDelayer = new ThrottledDelayer<void>(ExtensionsWorkbenchService.SyncPeriod);
J
Joao Moreno 已提交
332
		this.autoUpdateDelayer = new ThrottledDelayer<void>(1000);
333

334 335 336 337
		chain(urlService.onOpenURL)
			.filter(uri => /^extension/.test(uri.path))
			.on(this.onOpenExtensionUrl, this, this.disposables);

J
Joao Moreno 已提交
338
		this.queryLocal().done(() => this.eventuallySyncWithGallery(true));
J
Joao Moreno 已提交
339 340
	}

341 342
	get local(): IExtension[] {
		const installing = this.installing
343
			.filter(e => !this.installed.some(installed => stripVersion(installed.local.id) === e.id))
344 345 346 347 348 349
			.map(e => e.extension);

		return [...this.installed, ...installing];
	}

	queryLocal(): TPromise<IExtension[]> {
350 351 352 353
		return this.extensionService.getInstalled().then(result => {
			const installedById = index(this.installed, e => e.local.id);

			this.installed = result.map(local => {
J
Joao Moreno 已提交
354
				const extension = installedById[local.id] || new Extension(this.galleryService, this.stateProvider, local);
355 356 357 358
				extension.local = local;
				return extension;
			});

J
Joao Moreno 已提交
359
			this._onChange.fire();
360
			return this.local;
361
		});
J
Joao Moreno 已提交
362 363 364
	}

	queryGallery(options: IQueryOptions = {}): TPromise<IPager<IExtension>> {
J
Joao Moreno 已提交
365 366 367 368 369
		return this.galleryService.query(options)
			.then(result => mapPager(result, gallery => this.fromGallery(gallery)))
			.then(null, err => {
				if (/No extension gallery service configured/.test(err.message)) {
					return TPromise.as(singlePagePager([]));
370 371
				}

J
Joao Moreno 已提交
372
				return TPromise.wrapError(err);
373
			});
J
Joao Moreno 已提交
374
	}
375

S
Sandeep Somavarapu 已提交
376
	loadDependencies(extension: IExtension): TPromise<IExtensionDependencies> {
377
		if (!extension.dependencies.length) {
S
Sandeep Somavarapu 已提交
378 379 380 381 382
			return TPromise.wrap(null);
		}

		return this.galleryService.getAllDependencies((<Extension>extension).gallery)
			.then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension)))
S
Sandeep Somavarapu 已提交
383
			.then(extensions => [...this.local, ...extensions])
S
Sandeep Somavarapu 已提交
384
			.then(extensions => {
S
Sandeep Somavarapu 已提交
385
				const map = new Map<string, IExtension>();
S
Sandeep Somavarapu 已提交
386 387 388
				for (const extension of extensions) {
					map.set(`${extension.publisher}.${extension.name}`, extension);
				}
389
				return new ExtensionDependencies(extension, extension.identifier, map);
S
Sandeep Somavarapu 已提交
390 391 392
			});
	}

393 394
	open(extension: IExtension, sideByside: boolean = false): TPromise<any> {
		return this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), null, sideByside);
395 396
	}

J
Joao Moreno 已提交
397 398 399 400 401 402
	private fromGallery(gallery: IGalleryExtension): Extension {
		const installedByGalleryId = index(this.installed, e => e.local.metadata ? e.local.metadata.id : '');
		const id = gallery.id;
		const installed = installedByGalleryId[id];

		if (installed) {
S
Sandeep Somavarapu 已提交
403 404 405 406 407 408 409
			// Loading the compatible version only there is an engine property
			// Otherwise falling back to old way so that we will not make many roundtrips
			if (gallery.properties.engine) {
				this.galleryService.loadCompatibleVersion(gallery).then(compatible => this.syncLocalWithGalleryExtension(installed, compatible));
			} else {
				this.syncLocalWithGalleryExtension(installed, gallery);
			}
J
Joao Moreno 已提交
410 411 412
			return installed;
		}

J
Joao Moreno 已提交
413
		return new Extension(this.galleryService, this.stateProvider, null, gallery);
J
Joao Moreno 已提交
414 415
	}

S
Sandeep Somavarapu 已提交
416 417 418 419 420 421
	private syncLocalWithGalleryExtension(local: Extension, gallery: IGalleryExtension) {
		local.gallery = gallery;
		this._onChange.fire();
		this.eventuallyAutoUpdateExtensions();
	}

J
Joao Moreno 已提交
422 423
	private eventuallySyncWithGallery(immediate = false): void {
		const loop = () => this.syncWithGallery().then(() => this.eventuallySyncWithGallery());
424
		const delay = immediate ? 0 : ExtensionsWorkbenchService.SyncPeriod;
425

J
Joao Moreno 已提交
426
		this.syncDelayer.trigger(loop, delay)
J
Joao Moreno 已提交
427
			.done(null, err => null);
428 429
	}

J
Joao Moreno 已提交
430
	private syncWithGallery(): TPromise<void> {
431 432
		const ids = this.installed
			.filter(e => !!(e.local && e.local.metadata))
J
Joao Moreno 已提交
433 434
			.map(e => e.local.metadata.id)
			.filter(id => isUUID(id));
435 436 437 438 439

		if (ids.length === 0) {
			return TPromise.as(null);
		}

J
Joao Moreno 已提交
440 441
		return this.queryGallery({ ids, pageSize: ids.length }) as TPromise<any>;
	}
J
Joao Moreno 已提交
442

J
Joao Moreno 已提交
443 444
	private eventuallyAutoUpdateExtensions(): void {
		this.autoUpdateDelayer.trigger(() => this.autoUpdateExtensions())
J
Joao Moreno 已提交
445
			.done(null, err => null);
J
Joao Moreno 已提交
446
	}
J
Joao Moreno 已提交
447

448
	private autoUpdateExtensions(): TPromise<any> {
J
Joao Moreno 已提交
449 450 451 452 453 454
		const config = this.configurationService.getConfiguration<IExtensionsConfiguration>(ConfigurationKey);

		if (!config.autoUpdate) {
			return TPromise.as(null);
		}

455 456
		const toUpdate = this.local.filter(e => e.outdated && (e.state !== ExtensionState.Installing));
		return TPromise.join(toUpdate.map(e => this.install(e, false)));
457 458
	}

J
Joao Moreno 已提交
459 460 461 462 463 464 465 466
	canInstall(extension: IExtension): boolean {
		if (!(extension instanceof Extension)) {
			return;
		}

		return !!(extension as Extension).gallery;
	}

S
Sandeep Somavarapu 已提交
467
	install(extension: string | IExtension, promptToInstallDependencies: boolean = true): TPromise<void> {
J
Joao Moreno 已提交
468 469 470 471
		if (typeof extension === 'string') {
			return this.extensionService.install(extension);
		}

J
Joao Moreno 已提交
472 473 474 475 476
		if (!(extension instanceof Extension)) {
			return;
		}

		const ext = extension as Extension;
J
Joao Moreno 已提交
477
		const gallery = ext.gallery;
J
Joao Moreno 已提交
478

J
Joao Moreno 已提交
479 480
		if (!gallery) {
			return TPromise.wrapError<void>(new Error('Missing gallery'));
J
Joao Moreno 已提交
481 482
		}

483
		return this.extensionService.installFromGallery(gallery, promptToInstallDependencies);
J
Joao Moreno 已提交
484 485
	}

486
	setEnablement(extension: IExtension, enable: boolean, workspace: boolean = false): TPromise<any> {
487 488 489 490
		if (extension.type === LocalExtensionType.System) {
			return TPromise.wrap(null);
		}

491
		return this.doSetEnablement(extension, enable, workspace).then(reload => {
492
			(<Extension>extension).needsReload = reload;
493 494
			this.telemetryService.publicLog(enable ? 'extension:enable' : 'extension:disable', extension.telemetryData);
			this._onChange.fire();
S
Sandeep Somavarapu 已提交
495 496 497
		});
	}

J
Joao Moreno 已提交
498 499 500 501 502 503
	uninstall(extension: IExtension): TPromise<void> {
		if (!(extension instanceof Extension)) {
			return;
		}

		const ext = extension as Extension;
J
Joao Moreno 已提交
504
		const local = ext.local || this.installed.filter(e => e.local.metadata && ext.gallery && e.local.metadata.id === ext.gallery.id)[0].local;
J
Joao Moreno 已提交
505 506 507 508 509 510 511 512

		if (!local) {
			return TPromise.wrapError<void>(new Error('Missing local'));
		}

		return this.extensionService.uninstall(local);
	}

513 514
	private doSetEnablement(extension: IExtension, enable: boolean, workspace: boolean): TPromise<boolean> {
		if (workspace) {
515
			return this.extensionRuntimeService.setEnablement(extension.identifier, enable, workspace);
516 517
		}

518
		const globalElablement = this.extensionRuntimeService.setEnablement(extension.identifier, enable, false);
519 520 521
		if (!this.workspaceContextService.getWorkspace()) {
			return globalElablement;
		}
522
		return TPromise.join([globalElablement, this.extensionRuntimeService.setEnablement(extension.identifier, enable, true)])
523 524 525
			.then(values => values[0] || values[1]);
	}

526 527 528
	private onInstallExtension(event: InstallExtensionEvent): void {
		const { id, gallery } = event;

J
Joao Moreno 已提交
529 530 531 532
		if (!gallery) {
			return;
		}

J
Joao Moreno 已提交
533
		let extension = this.installed.filter(e => (e.local && e.local.metadata && e.local.metadata.id) === gallery.id)[0];
J
Joao Moreno 已提交
534 535

		if (!extension) {
J
Joao Moreno 已提交
536
			extension = new Extension(this.galleryService, this.stateProvider, null, gallery);
J
Joao Moreno 已提交
537 538 539
		}

		extension.gallery = gallery;
540 541

		const start = new Date();
542
		const operation = Operation.Installing;
543
		this.installing.push({ id: stripVersion(id), operation, extension, start });
J
Joao Moreno 已提交
544 545 546 547

		this._onChange.fire();
	}

548 549 550
	private onDidInstallExtension(event: DidInstallExtensionEvent): void {
		const { local, zipPath, error } = event;
		const id = stripVersion(event.id);
J
Joao Moreno 已提交
551 552
		const installing = this.installing.filter(e => e.id === id)[0];

553 554 555
		const extension: Extension = installing ? installing.extension : zipPath ? new Extension(this.galleryService, this.stateProvider, null) : null;
		if (extension) {
			this.installing = this.installing.filter(e => e.id !== id);
J
Joao Moreno 已提交
556

557 558 559 560
			if (!error) {
				this.newlyInstalled.push(extension);
				extension.local = local;
				extension.needsReload = true;
561

562 563
				const galleryId = local.metadata && local.metadata.id;
				const installed = this.installed.filter(e => (e.local && e.local.metadata && e.local.metadata.id) === galleryId)[0];
J
Joao Moreno 已提交
564

565 566 567 568 569 570
				if (galleryId && installed) {
					installing.operation = Operation.Updating;
					installed.local = local;
				} else {
					this.installed.push(extension);
				}
571
			}
J
Joao Moreno 已提交
572
		}
J
Joao Moreno 已提交
573

574 575 576 577
		if (extension.gallery) {
			// Report telemetry only for gallery extensions
			this.reportTelemetry(installing, !error);
		}
J
Joao Moreno 已提交
578 579 580
		this._onChange.fire();
	}

J
Joao Moreno 已提交
581
	private onUninstallExtension(id: string): void {
J
Joao Moreno 已提交
582
		const extension = this.installed.filter(e => e.local.id === id)[0];
S
Sandeep Somavarapu 已提交
583
		const newLength = this.installed.filter(e => e.local.id !== id).length;
S
Sandeep Somavarapu 已提交
584
		// TODO: Ask @Joao why is this?
S
Sandeep Somavarapu 已提交
585
		if (newLength === this.installed.length) {
J
Joao Moreno 已提交
586 587 588
			return;
		}

589 590 591 592 593
		const start = new Date();
		const operation = Operation.Uninstalling;
		const uninstalling = this.uninstalling.filter(e => e.id === id)[0] || { id, operation, extension, start };
		this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => e.id !== id)];

J
Joao Moreno 已提交
594 595 596
		this._onChange.fire();
	}

S
Sandeep Somavarapu 已提交
597
	private onDidUninstallExtension({id, error}: DidUninstallExtensionEvent): void {
S
Sandeep Somavarapu 已提交
598
		let newlyInstalled = false;
S
Sandeep Somavarapu 已提交
599
		if (!error) {
S
Sandeep Somavarapu 已提交
600
			newlyInstalled = this.newlyInstalled.filter(e => e.local.id === id).length > 0;
601
			this.newlyInstalled = this.newlyInstalled.filter(e => e.local.id !== id);
S
Sandeep Somavarapu 已提交
602 603 604
			this.installed = this.installed.filter(e => e.local.id !== id);
		}

605 606 607 608 609
		const uninstalling = this.uninstalling.filter(e => e.id === id)[0];
		this.uninstalling = this.uninstalling.filter(e => e.id !== id);
		if (!uninstalling) {
			return;
		}
610

S
Sandeep Somavarapu 已提交
611 612
		if (!error) {
			this.unInstalled.push(uninstalling.extension);
S
Sandeep Somavarapu 已提交
613
			uninstalling.extension.needsReload = !newlyInstalled;
S
Sandeep Somavarapu 已提交
614 615
			this.reportTelemetry(uninstalling, true);
		}
616

617
		this._onChange.fire();
618 619
	}

J
Joao Moreno 已提交
620
	private getExtensionState(extension: Extension): ExtensionState {
J
Joao Moreno 已提交
621
		if (extension.gallery && this.installing.some(e => e.extension.gallery.id === extension.gallery.id)) {
J
Joao Moreno 已提交
622 623 624
			return ExtensionState.Installing;
		}

S
Sandeep Somavarapu 已提交
625
		if (this.uninstalling.some(e => e.extension.identifier === extension.identifier)) {
S
Sandeep Somavarapu 已提交
626 627 628
			return ExtensionState.Uninstalling;
		}

J
Joao Moreno 已提交
629 630 631
		const local = this.installed.filter(e => e === extension || (e.gallery && extension.gallery && e.gallery.id === extension.gallery.id))[0];

		if (local) {
632 633
			if (this.newlyInstalled.some(e => e.gallery && extension.gallery && e.gallery.id === extension.gallery.id)) {
				return ExtensionState.Installed;
634
			}
635
			return this.extensionRuntimeService.isDisabled(extension.identifier) ? ExtensionState.Disabled : ExtensionState.Enabled;
J
Joao Moreno 已提交
636 637
		}

J
Joao Moreno 已提交
638 639 640
		return ExtensionState.Uninstalled;
	}

641
	private reportTelemetry(active: IActiveExtension, success: boolean): void {
642
		const data = active.extension.telemetryData;
643 644 645 646
		const duration = new Date().getTime() - active.start.getTime();
		const eventName = toTelemetryEventName(active.operation);

		this.telemetryService.publicLog(eventName, assign(data, { success, duration }));
J
Joao Moreno 已提交
647 648
	}

J
Joao Moreno 已提交
649 650 651 652 653
	private onError(err: any): void {
		if (isPromiseCanceledError(err)) {
			return;
		}

654 655
		const message = err && err.message || '';

J
Joao Moreno 已提交
656
		if (/getaddrinfo ENOTFOUND|getaddrinfo ENOENT|connect EACCES|connect ECONNREFUSED/.test(message)) {
657 658 659
			return;
		}

J
Joao Moreno 已提交
660 661 662
		this.messageService.show(Severity.Error, err);
	}

663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
	private onOpenExtensionUrl(uri: URI): void {
		const match = /^extension\/([^/]+)$/.exec(uri.path);

		if (!match) {
			return;
		}

		const extensionId = match[1];

		this.queryGallery({ names: [extensionId] })
			.done(result => {
				if (result.total < 1) {
					return;
				}

				const extension = result.firstPage[0];
679
				this.open(extension).done(null, error => this.onError(error));
680 681 682
			});
	}

J
Joao Moreno 已提交
683 684 685
	dispose(): void {
		this.disposables = dispose(this.disposables);
	}
J
Joao Moreno 已提交
686
}