extensionsActions.ts 108.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  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/extensionActions';
import { localize } from 'vs/nls';
import { IAction, Action } from 'vs/base/common/actions';
S
Sandeep Somavarapu 已提交
9
import { Throttler, Delayer } from 'vs/base/common/async';
10
import * as DOM from 'vs/base/browser/dom';
11
import * as paths from 'vs/base/common/paths';
12
import { Event } from 'vs/base/common/event';
13
import * as json from 'vs/base/common/json';
S
Explore  
Sandeep Somavarapu 已提交
14
import { ActionItem, Separator, IActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar';
15
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
S
Sandeep Somavarapu 已提交
16
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
17 18
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
S
Sandeep Somavarapu 已提交
19
import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
20
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
S
Sandeep Somavarapu 已提交
21
import { ExtensionType, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
22
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
B
Benjamin Pasero 已提交
23
import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
B
Benjamin Pasero 已提交
24
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
25
import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery';
26
import { IFileService, IContent } from 'vs/platform/files/common/files';
S
Sandeep Somavarapu 已提交
27
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
J
Joao Moreno 已提交
28
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
29
import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
30
import { URI } from 'vs/base/common/uri';
S
Sandeep Somavarapu 已提交
31
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
S
Sandeep Somavarapu 已提交
32
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
33 34 35
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, registerColor, foreground } from 'vs/platform/theme/common/colorRegistry';
import { Color } from 'vs/base/common/color';
36 37 38
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { ITextEditorSelection } from 'vs/platform/editor/common/editor';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
39
import { PagedModel } from 'vs/base/common/paging';
S
Sandeep Somavarapu 已提交
40 41 42
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
I
isidor 已提交
43
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
J
Joao Moreno 已提交
44 45 46 47
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
48
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
49
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
50
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
51
import product from 'vs/platform/node/product';
C
Christof Marti 已提交
52
import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
53
import { CancellationToken } from 'vs/base/common/cancellation';
54
import { clipboard } from 'electron';
B
Benjamin Pasero 已提交
55
import { IPartService } from 'vs/workbench/services/part/common/partService';
56
import { alert } from 'vs/base/browser/ui/aria/aria';
M
Matt Bierner 已提交
57
import { coalesce } from 'vs/base/common/arrays';
S
Sandeep Somavarapu 已提交
58
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
J
Joao Moreno 已提交
59

S
Sandeep Somavarapu 已提交
60 61 62 63 64 65 66 67 68 69
function toExtensionDescription(local: ILocalExtension): IExtensionDescription {
	return {
		identifier: new ExtensionIdentifier(local.identifier.id),
		isBuiltin: local.type === ExtensionType.System,
		isUnderDevelopment: false,
		extensionLocation: local.location,
		...local.manifest
	};
}

70 71
const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => {
	if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS) {
S
Sandeep Somavarapu 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		return Promise.reject(error);
	} else {
		const downloadUrl = `${product.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`;
		notificationService.prompt(Severity.Error, message, [{
			label: localize('download', "Download Manually"),
			run: () => openerService.open(URI.parse(downloadUrl)).then(() => {
				notificationService.prompt(
					Severity.Info,
					localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id),
					[{
						label: InstallVSIXAction.LABEL,
						run: () => {
							const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL);
							action.run();
							action.dispose();
						}
					}]
				);
			})
		}]);
		return Promise.resolve();
	}
94
};
95

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
function getRelativeDateLabel(date: Date): string {
	const delta = new Date().getTime() - date.getTime();

	const year = 365 * 24 * 60 * 60 * 1000;
	if (delta > year) {
		const noOfYears = Math.floor(delta / year);
		return noOfYears > 1 ? localize('noOfYearsAgo', "{0} years ago", noOfYears) : localize('one year ago', "1 year ago");
	}

	const month = 30 * 24 * 60 * 60 * 1000;
	if (delta > month) {
		const noOfMonths = Math.floor(delta / month);
		return noOfMonths > 1 ? localize('noOfMonthsAgo', "{0} months ago", noOfMonths) : localize('one month ago', "1 month ago");
	}

	const day = 24 * 60 * 60 * 1000;
	if (delta > day) {
		const noOfDays = Math.floor(delta / day);
		return noOfDays > 1 ? localize('noOfDaysAgo', "{0} days ago", noOfDays) : localize('one day ago', "1 day ago");
	}

	const hour = 60 * 60 * 1000;
	if (delta > hour) {
		const noOfHours = Math.floor(delta / day);
		return noOfHours > 1 ? localize('noOfHoursAgo', "{0} hours ago", noOfHours) : localize('one hour ago', "1 hour ago");
	}

	if (delta > 0) {
		return localize('just now', "Just now");
	}

	return '';
}

S
Sandeep Somavarapu 已提交
130 131 132 133 134
export abstract class ExtensionAction extends Action implements IExtensionContainer {
	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) { this._extension = extension; this.update(); }
	abstract update(): void;
135
}
136

S
Sandeep Somavarapu 已提交
137
export class InstallAction extends ExtensionAction {
138

139 140 141
	private static INSTALL_LABEL = localize('install', "Install");
	private static INSTALLING_LABEL = localize('installing', "Installing");

142 143
	private static readonly Class = 'extension-action prominent install';
	private static readonly InstallingClass = 'extension-action install installing';
144 145

	constructor(
146 147 148
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@INotificationService private readonly notificationService: INotificationService,
S
Sandeep Somavarapu 已提交
149 150 151 152
		@IOpenerService private readonly openerService: IOpenerService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@IExtensionService private readonly runtimeExtensionService: IExtensionService,
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService
153
	) {
154
		super(`extensions.install`, InstallAction.INSTALL_LABEL, InstallAction.Class, false);
155 156 157
		this.update();
	}

S
Sandeep Somavarapu 已提交
158
	update(): void {
159
		if (!this.extension || this.extension.type === ExtensionType.System) {
160 161
			this.enabled = false;
			this.class = InstallAction.Class;
162
			this.label = InstallAction.INSTALL_LABEL;
163 164 165 166 167 168
			return;
		}

		this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled;

		if (this.extension.state === ExtensionState.Installing) {
169
			this.label = InstallAction.INSTALLING_LABEL;
170
			this.class = InstallAction.InstallingClass;
171
			this.tooltip = InstallAction.INSTALLING_LABEL;
172
		} else {
173
			this.label = InstallAction.INSTALL_LABEL;
174
			this.class = InstallAction.Class;
175
			this.tooltip = InstallAction.INSTALL_LABEL;
176 177 178
		}
	}

S
Sandeep Somavarapu 已提交
179
	async run(): Promise<any> {
180
		this.extensionsWorkbenchService.open(this.extension);
J
Joao Moreno 已提交
181

182 183
		alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));

S
Sandeep Somavarapu 已提交
184 185
		const extension = await this.install(this.extension);

186
		if (extension.local && extension.local.manifest.contributes && extension.local.manifest.contributes.themes && extension.local.manifest.contributes.themes.length) {
S
Sandeep Somavarapu 已提交
187 188 189
			return this.applyInstalledTheme(extension.local);
		}

J
Joao Moreno 已提交
190 191
	}

S
Sandeep Somavarapu 已提交
192 193 194 195 196 197 198 199 200 201 202 203
	private install(extension: IExtension): Promise<IExtension> {
		return this.extensionsWorkbenchService.install(extension)
			.then(null, err => {
				if (!extension.gallery) {
					return this.notificationService.error(err);
				}

				console.error(err);

				return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService);
			});
	}
J
Joao Moreno 已提交
204

S
Sandeep Somavarapu 已提交
205 206 207 208 209 210 211 212 213 214
	private async applyInstalledTheme(extension: ILocalExtension): Promise<void> {
		const runningExtension = await this.getRunningExtension(extension);
		if (runningExtension) {
			const currentTheme = this.workbenchThemeService.getColorTheme();
			const themes = await this.workbenchThemeService.getColorThemes(runningExtension.identifier);
			const delayer = new Delayer<void>(100);
			const pickedTheme = await this.quickInputService.pick(
				themes.map(theme => (<IQuickPickItem>{ label: theme.label, id: theme.id })),
				{
					placeHolder: localize('apply installed theme', "Apply installed theme"),
215
					onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, ConfigurationTarget.MEMORY).then(() => undefined))
S
Sandeep Somavarapu 已提交
216 217 218 219
				});
			this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, undefined);
		}
	}
220

S
Sandeep Somavarapu 已提交
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	private async getRunningExtension(extension: ILocalExtension): Promise<IExtensionDescription | null> {
		const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id);
		if (runningExtension) {
			return runningExtension;
		}
		if (this.runtimeExtensionService.canAddExtension(toExtensionDescription(extension))) {
			return new Promise<IExtensionDescription | null>((c, e) => {
				const disposable = this.runtimeExtensionService.onDidChangeExtensions(async () => {
					const runningExtension = await this.runtimeExtensionService.getExtension(extension.identifier.id);
					if (runningExtension) {
						disposable.dispose();
						c(runningExtension);
					}
				});
			});
		}
		return null;
238 239 240
	}
}

S
Sandeep Somavarapu 已提交
241
export class UninstallAction extends ExtensionAction {
242

243 244
	private static readonly UninstallLabel = localize('uninstallAction', "Uninstall");
	private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling");
245

S
Sandeep Somavarapu 已提交
246 247 248
	private static readonly UninstallClass = 'extension-action uninstall';
	private static readonly UnInstallingClass = 'extension-action uninstall uninstalling';

249
	constructor(
S
Sandeep Somavarapu 已提交
250
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
251
	) {
S
Sandeep Somavarapu 已提交
252
		super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false);
253 254 255
		this.update();
	}

S
Sandeep Somavarapu 已提交
256
	update(): void {
257 258 259 260 261 262 263 264 265
		if (!this.extension) {
			this.enabled = false;
			return;
		}

		const state = this.extension.state;

		if (state === ExtensionState.Uninstalling) {
			this.label = UninstallAction.UninstallingLabel;
S
Sandeep Somavarapu 已提交
266
			this.class = UninstallAction.UnInstallingClass;
267 268 269 270 271
			this.enabled = false;
			return;
		}

		this.label = UninstallAction.UninstallLabel;
S
Sandeep Somavarapu 已提交
272
		this.class = UninstallAction.UninstallClass;
273

S
Sandeep Somavarapu 已提交
274
		const installedExtensions = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier));
275 276 277 278 279 280

		if (!installedExtensions.length) {
			this.enabled = false;
			return;
		}

S
Sandeep Somavarapu 已提交
281 282 283 284 285
		if (state !== ExtensionState.Installed) {
			this.enabled = false;
			return;
		}

286
		if (installedExtensions[0].type !== ExtensionType.User) {
287 288 289 290 291 292 293
			this.enabled = false;
			return;
		}

		this.enabled = true;
	}

S
Sandeep Somavarapu 已提交
294
	run(): Promise<any> {
295 296 297 298 299
		alert(localize('uninstallExtensionStart', "Uninstalling extension {0} started.", this.extension.displayName));

		return this.extensionsWorkbenchService.uninstall(this.extension).then(() => {
			alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName));
		});
300 301 302
	}
}

S
Sandeep Somavarapu 已提交
303
export class CombinedInstallAction extends ExtensionAction {
304

305
	private static readonly NoExtensionClass = 'extension-action prominent install no-extension';
306 307
	private installAction: InstallAction;
	private uninstallAction: UninstallAction;
308 309 310
	private disposables: IDisposable[] = [];

	constructor(
311
		@IInstantiationService instantiationService: IInstantiationService
312 313 314
	) {
		super('extensions.combinedInstall', '', '', false);

315 316
		this.installAction = instantiationService.createInstance(InstallAction);
		this.uninstallAction = instantiationService.createInstance(UninstallAction);
317 318 319 320 321
		this.disposables.push(this.installAction, this.uninstallAction);

		this.update();
	}

S
Sandeep Somavarapu 已提交
322 323 324 325 326 327
	update(): void {
		this.installAction.extension = this.extension;
		this.uninstallAction.extension = this.extension;
		this.installAction.update();
		this.uninstallAction.update();

328
		if (!this.extension || this.extension.type === ExtensionType.System) {
329 330
			this.enabled = false;
			this.class = CombinedInstallAction.NoExtensionClass;
S
Sandeep Somavarapu 已提交
331 332
		} else if (this.extension.state === ExtensionState.Installing) {
			this.enabled = false;
333 334
			this.label = this.installAction.label;
			this.class = this.installAction.class;
S
Sandeep Somavarapu 已提交
335
			this.tooltip = this.installAction.tooltip;
S
Sandeep Somavarapu 已提交
336 337
		} else if (this.extension.state === ExtensionState.Uninstalling) {
			this.enabled = false;
338 339
			this.label = this.uninstallAction.label;
			this.class = this.uninstallAction.class;
S
Sandeep Somavarapu 已提交
340
			this.tooltip = this.uninstallAction.tooltip;
S
Sandeep Somavarapu 已提交
341 342
		} else if (this.installAction.enabled) {
			this.enabled = true;
343 344
			this.label = this.installAction.label;
			this.class = this.installAction.class;
S
Sandeep Somavarapu 已提交
345
			this.tooltip = this.installAction.tooltip;
S
Sandeep Somavarapu 已提交
346 347
		} else if (this.uninstallAction.enabled) {
			this.enabled = true;
348 349
			this.label = this.uninstallAction.label;
			this.class = this.uninstallAction.class;
S
Sandeep Somavarapu 已提交
350
			this.tooltip = this.uninstallAction.tooltip;
351 352 353 354
		} else {
			this.enabled = false;
			this.label = this.installAction.label;
			this.class = this.installAction.class;
S
Sandeep Somavarapu 已提交
355
			this.tooltip = this.installAction.tooltip;
356 357 358
		}
	}

S
Sandeep Somavarapu 已提交
359
	run(): Promise<any> {
360 361 362 363 364 365
		if (this.installAction.enabled) {
			return this.installAction.run();
		} else if (this.uninstallAction.enabled) {
			return this.uninstallAction.run();
		}

S
Sandeep Somavarapu 已提交
366
		return Promise.resolve(null);
367 368 369 370 371 372 373 374
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

S
Sandeep Somavarapu 已提交
375
export class UpdateAction extends ExtensionAction {
376

377 378
	private static readonly EnabledClass = 'extension-action prominent update';
	private static readonly DisabledClass = `${UpdateAction.EnabledClass} disabled`;
379 380

	constructor(
381 382 383 384
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@INotificationService private readonly notificationService: INotificationService,
		@IOpenerService private readonly openerService: IOpenerService
385
	) {
386
		super(`extensions.update`, '', UpdateAction.DisabledClass, false);
387 388 389
		this.update();
	}

S
Sandeep Somavarapu 已提交
390
	update(): void {
391 392 393
		if (!this.extension) {
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
394
			this.label = this.getUpdateLabel();
395 396 397
			return;
		}

398
		if (this.extension.type !== ExtensionType.User) {
399 400
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
401
			this.label = this.getUpdateLabel();
402 403 404 405 406 407 408 409
			return;
		}

		const canInstall = this.extensionsWorkbenchService.canInstall(this.extension);
		const isInstalled = this.extension.state === ExtensionState.Installed;

		this.enabled = canInstall && isInstalled && this.extension.outdated;
		this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass;
410
		this.label = this.extension.outdated ? this.getUpdateLabel(this.extension.latestVersion) : this.getUpdateLabel();
411 412
	}

S
Sandeep Somavarapu 已提交
413
	run(): Promise<any> {
414
		alert(localize('updateExtensionStart', "Updating extension {0} to version {1} started.", this.extension.displayName, this.extension.latestVersion));
J
Joao Moreno 已提交
415 416 417
		return this.install(this.extension);
	}

S
Sandeep Somavarapu 已提交
418
	private install(extension: IExtension): Promise<void> {
419 420 421
		return this.extensionsWorkbenchService.install(extension).then(() => {
			alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", this.extension.displayName, this.extension.latestVersion));
		}, err => {
422
			if (!extension.gallery) {
J
Joao Moreno 已提交
423 424 425 426
				return this.notificationService.error(err);
			}

			console.error(err);
427

S
Sandeep Somavarapu 已提交
428
			return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService);
J
Joao Moreno 已提交
429
		});
430 431
	}

432
	private getUpdateLabel(version?: string): string {
433
		return version ? localize('updateTo', "Update to {0}", version) : localize('updateAction', "Update");
434
	}
435 436
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
interface IExtensionActionItemOptions extends IActionItemOptions {
	tabOnlyOnFocus?: boolean;
}

export class ExtensionActionItem extends ActionItem {

	protected options: IExtensionActionItemOptions;

	constructor(context: any, action: IAction, options: IExtensionActionItemOptions = {}) {
		super(context, action, options);
	}

	updateEnabled(): void {
		super.updateEnabled();

		if (this.options.tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) {
			DOM.removeTabIndexAndUpdateFocus(this.label);
		}
	}

	private _hasFocus: boolean;
	setFocus(value: boolean): void {
		if (!this.options.tabOnlyOnFocus || this._hasFocus === value) {
			return;
		}
		this._hasFocus = value;
		if (this.getAction().enabled) {
			if (this._hasFocus) {
				this.label.tabIndex = 0;
			} else {
				DOM.removeTabIndexAndUpdateFocus(this.label);
			}
		}
	}
}

S
Sandeep Somavarapu 已提交
473
export abstract class ExtensionDropDownAction extends ExtensionAction {
S
Explore  
Sandeep Somavarapu 已提交
474 475 476 477 478 479 480 481 482

	protected disposables: IDisposable[] = [];

	constructor(
		id: string,
		label: string,
		cssClass: string,
		enabled: boolean,
		private readonly tabOnlyOnFocus: boolean,
S
Sandeep Somavarapu 已提交
483
		@IInstantiationService protected instantiationService: IInstantiationService
S
Explore  
Sandeep Somavarapu 已提交
484 485 486 487 488 489
	) {
		super(id, label, cssClass, enabled);
	}

	private _actionItem: DropDownMenuActionItem;
	createActionItem(): DropDownMenuActionItem {
S
Sandeep Somavarapu 已提交
490
		this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, this.tabOnlyOnFocus);
S
Explore  
Sandeep Somavarapu 已提交
491 492 493
		return this._actionItem;
	}

J
Johannes Rieken 已提交
494
	public run({ actionGroups, disposeActionsOnHide }: { actionGroups: IAction[][], disposeActionsOnHide: boolean }): Promise<any> {
S
Explore  
Sandeep Somavarapu 已提交
495
		if (this._actionItem) {
S
Sandeep Somavarapu 已提交
496
			this._actionItem.showMenu(actionGroups, disposeActionsOnHide);
S
Explore  
Sandeep Somavarapu 已提交
497 498 499 500 501 502 503 504 505 506
		}
		return Promise.resolve(null);
	}

	dispose(): void {
		dispose(this.disposables);
		super.dispose();
	}
}

507
export class DropDownMenuActionItem extends ExtensionActionItem {
508 509 510

	private disposables: IDisposable[] = [];

S
Sandeep Somavarapu 已提交
511
	constructor(action: ExtensionDropDownAction,
512
		tabOnlyOnFocus: boolean,
513
		@IContextMenuService private readonly contextMenuService: IContextMenuService
S
Sandeep Somavarapu 已提交
514
	) {
515
		super(null, action, { icon: true, label: true, tabOnlyOnFocus });
516 517
	}

S
Sandeep Somavarapu 已提交
518
	public showMenu(menuActionGroups: IAction[][], disposeActionsOnHide: boolean): void {
519 520 521 522 523 524 525 526 527 528 529
		if (this.element) {
			const actions = this.getActions(menuActionGroups);
			let elementPosition = DOM.getDomNodePagePosition(this.element);
			const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 };
			this.contextMenuService.showContextMenu({
				getAnchor: () => anchor,
				getActions: () => actions,
				actionRunner: this.actionRunner,
				onHide: () => { if (disposeActionsOnHide) { dispose(actions); } }
			});
		}
530 531
	}

S
Sandeep Somavarapu 已提交
532
	private getActions(menuActionGroups: IAction[][]): IAction[] {
B
Benjamin Pasero 已提交
533
		let actions: IAction[] = [];
S
Sandeep Somavarapu 已提交
534
		for (const menuActions of menuActionGroups) {
S
Sandeep Somavarapu 已提交
535
			actions = [...actions, ...menuActions, new Separator()];
536 537 538 539 540 541 542 543 544 545
		}
		return actions.length ? actions.slice(0, actions.length - 1) : actions;
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

S
Sandeep Somavarapu 已提交
546
export class ManageExtensionAction extends ExtensionDropDownAction {
547

548
	static readonly ID = 'extensions.manage';
549 550
	private static readonly Class = 'extension-action manage';
	private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`;
551 552

	constructor(
S
Sandeep Somavarapu 已提交
553
		@IInstantiationService instantiationService: IInstantiationService,
554
		@IExtensionService private readonly extensionService: IExtensionService
555
	) {
S
Sandeep Somavarapu 已提交
556 557

		super(ManageExtensionAction.ID, '', '', true, true, instantiationService);
558

S
Sandeep Somavarapu 已提交
559
		this.tooltip = localize('manage', "Manage");
560 561 562 563

		this.update();
	}

S
Sandeep Somavarapu 已提交
564
	getActionGroups(runningExtensions: IExtensionDescription[]): IAction[][] {
S
Sandeep Somavarapu 已提交
565
		const groups: ExtensionAction[][] = [];
S
Sandeep Somavarapu 已提交
566
		groups.push([
S
Sandeep Somavarapu 已提交
567
			this.instantiationService.createInstance(EnableGloballyAction),
568
			this.instantiationService.createInstance(EnableForWorkspaceAction)
S
Sandeep Somavarapu 已提交
569 570
		]);
		groups.push([
S
Sandeep Somavarapu 已提交
571 572
			this.instantiationService.createInstance(DisableGloballyAction, runningExtensions),
			this.instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions)
S
Sandeep Somavarapu 已提交
573
		]);
S
Sandeep Somavarapu 已提交
574 575 576 577 578
		groups.push([this.instantiationService.createInstance(UninstallAction)]);
		groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
		groups.push([this.instantiationService.createInstance(ExtensionInfoAction)]);

		groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension));
S
Sandeep Somavarapu 已提交
579 580 581 582

		return groups;
	}

J
Johannes Rieken 已提交
583
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
584 585 586
		return this.extensionService.getExtensions().then(runtimeExtensions => super.run({ actionGroups: this.getActionGroups(runtimeExtensions), disposeActionsOnHide: true }));
	}

S
Sandeep Somavarapu 已提交
587
	update(): void {
S
Sandeep Somavarapu 已提交
588
		this.class = ManageExtensionAction.HideManageExtensionClass;
589
		this.enabled = false;
S
Sandeep Somavarapu 已提交
590
		if (this.extension) {
591 592
			const state = this.extension.state;
			this.enabled = state === ExtensionState.Installed;
S
Sandeep Somavarapu 已提交
593
			this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass;
594 595 596 597 598
			this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : '';
		}
	}
}

S
Sandeep Somavarapu 已提交
599
export class InstallAnotherVersionAction extends ExtensionAction {
600

601 602
	static readonly ID = 'workbench.extensions.action.install.anotherVersion';
	static LABEL = localize('install another version', "Install Another Version...");
603

S
Sandeep Somavarapu 已提交
604
	constructor(
605 606 607 608 609 610
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@INotificationService private readonly notificationService: INotificationService,
		@IOpenerService private readonly openerService: IOpenerService
611 612
	) {
		super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL);
S
Sandeep Somavarapu 已提交
613 614 615 616
		this.update();
	}

	update(): void {
S
Sandeep Somavarapu 已提交
617
		this.enabled = this.extension && !!this.extension.gallery;
618 619
	}

J
Johannes Rieken 已提交
620
	run(): Promise<any> {
621 622 623
		if (!this.enabled) {
			return Promise.resolve();
		}
S
Sandeep Somavarapu 已提交
624
		return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true })
625 626
			.then(pick => {
				if (pick) {
S
Sandeep Somavarapu 已提交
627 628 629
					if (this.extension.version === pick.id) {
						return Promise.resolve();
					}
S
Sandeep Somavarapu 已提交
630 631 632
					const promise: Promise<any> = pick.latest ? this.extensionsWorkbenchService.install(this.extension) : this.extensionsWorkbenchService.installVersion(this.extension, pick.id);
					return promise
						.then(null, err => {
633 634 635 636 637
							if (!this.extension.gallery) {
								return this.notificationService.error(err);
							}

							console.error(err);
638

S
Sandeep Somavarapu 已提交
639
							return promptDownloadManually(this.extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService);
640 641 642 643 644
						});
				}
				return null;
			});
	}
S
Sandeep Somavarapu 已提交
645

646 647
	private getVersionEntries(): Promise<(IQuickPickItem & { latest: boolean, id: string })[]> {
		return this.extensionGalleryService.getAllVersions(this.extension.gallery!, true)
648
			.then(allVersions => allVersions.map((v, i) => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === this.extension.version ? ` (${localize('current', "Current")})` : ''}`, latest: i === 0 })));
S
Sandeep Somavarapu 已提交
649
	}
650 651
}

S
Sandeep Somavarapu 已提交
652
export class ExtensionInfoAction extends ExtensionAction {
653

S
Sandeep Somavarapu 已提交
654 655
	static readonly ID = 'extensions.extensionInfo';
	static readonly LABEL = localize('extensionInfoAction', "Copy Extension Information");
656

S
Sandeep Somavarapu 已提交
657
	constructor() {
658
		super(ExtensionInfoAction.ID, ExtensionInfoAction.LABEL);
S
Sandeep Somavarapu 已提交
659 660 661 662 663
		this.update();
	}

	update(): void {
		this.enabled = !!this.extension;
664 665 666 667
	}

	run(): Promise<any> {

S
Sandeep Somavarapu 已提交
668 669 670 671 672 673
		const name = localize('extensionInfoName', 'Name: {0}', this.extension.displayName);
		const id = localize('extensionInfoId', 'Id: {0}', this.extension.identifier.id);
		const description = localize('extensionInfoDescription', 'Description: {0}', this.extension.description);
		const verision = localize('extensionInfoVersion', 'Version: {0}', this.extension.version);
		const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', this.extension.publisherDisplayName);
		const link = this.extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', this.extension.url.toString()) : null;
674

S
Sandeep Somavarapu 已提交
675
		const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`;
676 677 678 679 680 681

		clipboard.writeText(clipboardStr);
		return Promise.resolve(null);
	}
}

S
Sandeep Somavarapu 已提交
682
export class EnableForWorkspaceAction extends ExtensionAction {
683

684
	static readonly ID = 'extensions.enableForWorkspace';
S
Sandeep Somavarapu 已提交
685
	static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");
686

S
Sandeep Somavarapu 已提交
687
	constructor(
688 689
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
690
	) {
S
Explore  
Sandeep Somavarapu 已提交
691
		super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL);
692 693 694
		this.update();
	}

S
Sandeep Somavarapu 已提交
695
	update(): void {
696 697
		this.enabled = false;
		if (this.extension) {
698
			this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
699 700 701
		}
	}

S
Sandeep Somavarapu 已提交
702
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
703
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.WorkspaceEnabled);
704 705 706
	}
}

S
Sandeep Somavarapu 已提交
707
export class EnableGloballyAction extends ExtensionAction {
708

709
	static readonly ID = 'extensions.enableGlobally';
S
Sandeep Somavarapu 已提交
710
	static LABEL = localize('enableGloballyAction', "Enable");
711

S
Sandeep Somavarapu 已提交
712
	constructor(
713
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
714
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
715
	) {
S
Sandeep Somavarapu 已提交
716
		super(EnableGloballyAction.ID, EnableGloballyAction.LABEL);
717 718 719
		this.update();
	}

S
Sandeep Somavarapu 已提交
720
	update(): void {
721
		this.enabled = false;
722
		if (this.extension && this.extension.local) {
S
Explore  
Sandeep Somavarapu 已提交
723
			this.enabled = this.extension.state === ExtensionState.Installed && this.extension.enablementState === EnablementState.Disabled && this.extensionEnablementService.canChangeEnablement(this.extension.local);
724 725 726
		}
	}

S
Sandeep Somavarapu 已提交
727
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
728
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.Enabled);
729 730 731
	}
}

S
Sandeep Somavarapu 已提交
732
export class DisableForWorkspaceAction extends ExtensionAction {
733

734
	static readonly ID = 'extensions.disableForWorkspace';
S
Sandeep Somavarapu 已提交
735
	static LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");
736

S
Sandeep Somavarapu 已提交
737
	constructor(readonly runningExtensions: IExtensionDescription[],
738 739 740
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
741
	) {
S
Sandeep Somavarapu 已提交
742
		super(DisableForWorkspaceAction.ID, DisableForWorkspaceAction.LABEL);
743 744 745
		this.update();
	}

S
Sandeep Somavarapu 已提交
746
	update(): void {
747
		this.enabled = false;
748
		if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
749
			this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
750 751 752
		}
	}

S
Sandeep Somavarapu 已提交
753
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
754
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.WorkspaceDisabled);
755 756 757
	}
}

S
Sandeep Somavarapu 已提交
758
export class DisableGloballyAction extends ExtensionAction {
759

760
	static readonly ID = 'extensions.disableGlobally';
S
Sandeep Somavarapu 已提交
761
	static LABEL = localize('disableGloballyAction', "Disable");
762

S
Sandeep Somavarapu 已提交
763
	constructor(readonly runningExtensions: IExtensionDescription[],
764 765
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
766
	) {
S
Sandeep Somavarapu 已提交
767
		super(DisableGloballyAction.ID, DisableGloballyAction.LABEL);
768 769 770
		this.update();
	}

S
Sandeep Somavarapu 已提交
771
	update(): void {
772
		this.enabled = false;
773
		if (this.extension && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))) {
774
			this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && !!this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local);
775 776 777
		}
	}

S
Sandeep Somavarapu 已提交
778
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
779
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.Disabled);
780 781 782
	}
}

S
Sandeep Somavarapu 已提交
783
export abstract class ExtensionEditorDropDownAction extends ExtensionDropDownAction {
784

S
Sandeep Somavarapu 已提交
785 786 787
	private static readonly EnabledClass = 'extension-action extension-editor-dropdown-action';
	private static readonly EnabledDropDownClass = 'extension-action extension-editor-dropdown-action dropdown enable';
	private static readonly DisabledClass = `${ExtensionEditorDropDownAction.EnabledClass} disabled`;
788 789

	constructor(
S
Sandeep Somavarapu 已提交
790 791 792
		id: string, private readonly initialLabel: string,
		readonly actions: ExtensionAction[],
		@IInstantiationService instantiationService: IInstantiationService
793
	) {
S
Sandeep Somavarapu 已提交
794
		super(id, initialLabel, ExtensionEditorDropDownAction.DisabledClass, false, false, instantiationService);
795 796 797
		this.update();
	}

S
Sandeep Somavarapu 已提交
798 799 800
	update(): void {
		this.actions.forEach(a => a.extension = this.extension);
		this.actions.forEach(a => a.update());
S
Sandeep Somavarapu 已提交
801
		const enabledActions = this.actions.filter(a => a.enabled);
802 803 804 805
		this.enabled = enabledActions.length > 0;
		if (this.enabled) {
			if (enabledActions.length === 1) {
				this.label = enabledActions[0].label;
S
Sandeep Somavarapu 已提交
806
				this.class = ExtensionEditorDropDownAction.EnabledClass;
807
			} else {
S
Sandeep Somavarapu 已提交
808
				this.label = this.initialLabel;
S
Sandeep Somavarapu 已提交
809
				this.class = ExtensionEditorDropDownAction.EnabledDropDownClass;
810 811
			}
		} else {
S
Sandeep Somavarapu 已提交
812
			this.class = ExtensionEditorDropDownAction.DisabledClass;
813 814 815
		}
	}

J
Johannes Rieken 已提交
816
	public run(): Promise<any> {
S
Sandeep Somavarapu 已提交
817
		const enabledActions = this.actions.filter(a => a.enabled);
818 819 820
		if (enabledActions.length === 1) {
			enabledActions[0].run();
		} else {
S
Sandeep Somavarapu 已提交
821
			return super.run({ actionGroups: [this.actions], disposeActionsOnHide: false });
822
		}
S
Sandeep Somavarapu 已提交
823
		return Promise.resolve(null);
824
	}
S
Sandeep Somavarapu 已提交
825
}
826

S
Sandeep Somavarapu 已提交
827 828 829
export class EnableDropDownAction extends ExtensionEditorDropDownAction {

	constructor(
S
Sandeep Somavarapu 已提交
830
		@IInstantiationService instantiationService: IInstantiationService
S
Sandeep Somavarapu 已提交
831
	) {
S
Sandeep Somavarapu 已提交
832 833
		super('extensions.enable', localize('enableAction', "Enable"), [
			instantiationService.createInstance(EnableGloballyAction),
834
			instantiationService.createInstance(EnableForWorkspaceAction)
S
Sandeep Somavarapu 已提交
835
		], instantiationService);
S
Sandeep Somavarapu 已提交
836 837 838 839 840 841
	}
}

export class DisableDropDownAction extends ExtensionEditorDropDownAction {

	constructor(
S
Sandeep Somavarapu 已提交
842 843
		runningExtensions: IExtensionDescription[],
		@IInstantiationService instantiationService: IInstantiationService
S
Sandeep Somavarapu 已提交
844
	) {
S
Sandeep Somavarapu 已提交
845 846 847 848
		super('extensions.disable', localize('disableAction', "Disable"), [
			instantiationService.createInstance(DisableGloballyAction, runningExtensions),
			instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions)
		], instantiationService);
849 850 851
	}
}

J
Joao Moreno 已提交
852 853
export class CheckForUpdatesAction extends Action {

854
	static readonly ID = 'workbench.extensions.action.checkForUpdates';
855
	static LABEL = localize('checkForUpdates', "Check for Extension Updates");
J
Joao Moreno 已提交
856 857

	constructor(
858 859
		id = CheckForUpdatesAction.ID,
		label = CheckForUpdatesAction.LABEL,
860 861 862
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IViewletService private readonly viewletService: IViewletService,
		@INotificationService private readonly notificationService: INotificationService
J
Joao Moreno 已提交
863 864 865 866
	) {
		super(id, label, '', true);
	}

867 868 869
	private checkUpdatesAndNotify(): void {
		this.extensionsWorkbenchService.queryLocal().then(
			extensions => {
870 871
				const outdatedExtensions = extensions.filter(ext => ext.outdated === true);
				if (!outdatedExtensions.length) {
B
Benjamin Pasero 已提交
872
					this.notificationService.info(localize('noUpdatesAvailable', "All Extensions are up to date."));
873
					return;
874
				}
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894

				let msgAvailableExtensions = outdatedExtensions.length === 1 ? localize('singleUpdateAvailable', "An extension update is available.") : localize('updatesAvailable', "{0} extension updates are available.", outdatedExtensions.length);

				const disabledExtensionsCount = outdatedExtensions.filter(ext => ext.enablementState === EnablementState.Disabled || ext.enablementState === EnablementState.WorkspaceDisabled).length;
				if (disabledExtensionsCount) {
					if (outdatedExtensions.length === 1) {
						msgAvailableExtensions = localize('singleDisabledUpdateAvailable', "An update to an extension which is disabled is available.");
					} else if (disabledExtensionsCount === 1) {
						msgAvailableExtensions = localize('updatesAvailableOneDisabled', "{0} extension updates are available. One of them is for a disabled extension.", outdatedExtensions.length);
					} else if (disabledExtensionsCount === outdatedExtensions.length) {
						msgAvailableExtensions = localize('updatesAvailableAllDisabled', "{0} extension updates are available. All of them are for disabled extensions.", outdatedExtensions.length);
					} else {
						msgAvailableExtensions = localize('updatesAvailableIncludingDisabled', "{0} extension updates are available. {1} of them are for disabled extensions.", outdatedExtensions.length, disabledExtensionsCount);
					}
				}

				this.viewletService.openViewlet(VIEWLET_ID, true)
					.then(viewlet => viewlet as IExtensionsViewlet)
					.then(viewlet => viewlet.search(''));

B
Benjamin Pasero 已提交
895
				this.notificationService.info(msgAvailableExtensions);
896 897 898 899
			}
		);
	}

S
Sandeep Somavarapu 已提交
900
	run(): Promise<any> {
901
		return this.extensionsWorkbenchService.checkForUpdates().then(() => this.checkUpdatesAndNotify());
J
Joao Moreno 已提交
902 903 904
	}
}

S
Sandeep Somavarapu 已提交
905 906 907 908 909 910
export class ToggleAutoUpdateAction extends Action {

	constructor(
		id: string,
		label: string,
		private autoUpdateValue: boolean,
911
		@IConfigurationService private readonly configurationService: IConfigurationService
S
Sandeep Somavarapu 已提交
912 913 914
	) {
		super(id, label, '', true);
		this.updateEnablement();
915
		configurationService.onDidChangeConfiguration(() => this.updateEnablement());
S
Sandeep Somavarapu 已提交
916 917 918
	}

	private updateEnablement(): void {
919
		this.enabled = this.configurationService.getValue(AutoUpdateConfigurationKey) !== this.autoUpdateValue;
S
Sandeep Somavarapu 已提交
920 921
	}

S
Sandeep Somavarapu 已提交
922
	run(): Promise<any> {
923
		return this.configurationService.updateValue(AutoUpdateConfigurationKey, this.autoUpdateValue);
S
Sandeep Somavarapu 已提交
924 925 926 927 928
	}
}

export class EnableAutoUpdateAction extends ToggleAutoUpdateAction {

929
	static readonly ID = 'workbench.extensions.action.enableAutoUpdate';
S
Sandeep Somavarapu 已提交
930 931 932 933 934
	static LABEL = localize('enableAutoUpdate', "Enable Auto Updating Extensions");

	constructor(
		id = EnableAutoUpdateAction.ID,
		label = EnableAutoUpdateAction.LABEL,
935
		@IConfigurationService configurationService: IConfigurationService
S
Sandeep Somavarapu 已提交
936
	) {
937
		super(id, label, true, configurationService);
S
Sandeep Somavarapu 已提交
938 939 940 941 942
	}
}

export class DisableAutoUpdateAction extends ToggleAutoUpdateAction {

943
	static readonly ID = 'workbench.extensions.action.disableAutoUpdate';
S
Sandeep Somavarapu 已提交
944 945 946 947 948
	static LABEL = localize('disableAutoUpdate', "Disable Auto Updating Extensions");

	constructor(
		id = EnableAutoUpdateAction.ID,
		label = EnableAutoUpdateAction.LABEL,
949
		@IConfigurationService configurationService: IConfigurationService
S
Sandeep Somavarapu 已提交
950
	) {
951
		super(id, label, false, configurationService);
S
Sandeep Somavarapu 已提交
952 953 954
	}
}

955 956
export class UpdateAllAction extends Action {

957
	static readonly ID = 'workbench.extensions.action.updateAllExtensions';
958 959 960 961 962 963 964
	static LABEL = localize('updateAll', "Update All Extensions");

	private disposables: IDisposable[] = [];

	constructor(
		id = UpdateAllAction.ID,
		label = UpdateAllAction.LABEL,
965 966 967 968
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@INotificationService private readonly notificationService: INotificationService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IOpenerService private readonly openerService: IOpenerService
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
	) {
		super(id, label, '', false);

		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
		this.update();
	}

	private get outdated(): IExtension[] {
		return this.extensionsWorkbenchService.local.filter(e => e.outdated && e.state !== ExtensionState.Installing);
	}

	private update(): void {
		this.enabled = this.outdated.length > 0;
	}

S
Sandeep Somavarapu 已提交
984 985
	run(): Promise<any> {
		return Promise.all(this.outdated.map(e => this.install(e)));
J
Joao Moreno 已提交
986 987
	}

J
Johannes Rieken 已提交
988
	private install(extension: IExtension): Promise<any> {
R
Rob Lourens 已提交
989
		return this.extensionsWorkbenchService.install(extension).then(undefined, err => {
990
			if (!extension.gallery) {
J
Joao Moreno 已提交
991 992 993 994
				return this.notificationService.error(err);
			}

			console.error(err);
995

S
Sandeep Somavarapu 已提交
996
			return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService);
J
Joao Moreno 已提交
997
		});
998 999 1000 1001 1002 1003 1004 1005
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

S
Sandeep Somavarapu 已提交
1006
export class ReloadAction extends ExtensionAction {
1007

1008 1009
	private static readonly EnabledClass = 'extension-action reload';
	private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`;
1010

S
Sandeep Somavarapu 已提交
1011
	// Use delayer to wait for more updates
S
#66931  
Sandeep Somavarapu 已提交
1012
	private throttler: Throttler;
1013
	private disposables: IDisposable[] = [];
1014 1015

	constructor(
1016 1017 1018
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IWindowService private readonly windowService: IWindowService,
		@IExtensionService private readonly extensionService: IExtensionService,
1019
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
1020 1021
	) {
		super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false);
S
#66931  
Sandeep Somavarapu 已提交
1022
		this.throttler = new Throttler();
1023
		this.extensionService.onDidChangeExtensions(this.update, this, this.disposables);
1024 1025 1026
		this.update();
	}

S
Sandeep Somavarapu 已提交
1027
	update(): Promise<void> {
S
#66931  
Sandeep Somavarapu 已提交
1028
		return this.throttler.queue(() => {
1029 1030 1031
			this.enabled = false;
			this.tooltip = '';
			if (!this.extension) {
J
Johannes Rieken 已提交
1032
				return Promise.resolve(undefined);
1033 1034 1035
			}
			const state = this.extension.state;
			if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
J
Johannes Rieken 已提交
1036
				return Promise.resolve(undefined);
1037
			}
S
Sandeep Somavarapu 已提交
1038
			const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
S
Sandeep Somavarapu 已提交
1039 1040
			const local = this.extension.local || (installed && installed.local);
			if (local && local.manifest && local.manifest.contributes && local.manifest.contributes.localizations && local.manifest.contributes.localizations.length > 0) {
J
Johannes Rieken 已提交
1041
				return Promise.resolve(undefined);
S
Sandeep Somavarapu 已提交
1042
			}
1043
			return this.extensionService.getExtensions()
S
Sandeep Somavarapu 已提交
1044
				.then(runningExtensions => this.computeReloadState(runningExtensions, installed));
1045
		}).then(() => {
1046 1047 1048 1049
			this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
		});
	}

S
Sandeep Somavarapu 已提交
1050
	private computeReloadState(runningExtensions: IExtensionDescription[], installed: IExtension): void {
1051
		const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
1052
		const isDisabled = this.extension.local ? !this.extensionEnablementService.isEnabled(this.extension.local) : false;
1053
		const isEnabled = this.extension.local ? this.extensionEnablementService.isEnabled(this.extension.local) : false;
1054
		const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
1055 1056 1057

		if (installed && installed.local) {
			if (runningExtension) {
1058 1059
				const isDifferentVersionRunning = this.extension.version !== runningExtension.version;
				if (isDifferentVersionRunning && !isDisabled) {
S
Sandeep Somavarapu 已提交
1060 1061 1062 1063 1064 1065
					if (!(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) {
						// Requires reload to run the updated extension
						this.enabled = true;
						this.label = localize('reloadRequired', "Reload Required");
						this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to complete the updating of this extension.");
					}
1066 1067 1068 1069 1070
					return;
				}
				if (isDisabled) {
					// Requires reload to disable the extension
					this.enabled = true;
S
Sandeep Somavarapu 已提交
1071
					this.label = localize('reloadRequired', "Reload Required");
1072
					this.tooltip = localize('postDisableTooltip', "Please reload Visual Studio Code to complete the disabling of this extension.");
1073
					return;
1074 1075
				}
			} else {
S
#66931  
Sandeep Somavarapu 已提交
1076
				if (!isDisabled && !(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) {
1077
					this.enabled = true;
S
#66931  
Sandeep Somavarapu 已提交
1078
					if (isEnabled) {
S
Sandeep Somavarapu 已提交
1079
						this.label = localize('reloadRequired', "Reload Required");
S
#66931  
Sandeep Somavarapu 已提交
1080 1081
						this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension.");
					} else {
S
Sandeep Somavarapu 已提交
1082
						this.label = localize('reloadRequired', "Reload Required");
1083
						this.tooltip = localize('postInstallTooltip', "Please reload Visual Studio Code to complete the installation of this extension.");
S
Sandeep Somavarapu 已提交
1084
						alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName));
S
Explore  
Sandeep Somavarapu 已提交
1085
					}
1086
				}
1087 1088 1089 1090
			}
			return;
		}

1091
		if (isUninstalled && runningExtension) {
1092 1093
			// Requires reload to deactivate the extension
			this.enabled = true;
S
Sandeep Somavarapu 已提交
1094
			this.label = localize('reloadRequired', "Reload Required");
R
Rob Lourens 已提交
1095
			this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension.");
S
Sandeep Somavarapu 已提交
1096
			alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName));
1097 1098 1099 1100
			return;
		}
	}

S
Sandeep Somavarapu 已提交
1101 1102
	run(): Promise<any> {
		return Promise.resolve(this.windowService.reloadWindow());
1103
	}
1104 1105 1106 1107 1108

	dispose(): void {
		dispose(this.disposables);
		super.dispose();
	}
1109 1110
}

B
Benjamin Pasero 已提交
1111
export class OpenExtensionsViewletAction extends ShowViewletAction {
1112 1113 1114 1115 1116 1117 1118 1119

	static ID = VIEWLET_ID;
	static LABEL = localize('toggleExtensionsViewlet', "Show Extensions");

	constructor(
		id: string,
		label: string,
		@IViewletService viewletService: IViewletService,
B
Benjamin Pasero 已提交
1120 1121
		@IEditorGroupsService editorGroupService: IEditorGroupsService,
		@IPartService partService: IPartService
1122
	) {
B
Benjamin Pasero 已提交
1123
		super(id, label, VIEWLET_ID, viewletService, editorGroupService, partService);
1124 1125 1126 1127 1128 1129 1130 1131
	}
}

export class InstallExtensionsAction extends OpenExtensionsViewletAction {
	static ID = 'workbench.extensions.action.installExtensions';
	static LABEL = localize('installExtensions', "Install Extensions");
}

1132 1133
export class ShowEnabledExtensionsAction extends Action {

1134
	static readonly ID = 'workbench.extensions.action.showEnabledExtensions';
1135 1136 1137 1138 1139
	static LABEL = localize('showEnabledExtensions', 'Show Enabled Extensions');

	constructor(
		id: string,
		label: string,
1140
		@IViewletService private readonly viewletService: IViewletService
1141
	) {
R
Rob Lourens 已提交
1142
		super(id, label, undefined, true);
1143 1144
	}

J
Johannes Rieken 已提交
1145
	run(): Promise<void> {
1146 1147 1148
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
1149
				viewlet.search('@enabled ');
1150 1151 1152 1153 1154
				viewlet.focus();
			});
	}
}

1155 1156
export class ShowInstalledExtensionsAction extends Action {

1157
	static readonly ID = 'workbench.extensions.action.showInstalledExtensions';
1158 1159 1160 1161 1162
	static LABEL = localize('showInstalledExtensions', "Show Installed Extensions");

	constructor(
		id: string,
		label: string,
1163
		@IViewletService private readonly viewletService: IViewletService
1164
	) {
R
Rob Lourens 已提交
1165
		super(id, label, undefined, true);
1166 1167
	}

J
Johannes Rieken 已提交
1168
	run(): Promise<void> {
1169 1170 1171
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
1172
				viewlet.search('@installed ');
1173 1174 1175 1176 1177 1178 1179
				viewlet.focus();
			});
	}
}

export class ShowDisabledExtensionsAction extends Action {

1180
	static readonly ID = 'workbench.extensions.action.showDisabledExtensions';
1181 1182 1183 1184 1185
	static LABEL = localize('showDisabledExtensions', "Show Disabled Extensions");

	constructor(
		id: string,
		label: string,
1186
		@IViewletService private readonly viewletService: IViewletService
1187 1188 1189 1190
	) {
		super(id, label, 'null', true);
	}

J
Johannes Rieken 已提交
1191
	run(): Promise<void> {
1192 1193 1194 1195 1196 1197 1198 1199 1200
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@disabled ');
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
1201
export class ClearExtensionsInputAction extends Action {
1202

1203
	static readonly ID = 'workbench.extensions.action.clearExtensionsInput';
1204 1205 1206 1207 1208 1209 1210 1211
	static LABEL = localize('clearExtensionsInput', "Clear Extensions Input");

	private disposables: IDisposable[] = [];

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
1212
		@IViewletService private readonly viewletService: IViewletService
1213
	) {
S
Sandeep Somavarapu 已提交
1214
		super(id, label, 'clear-extensions', true);
1215 1216 1217 1218 1219 1220 1221 1222
		this.enabled = false;
		onSearchChange(this.onSearchChange, this, this.disposables);
	}

	private onSearchChange(value: string): void {
		this.enabled = !!value;
	}

J
Johannes Rieken 已提交
1223
	run(): Promise<void> {
S
Sandeep Somavarapu 已提交
1224 1225 1226 1227 1228 1229 1230 1231
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('');
				viewlet.focus();
			});
	}

1232 1233 1234 1235 1236
	dispose(): void {
		this.disposables = dispose(this.disposables);
	}
}

S
Sandeep Somavarapu 已提交
1237 1238 1239 1240 1241 1242 1243 1244
export class ShowBuiltInExtensionsAction extends Action {

	static readonly ID = 'workbench.extensions.action.listBuiltInExtensions';
	static LABEL = localize('showBuiltInExtensions', "Show Built-in Extensions");

	constructor(
		id: string,
		label: string,
1245
		@IViewletService private readonly viewletService: IViewletService
S
Sandeep Somavarapu 已提交
1246
	) {
R
Rob Lourens 已提交
1247
		super(id, label, undefined, true);
S
Sandeep Somavarapu 已提交
1248 1249
	}

J
Johannes Rieken 已提交
1250
	run(): Promise<void> {
S
Sandeep Somavarapu 已提交
1251 1252 1253 1254 1255 1256 1257 1258 1259
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@builtin ');
				viewlet.focus();
			});
	}
}

1260 1261
export class ShowOutdatedExtensionsAction extends Action {

1262
	static readonly ID = 'workbench.extensions.action.listOutdatedExtensions';
1263 1264 1265 1266 1267
	static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions");

	constructor(
		id: string,
		label: string,
1268
		@IViewletService private readonly viewletService: IViewletService
1269
	) {
R
Rob Lourens 已提交
1270
		super(id, label, undefined, true);
1271 1272
	}

J
Johannes Rieken 已提交
1273
	run(): Promise<void> {
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@outdated ');
				viewlet.focus();
			});
	}
}

export class ShowPopularExtensionsAction extends Action {

1285
	static readonly ID = 'workbench.extensions.action.showPopularExtensions';
1286 1287 1288 1289 1290
	static LABEL = localize('showPopularExtensions', "Show Popular Extensions");

	constructor(
		id: string,
		label: string,
1291
		@IViewletService private readonly viewletService: IViewletService
1292
	) {
R
Rob Lourens 已提交
1293
		super(id, label, undefined, true);
1294 1295
	}

J
Johannes Rieken 已提交
1296
	run(): Promise<void> {
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@sort:installs ');
				viewlet.focus();
			});
	}
}

export class ShowRecommendedExtensionsAction extends Action {

1308
	static readonly ID = 'workbench.extensions.action.showRecommendedExtensions';
1309 1310 1311 1312 1313
	static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions");

	constructor(
		id: string,
		label: string,
1314
		@IViewletService private readonly viewletService: IViewletService
1315
	) {
R
Rob Lourens 已提交
1316
		super(id, label, undefined, true);
1317 1318
	}

J
Johannes Rieken 已提交
1319
	run(): Promise<void> {
1320 1321 1322 1323 1324 1325 1326 1327 1328
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@recommended ');
				viewlet.focus();
			});
	}
}

1329 1330
export class InstallWorkspaceRecommendedExtensionsAction extends Action {

1331
	static readonly ID = 'workbench.extensions.action.installWorkspaceRecommendedExtensions';
1332 1333
	static LABEL = localize('installWorkspaceRecommendedExtensions', "Install All Workspace Recommended Extensions");

S
Sandeep Somavarapu 已提交
1334 1335 1336 1337
	private _recommendations: IExtensionRecommendation[] = [];
	get recommendations(): IExtensionRecommendation[] { return this._recommendations; }
	set recommendations(recommendations: IExtensionRecommendation[]) { this._recommendations = recommendations; this.enabled = this._recommendations.length > 0; }

1338 1339 1340
	constructor(
		id: string = InstallWorkspaceRecommendedExtensionsAction.ID,
		label: string = InstallWorkspaceRecommendedExtensionsAction.LABEL,
S
Sandeep Somavarapu 已提交
1341
		recommendations: IExtensionRecommendation[],
1342 1343 1344 1345 1346
		@IViewletService private readonly viewletService: IViewletService,
		@INotificationService private readonly notificationService: INotificationService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IOpenerService private readonly openerService: IOpenerService,
		@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService
1347 1348
	) {
		super(id, label, 'extension-action');
S
Sandeep Somavarapu 已提交
1349
		this.recommendations = recommendations;
1350 1351
	}

J
Johannes Rieken 已提交
1352
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
1353 1354 1355 1356 1357 1358
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@recommended ');
				viewlet.focus();
				const names = this.recommendations.map(({ extensionId }) => extensionId);
1359
				return this.extensionWorkbenchService.queryGallery({ names, source: 'install-all-workspace-recommendations' }).then(pager => {
J
Johannes Rieken 已提交
1360
					let installPromises: Promise<any>[] = [];
S
Sandeep Somavarapu 已提交
1361 1362
					let model = new PagedModel(pager);
					for (let i = 0; i < pager.total; i++) {
1363
						installPromises.push(model.resolve(i, CancellationToken.None).then(e => {
R
Rob Lourens 已提交
1364
							return this.extensionWorkbenchService.install(e).then(undefined, err => {
1365
								console.error(err);
S
Sandeep Somavarapu 已提交
1366
								return promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.identifier.id), err, this.instantiationService, this.notificationService, this.openerService);
1367
							});
S
Sandeep Somavarapu 已提交
1368 1369
						}));
					}
S
Sandeep Somavarapu 已提交
1370
					return Promise.all(installPromises);
1371
				});
S
Sandeep Somavarapu 已提交
1372
			});
1373 1374 1375
	}
}

1376
export class InstallRecommendedExtensionAction extends Action {
1377

1378
	static readonly ID = 'workbench.extensions.action.installRecommendedExtension';
1379 1380 1381 1382 1383
	static LABEL = localize('installRecommendedExtension', "Install Recommended Extension");

	private extensionId: string;

	constructor(
1384
		extensionId: string,
1385 1386 1387 1388 1389
		@IViewletService private readonly viewletService: IViewletService,
		@INotificationService private readonly notificationService: INotificationService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IOpenerService private readonly openerService: IOpenerService,
		@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService
1390
	) {
R
Rob Lourens 已提交
1391
		super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, undefined, false);
1392 1393 1394
		this.extensionId = extensionId;
	}

J
Johannes Rieken 已提交
1395
	run(): Promise<any> {
1396
		return this.viewletService.openViewlet(VIEWLET_ID, true)
1397 1398
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
1399
				viewlet.search('@recommended ');
1400
				viewlet.focus();
1401
				return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 })
S
Sandeep Somavarapu 已提交
1402 1403
					.then(pager => {
						if (pager && pager.firstPage && pager.firstPage.length) {
1404 1405 1406 1407
							const extension = pager.firstPage[0];
							return this.extensionWorkbenchService.install(extension)
								.then(() => null, err => {
									console.error(err);
S
Sandeep Somavarapu 已提交
1408
									return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService);
1409
								});
S
Sandeep Somavarapu 已提交
1410
						}
1411
						return null;
S
Sandeep Somavarapu 已提交
1412
					});
1413
			});
1414 1415 1416
	}
}

1417 1418 1419 1420
export class IgnoreExtensionRecommendationAction extends Action {

	static readonly ID = 'extensions.ignore';

1421
	private static readonly Class = 'extension-action ignore';
1422 1423 1424 1425 1426

	private disposables: IDisposable[] = [];
	extension: IExtension;

	constructor(
1427
		@IExtensionTipsService private readonly extensionsTipsService: IExtensionTipsService,
1428
	) {
1429
		super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation');
1430 1431 1432 1433 1434 1435

		this.class = IgnoreExtensionRecommendationAction.Class;
		this.tooltip = localize('ignoreExtensionRecommendation', "Do not recommend this extension again");
		this.enabled = true;
	}

S
Sandeep Somavarapu 已提交
1436
	public run(): Promise<any> {
S
Sandeep Somavarapu 已提交
1437
		this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.identifier.id, true);
S
Sandeep Somavarapu 已提交
1438
		return Promise.resolve(null);
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

export class UndoIgnoreExtensionRecommendationAction extends Action {

	static readonly ID = 'extensions.ignore';

	private static readonly Class = 'extension-action undo-ignore';

	private disposables: IDisposable[] = [];
	extension: IExtension;

	constructor(
1457
		@IExtensionTipsService private readonly extensionsTipsService: IExtensionTipsService,
1458 1459 1460 1461 1462 1463 1464 1465
	) {
		super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo');

		this.class = UndoIgnoreExtensionRecommendationAction.Class;
		this.tooltip = localize('undo', "Undo");
		this.enabled = true;
	}

S
Sandeep Somavarapu 已提交
1466
	public run(): Promise<any> {
S
Sandeep Somavarapu 已提交
1467
		this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.identifier.id, false);
S
Sandeep Somavarapu 已提交
1468
		return Promise.resolve(null);
1469 1470 1471 1472 1473 1474 1475 1476
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

1477

1478 1479
export class ShowRecommendedKeymapExtensionsAction extends Action {

1480
	static readonly ID = 'workbench.extensions.action.showRecommendedKeymapExtensions';
1481 1482 1483 1484 1485
	static SHORT_LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps");

	constructor(
		id: string,
		label: string,
1486
		@IViewletService private readonly viewletService: IViewletService
1487
	) {
R
Rob Lourens 已提交
1488
		super(id, label, undefined, true);
1489 1490
	}

J
Johannes Rieken 已提交
1491
	run(): Promise<void> {
1492 1493 1494 1495 1496 1497 1498 1499 1500
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@recommended:keymaps ');
				viewlet.focus();
			});
	}
}

1501
export class ShowLanguageExtensionsAction extends Action {
1502

1503
	static readonly ID = 'workbench.extensions.action.showLanguageExtensions';
1504
	static SHORT_LABEL = localize('showLanguageExtensionsShort', "Language Extensions");
1505 1506 1507 1508

	constructor(
		id: string,
		label: string,
1509
		@IViewletService private readonly viewletService: IViewletService
1510
	) {
R
Rob Lourens 已提交
1511
		super(id, label, undefined, true);
1512 1513
	}

J
Johannes Rieken 已提交
1514
	run(): Promise<void> {
1515 1516 1517
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
C
Christof Marti 已提交
1518
				viewlet.search('@category:"programming languages" @sort:installs ');
1519 1520 1521 1522 1523
				viewlet.focus();
			});
	}
}

C
Christof Marti 已提交
1524 1525
export class ShowAzureExtensionsAction extends Action {

1526
	static readonly ID = 'workbench.extensions.action.showAzureExtensions';
C
Christof Marti 已提交
1527 1528 1529 1530 1531
	static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions");

	constructor(
		id: string,
		label: string,
1532
		@IViewletService private readonly viewletService: IViewletService
C
Christof Marti 已提交
1533
	) {
R
Rob Lourens 已提交
1534
		super(id, label, undefined, true);
C
Christof Marti 已提交
1535 1536
	}

J
Johannes Rieken 已提交
1537
	run(): Promise<void> {
C
Christof Marti 已提交
1538 1539 1540 1541 1542 1543 1544 1545 1546
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search('@sort:installs azure ');
				viewlet.focus();
			});
	}
}

1547 1548 1549 1550 1551 1552 1553 1554 1555 1556
export class ChangeSortAction extends Action {

	private query: Query;
	private disposables: IDisposable[] = [];

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
		private sortBy: string,
1557
		@IViewletService private readonly viewletService: IViewletService
1558
	) {
R
Rob Lourens 已提交
1559
		super(id, label, undefined, true);
1560

J
Joao Moreno 已提交
1561
		if (sortBy === undefined) {
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
			throw new Error('bad arguments');
		}

		this.query = Query.parse('');
		this.enabled = false;
		onSearchChange(this.onSearchChange, this, this.disposables);
	}

	private onSearchChange(value: string): void {
		const query = Query.parse(value);
1572
		this.query = new Query(query.value, this.sortBy || query.sortBy, query.groupBy);
1573
		this.enabled = !!value && this.query.isValid() && !this.query.equals(query);
1574 1575
	}

J
Johannes Rieken 已提交
1576
	run(): Promise<void> {
1577 1578 1579 1580 1581 1582 1583 1584 1585
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search(this.query.toString());
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
1586 1587 1588 1589
export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution {

	private workspaceContextKey = new RawContextKey<boolean>('workspaceRecommendations', true);
	private workspaceFolderContextKey = new RawContextKey<boolean>('workspaceFolderRecommendations', true);
1590 1591
	private addToWorkspaceRecommendationsContextKey = new RawContextKey<boolean>('addToWorkspaceRecommendations', false);
	private addToWorkspaceFolderRecommendationsContextKey = new RawContextKey<boolean>('addToWorkspaceFolderRecommendations', false);
S
Sandeep Somavarapu 已提交
1592 1593 1594

	constructor(
		@IContextKeyService contextKeyService: IContextKeyService,
1595 1596
		@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService
S
Sandeep Somavarapu 已提交
1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
	) {
		super();
		const boundWorkspaceContextKey = this.workspaceContextKey.bindTo(contextKeyService);
		boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE);
		this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE)));

		const boundWorkspaceFolderContextKey = this.workspaceFolderContextKey.bindTo(contextKeyService);
		boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0);
		this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0)));

1607 1608 1609 1610 1611 1612 1613 1614 1615 1616
		const boundAddToWorkspaceRecommendationsContextKey = this.addToWorkspaceRecommendationsContextKey.bindTo(contextKeyService);
		boundAddToWorkspaceRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE);
		this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceRecommendationsContextKey.set(
			editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE)));
		this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundAddToWorkspaceRecommendationsContextKey.set(
			editorService.activeEditor instanceof ExtensionsInput && workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE)));

		const boundAddToWorkspaceFolderRecommendationsContextKey = this.addToWorkspaceFolderRecommendationsContextKey.bindTo(contextKeyService);
		boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput);
		this._register(editorService.onDidActiveEditorChange(() => boundAddToWorkspaceFolderRecommendationsContextKey.set(editorService.activeEditor instanceof ExtensionsInput)));
1617

S
Sandeep Somavarapu 已提交
1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
		this.registerCommands();
	}

	private registerCommands(): void {
		CommandsRegistry.registerCommand(ConfigureWorkspaceRecommendedExtensionsAction.ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run();
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
				id: ConfigureWorkspaceRecommendedExtensionsAction.ID,
B
Benjamin Pasero 已提交
1628
				title: { value: `${ExtensionsLabel}: ${ConfigureWorkspaceRecommendedExtensionsAction.LABEL}`, original: 'Extensions: Configure Recommended Extensions (Workspace)' },
S
Sandeep Somavarapu 已提交
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
			},
			when: this.workspaceContextKey
		});

		CommandsRegistry.registerCommand(ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run();
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
				id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID,
B
Benjamin Pasero 已提交
1639
				title: { value: `${ExtensionsLabel}: ${ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL}`, original: 'Extensions: Configure Recommended Extensions (Workspace Folder)' },
S
Sandeep Somavarapu 已提交
1640 1641 1642
			},
			when: this.workspaceFolderContextKey
		});
1643

1644 1645 1646 1647
		CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.ADD_ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService)
				.createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.ADD_ID, AddToWorkspaceRecommendationsAction.ADD_LABEL)
				.run(AddToWorkspaceRecommendationsAction.ADD);
1648 1649 1650
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
1651
				id: AddToWorkspaceRecommendationsAction.ADD_ID,
B
Benjamin Pasero 已提交
1652
				title: { value: `${ExtensionsLabel}: ${AddToWorkspaceRecommendationsAction.ADD_LABEL}`, original: 'Extensions: Add to Recommended Extensions (Workspace)' }
1653
			},
1654
			when: this.addToWorkspaceRecommendationsContextKey
1655 1656
		});

1657 1658 1659 1660 1661 1662 1663 1664
		CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.ADD_ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService)
				.createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.ADD_ID, AddToWorkspaceFolderRecommendationsAction.ADD_LABEL)
				.run(AddToWorkspaceRecommendationsAction.ADD);
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
				id: AddToWorkspaceFolderRecommendationsAction.ADD_ID,
B
Benjamin Pasero 已提交
1665
				title: { value: `${ExtensionsLabel}: ${AddToWorkspaceFolderRecommendationsAction.ADD_LABEL}`, original: 'Extensions: Add to Recommended Extensions (Workspace Folder)' }
1666 1667 1668
			},
			when: this.addToWorkspaceFolderRecommendationsContextKey
		});
S
Sandeep Somavarapu 已提交
1669

1670 1671 1672 1673 1674 1675 1676 1677
		CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.IGNORE_ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService)
				.createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.IGNORE_ID, AddToWorkspaceRecommendationsAction.IGNORE_LABEL)
				.run(AddToWorkspaceRecommendationsAction.IGNORE);
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
				id: AddToWorkspaceRecommendationsAction.IGNORE_ID,
B
Benjamin Pasero 已提交
1678
				title: { value: `${ExtensionsLabel}: ${AddToWorkspaceRecommendationsAction.IGNORE_LABEL}`, original: 'Extensions: Ignore Recommended Extension (Workspace)' }
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690
			},
			when: this.addToWorkspaceRecommendationsContextKey
		});

		CommandsRegistry.registerCommand(AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService)
				.createInstance(AddToWorkspaceFolderRecommendationsAction, AddToWorkspaceFolderRecommendationsAction.IGNORE_ID, AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL)
				.run(AddToWorkspaceRecommendationsAction.IGNORE);
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
				id: AddToWorkspaceFolderRecommendationsAction.IGNORE_ID,
B
Benjamin Pasero 已提交
1691
				title: { value: `${ExtensionsLabel}: ${AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL}`, original: 'Extensions: Ignore Recommended Extension (Workspace Folder)' }
1692 1693 1694 1695
			},
			when: this.addToWorkspaceFolderRecommendationsContextKey
		});
	}
1696 1697 1698 1699 1700 1701 1702 1703
}

export abstract class AbstractConfigureRecommendedExtensionsAction extends Action {

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService protected contextService: IWorkspaceContextService,
1704
		@IFileService private readonly fileService: IFileService,
1705
		@IEditorService protected editorService: IEditorService,
1706 1707
		@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
		@ITextModelService private readonly textModelResolverService: ITextModelService
1708
	) {
1709
		super(id, label);
1710 1711
	}

S
Sandeep Somavarapu 已提交
1712
	protected openExtensionsFile(extensionsFileResource: URI): Promise<any> {
1713
		return this.getOrCreateExtensionsFile(extensionsFileResource)
S
Sandeep Somavarapu 已提交
1714 1715 1716 1717 1718 1719 1720 1721 1722
			.then(({ created, content }) =>
				this.getSelectionPosition(content, extensionsFileResource, ['recommendations'])
					.then(selection => this.editorService.openEditor({
						resource: extensionsFileResource,
						options: {
							pinned: created,
							selection
						}
					})),
S
Sandeep Somavarapu 已提交
1723
				error => Promise.reject(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error))));
1724 1725
	}

S
Sandeep Somavarapu 已提交
1726
	protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {
1727
		return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
S
Sandeep Somavarapu 已提交
1728
			.then(content => this.getSelectionPosition(content.value, content.resource, ['extensions', 'recommendations']))
1729 1730 1731
			.then(selection => this.editorService.openEditor({
				resource: workspaceConfigurationFile,
				options: {
B
Benjamin Pasero 已提交
1732 1733
					selection,
					forceReload: true // because content has changed
1734 1735 1736 1737
				}
			}));
	}

1738 1739 1740 1741 1742 1743 1744 1745 1746
	protected addExtensionToWorkspaceConfig(workspaceConfigurationFile: URI, extensionId: string, shouldRecommend: boolean) {
		return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
			.then(content => {
				const extensionIdLowerCase = extensionId.toLowerCase();
				const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value) || {})['extensions'] || {};
				let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || [];
				let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || [];

				if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) {
S
Sandeep Somavarapu 已提交
1747
					return Promise.resolve(null);
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764
				}

				insertInto.push(extensionId);
				removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase);

				return this.jsonEditingService.write(workspaceConfigurationFile,
					{
						key: 'extensions',
						value: {
							recommendations: shouldRecommend ? insertInto : removeFrom,
							unwantedRecommendations: shouldRecommend ? removeFrom : insertInto
						}
					},
					true);
			});
	}

S
Sandeep Somavarapu 已提交
1765
	protected addExtensionToWorkspaceFolderConfig(extensionsFileResource: URI, extensionId: string, shouldRecommend: boolean): Promise<any> {
1766 1767 1768
		return this.getOrCreateExtensionsFile(extensionsFileResource)
			.then(({ content }) => {
				const extensionIdLowerCase = extensionId.toLowerCase();
1769 1770 1771
				const extensionsConfigContent: IExtensionsConfigContent = json.parse(content) || {};
				let insertInto = shouldRecommend ? extensionsConfigContent.recommendations || [] : extensionsConfigContent.unwantedRecommendations || [];
				let removeFrom = shouldRecommend ? extensionsConfigContent.unwantedRecommendations || [] : extensionsConfigContent.recommendations || [];
1772

1773
				if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) {
S
Sandeep Somavarapu 已提交
1774
					return Promise.resolve(null);
1775 1776
				}

1777 1778
				insertInto.push(extensionId);

1779
				let removeFromPromise: Promise<void> = Promise.resolve();
1780 1781 1782
				if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) {
					removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase);
					removeFromPromise = this.jsonEditingService.write(extensionsFileResource,
1783
						{
1784 1785
							key: shouldRecommend ? 'unwantedRecommendations' : 'recommendations',
							value: removeFrom
1786 1787 1788 1789
						},
						true);
				}

1790
				return removeFromPromise.then(() =>
1791 1792
					this.jsonEditingService.write(extensionsFileResource,
						{
1793 1794
							key: shouldRecommend ? 'recommendations' : 'unwantedRecommendations',
							value: insertInto
1795 1796 1797 1798 1799 1800
						},
						true)
				);
			});
	}

S
Sandeep Somavarapu 已提交
1801 1802
	protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
		return Promise.resolve(this.fileService.resolveContent(extensionsFileResource))
1803 1804 1805 1806 1807
			.then(content => {
				return (json.parse(content.value) || {})['extensions'] || {};
			}, err => ({ recommendations: [], unwantedRecommendations: [] }));
	}

S
Sandeep Somavarapu 已提交
1808 1809
	protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
		return Promise.resolve(this.fileService.resolveContent(extensionsFileResource))
1810
			.then(content => {
1811 1812
				return (<IExtensionsConfigContent>json.parse(content.value));
			}, err => ({ recommendations: [], unwantedRecommendations: [] }));
1813 1814
	}

S
Sandeep Somavarapu 已提交
1815 1816
	private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IContent> {
		return Promise.resolve(this.fileService.resolveContent(workspaceConfigurationFile))
1817
			.then(content => {
1818
				const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value)['extensions'];
1819 1820 1821 1822 1823 1824 1825 1826
				if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {
					return this.jsonEditingService.write(workspaceConfigurationFile, { key: 'extensions', value: { recommendations: [] } }, true)
						.then(() => this.fileService.resolveContent(workspaceConfigurationFile));
				}
				return content;
			});
	}

J
Johannes Rieken 已提交
1827
	private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): Promise<ITextEditorSelection | undefined> {
S
Sandeep Somavarapu 已提交
1828 1829
		const tree = json.parseTree(content);
		const node = json.findNodeAtLocation(tree, path);
1830
		if (node && node.parent && node.parent.children) {
S
Sandeep Somavarapu 已提交
1831 1832 1833
			const recommendationsValueNode = node.parent.children[1];
			const lastExtensionNode = recommendationsValueNode.children && recommendationsValueNode.children.length ? recommendationsValueNode.children[recommendationsValueNode.children.length - 1] : null;
			const offset = lastExtensionNode ? lastExtensionNode.offset + lastExtensionNode.length : recommendationsValueNode.offset + 1;
S
Sandeep Somavarapu 已提交
1834
			return Promise.resolve(this.textModelResolverService.createModelReference(resource))
1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845
				.then(reference => {
					const position = reference.object.textEditorModel.getPositionAt(offset);
					reference.dispose();
					return <ITextEditorSelection>{
						startLineNumber: position.lineNumber,
						startColumn: position.column,
						endLineNumber: position.lineNumber,
						endColumn: position.column,
					};
				});
		}
J
Johannes Rieken 已提交
1846
		return Promise.resolve(undefined);
1847 1848
	}

S
Sandeep Somavarapu 已提交
1849 1850
	private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> {
		return Promise.resolve(this.fileService.resolveContent(extensionsFileResource)).then(content => {
S
Sandeep Somavarapu 已提交
1851
			return { created: false, extensionsFileResource, content: content.value };
1852 1853
		}, err => {
			return this.fileService.updateContent(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
S
Sandeep Somavarapu 已提交
1854
				return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
1855 1856 1857 1858 1859 1860
			});
		});
	}
}

export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {
1861

1862
	static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';
1863 1864
	static LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)");

1865 1866
	private disposables: IDisposable[] = [];

1867 1868 1869
	constructor(
		id: string,
		label: string,
1870 1871
		@IFileService fileService: IFileService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
1872
		@IEditorService editorService: IEditorService,
1873 1874
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService
1875
	) {
1876
		super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
1877 1878 1879 1880 1881 1882
		this.contextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
		this.update();
	}

	private update(): void {
		this.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
1883 1884
	}

1885
	public run(): Promise<void> {
1886 1887
		switch (this.contextService.getWorkbenchState()) {
			case WorkbenchState.FOLDER:
1888
				return this.openExtensionsFile(this.contextService.getWorkspace().folders[0].toResource(paths.join('.vscode', 'extensions.json')));
1889
			case WorkbenchState.WORKSPACE:
1890
				return this.openWorkspaceConfigurationFile(this.contextService.getWorkspace().configuration!);
1891
		}
1892
		return Promise.resolve();
1893 1894
	}

1895 1896 1897 1898 1899
	dispose(): void {
		this.disposables = dispose(this.disposables);
		super.dispose();
	}
}
1900

1901 1902
export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {

1903
	static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';
1904 1905 1906 1907 1908 1909 1910 1911 1912
	static LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)");

	private disposables: IDisposable[] = [];

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
1913
		@IEditorService editorService: IEditorService,
1914
		@IJSONEditingService jsonEditingService: IJSONEditingService,
S
Sandeep Somavarapu 已提交
1915
		@ITextModelService textModelResolverService: ITextModelService,
1916
		@ICommandService private readonly commandService: ICommandService
1917 1918 1919 1920
	) {
		super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
		this.contextService.onDidChangeWorkspaceFolders(() => this.update(), this, this.disposables);
		this.update();
1921 1922
	}

1923 1924 1925
	private update(): void {
		this.enabled = this.contextService.getWorkspace().folders.length > 0;
	}
1926

S
Sandeep Somavarapu 已提交
1927
	public run(): Promise<any> {
1928
		const folderCount = this.contextService.getWorkspace().folders.length;
S
Sandeep Somavarapu 已提交
1929 1930
		const pickFolderPromise = folderCount === 1 ? Promise.resolve(this.contextService.getWorkspace().folders[0]) : this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);
		return Promise.resolve(pickFolderPromise)
S
Sandeep Somavarapu 已提交
1931 1932
			.then(workspaceFolder => {
				if (workspaceFolder) {
1933
					return this.openExtensionsFile(workspaceFolder.toResource(paths.join('.vscode', 'extensions.json')));
1934
				}
S
Sandeep Somavarapu 已提交
1935
				return null;
1936
			});
1937
	}
1938 1939 1940 1941 1942

	dispose(): void {
		this.disposables = dispose(this.disposables);
		super.dispose();
	}
1943 1944
}

1945 1946 1947 1948 1949 1950 1951
export class AddToWorkspaceFolderRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction {
	static readonly ADD = true;
	static readonly IGNORE = false;
	static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceFolderRecommendations';
	static readonly ADD_LABEL = localize('addToWorkspaceFolderRecommendations', "Add to Recommended Extensions (Workspace Folder)");
	static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations';
	static readonly IGNORE_LABEL = localize('addToWorkspaceFolderIgnoredRecommendations', "Ignore Recommended Extension (Workspace Folder)");
1952 1953 1954 1955 1956 1957 1958 1959 1960

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService,
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService,
1961 1962
		@ICommandService private readonly commandService: ICommandService,
		@INotificationService private readonly notificationService: INotificationService
1963
	) {
1964
		super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
1965 1966
	}

S
Sandeep Somavarapu 已提交
1967
	run(shouldRecommend: boolean): Promise<void> {
1968
		if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension) {
1969
			return Promise.resolve();
1970 1971 1972
		}
		const folders = this.contextService.getWorkspace().folders;
		if (!folders || !folders.length) {
1973
			this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.noWorkspace', 'There are no workspace folders open to add recommendations.'));
1974
			return Promise.resolve();
1975 1976
		}

S
Sandeep Somavarapu 已提交
1977
		const extensionId = this.editorService.activeEditor.extension.identifier;
1978
		const pickFolderPromise = folders.length === 1
S
Sandeep Somavarapu 已提交
1979
			? Promise.resolve(folders[0])
1980
			: this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);
S
Sandeep Somavarapu 已提交
1981
		return Promise.resolve(pickFolderPromise)
1982 1983
			.then(workspaceFolder => {
				if (!workspaceFolder) {
1984
					return Promise.resolve();
1985 1986
				}
				const configurationFile = workspaceFolder.toResource(paths.join('.vscode', 'extensions.json'));
1987
				return this.getWorkspaceFolderExtensionsConfigContent(configurationFile).then(content => {
S
Sandeep Somavarapu 已提交
1988
					const extensionIdLowerCase = extensionId.id.toLowerCase();
1989
					if (shouldRecommend) {
1990
						if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
1991
							this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s recommendations.'));
1992
							return Promise.resolve();
1993 1994
						}

S
Sandeep Somavarapu 已提交
1995
						return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => {
1996 1997 1998 1999 2000 2001
							this.notificationService.prompt(Severity.Info,
								localize('AddToWorkspaceFolderRecommendations.success', 'The extension was successfully added to this workspace folder\'s recommendations.'),
								[{
									label: localize('viewChanges', "View Changes"),
									run: () => this.openExtensionsFile(configurationFile)
								}]);
2002 2003 2004
						}, err => {
							this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
						});
2005
					}
2006
					else {
2007
						if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
2008
							this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s unwanted recommendations.'));
2009
							return Promise.resolve();
2010
						}
2011

S
Sandeep Somavarapu 已提交
2012
						return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => {
2013 2014 2015 2016 2017 2018
							this.notificationService.prompt(Severity.Info,
								localize('AddToWorkspaceFolderIgnoredRecommendations.success', 'The extension was successfully added to this workspace folder\'s unwanted recommendations.'),
								[{
									label: localize('viewChanges', "View Changes"),
									run: () => this.openExtensionsFile(configurationFile)
								}]);
2019 2020 2021 2022
						}, err => {
							this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
						});
					}
2023 2024 2025 2026 2027
				});
			});
	}
}

2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043
export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecommendedExtensionsAction {
	static readonly ADD = true;
	static readonly IGNORE = false;
	static readonly ADD_ID = 'workbench.extensions.action.addToWorkspaceRecommendations';
	static readonly ADD_LABEL = localize('addToWorkspaceRecommendations', "Add to Recommended Extensions (Workspace)");
	static readonly IGNORE_ID = 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations';
	static readonly IGNORE_LABEL = localize('addToWorkspaceIgnoredRecommendations', "Ignore Recommended Extension (Workspace)");

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService,
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService,
2044
		@INotificationService private readonly notificationService: INotificationService
2045 2046 2047 2048
	) {
		super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService);
	}

S
Sandeep Somavarapu 已提交
2049
	run(shouldRecommend: boolean): Promise<void> {
2050 2051
		const workspaceConfig = this.contextService.getWorkspace().configuration;

2052 2053 2054 2055
		if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension || !workspaceConfig) {
			return Promise.resolve();
		}

S
Sandeep Somavarapu 已提交
2056
		const extensionId = this.editorService.activeEditor.extension.identifier;
2057 2058

		return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => {
S
Sandeep Somavarapu 已提交
2059
			const extensionIdLowerCase = extensionId.id.toLowerCase();
2060 2061 2062
			if (shouldRecommend) {
				if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
					this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.'));
2063
					return Promise.resolve();
2064 2065
				}

S
Sandeep Somavarapu 已提交
2066
				return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => {
2067 2068 2069 2070 2071 2072 2073
					this.notificationService.prompt(Severity.Info,
						localize('AddToWorkspaceRecommendations.success', 'The extension was successfully added to this workspace\'s recommendations.'),
						[{
							label: localize('viewChanges', "View Changes"),
							run: () => this.openWorkspaceConfigurationFile(workspaceConfig)
						}]);

2074 2075 2076 2077 2078 2079
				}, err => {
					this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err));
				});
			} else {
				if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
					this.notificationService.info(localize('AddToWorkspaceUnwantedRecommendations.alreadyExists', 'This extension is already present in workspace unwanted recommendations.'));
2080
					return Promise.resolve();
2081 2082
				}

S
Sandeep Somavarapu 已提交
2083
				return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => {
2084 2085 2086 2087 2088 2089
					this.notificationService.prompt(Severity.Info,
						localize('AddToWorkspaceUnwantedRecommendations.success', 'The extension was successfully added to this workspace\'s unwanted recommendations.'),
						[{
							label: localize('viewChanges', "View Changes"),
							run: () => this.openWorkspaceConfigurationFile(workspaceConfig)
						}]);
2090 2091 2092 2093 2094 2095 2096 2097
				}, err => {
					this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err));
				});
			}
		});
	}
}

S
#66931  
Sandeep Somavarapu 已提交
2098 2099 2100 2101 2102 2103 2104
export class StatusLabelAction extends Action implements IExtensionContainer {

	private static readonly ENABLED_CLASS = 'extension-status-label';
	private static readonly DISABLED_CLASS = `${StatusLabelAction.ENABLED_CLASS} hide`;

	private status: ExtensionState | null = null;
	private enablementState: EnablementState | null = null;
S
Sandeep Somavarapu 已提交
2105
	private version: string | null = null;
S
#66931  
Sandeep Somavarapu 已提交
2106 2107 2108 2109

	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) {
S
Sandeep Somavarapu 已提交
2110
		if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) {
S
#66931  
Sandeep Somavarapu 已提交
2111 2112 2113
			// Different extension. Reset
			this.status = null;
			this.enablementState = null;
S
Sandeep Somavarapu 已提交
2114
			this.version = null;
S
#66931  
Sandeep Somavarapu 已提交
2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140
		}
		this._extension = extension;
		this.update();
	}

	constructor(
		@IExtensionService private readonly extensionService: IExtensionService
	) {
		super('extensions.action.statusLabel', '', StatusLabelAction.DISABLED_CLASS, false);
	}

	update(): void {
		this.computeLabel()
			.then(label => {
				this.label = label || '';
				this.class = label ? StatusLabelAction.ENABLED_CLASS : StatusLabelAction.DISABLED_CLASS;
			});
	}

	private async computeLabel(): Promise<string | null> {
		if (!this.extension) {
			return null;
		}

		const currentStatus = this.status;
		const currentEnablementState = this.enablementState;
S
Sandeep Somavarapu 已提交
2141
		const currentVersion = this.version;
S
#66931  
Sandeep Somavarapu 已提交
2142 2143
		this.status = this.extension.state;
		this.enablementState = this.extension.enablementState;
S
Sandeep Somavarapu 已提交
2144
		this.version = this.extension.version;
S
#66931  
Sandeep Somavarapu 已提交
2145 2146

		const runningExtensions = await this.extensionService.getExtensions();
S
Sandeep Somavarapu 已提交
2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165
		const canAddExtension = () => {
			const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
			if (this.extension.local) {
				if (runningExtension && this.extension.version === runningExtension.version) {
					return true;
				}
				return this.extensionService.canAddExtension(toExtensionDescription(this.extension.local));
			}
			return false;
		};
		const canRemoveExtension = () => {
			if (this.extension.local) {
				if (runningExtensions.every(e => !areSameExtensions({ id: e.identifier.value }, this.extension.identifier))) {
					return true;
				}
				return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension.local));
			}
			return false;
		};
S
#66931  
Sandeep Somavarapu 已提交
2166 2167 2168

		if (currentStatus !== null) {
			if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) {
S
Sandeep Somavarapu 已提交
2169
				return canAddExtension() ? currentVersion !== this.version ? localize('updated', "Updated") : localize('installed', "Installed") : null;
S
#66931  
Sandeep Somavarapu 已提交
2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196
			}
			if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {
				return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;
			}
		}

		if (currentEnablementState !== null) {
			const currentlyEnabled = currentEnablementState === EnablementState.Enabled || currentEnablementState === EnablementState.WorkspaceEnabled;
			const enabled = this.enablementState === EnablementState.Enabled || this.enablementState === EnablementState.WorkspaceEnabled;
			if (!currentlyEnabled && enabled) {
				return canAddExtension() ? localize('enabled', "Enabled") : null;
			}
			if (currentlyEnabled && !enabled) {
				return canRemoveExtension() ? localize('disabled', "Disabled") : null;
			}

		}

		return null;
	}

	run(): Promise<any> {
		return Promise.resolve(null);
	}

}

S
Sandeep Somavarapu 已提交
2197
export class MaliciousStatusLabelAction extends ExtensionAction {
J
Joao Moreno 已提交
2198 2199 2200 2201

	private static readonly Class = 'malicious-status';

	constructor(long: boolean) {
J
Joao Moreno 已提交
2202
		const tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
J
Joao Moreno 已提交
2203 2204
		const label = long ? tooltip : localize('malicious', "Malicious");
		super('extensions.install', label, '', false);
J
Joao Moreno 已提交
2205
		this.tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
J
Joao Moreno 已提交
2206 2207
	}

S
Sandeep Somavarapu 已提交
2208
	update(): void {
J
Joao Moreno 已提交
2209 2210 2211 2212 2213 2214 2215
		if (this.extension && this.extension.isMalicious) {
			this.class = `${MaliciousStatusLabelAction.Class} malicious`;
		} else {
			this.class = `${MaliciousStatusLabelAction.Class} not-malicious`;
		}
	}

S
Sandeep Somavarapu 已提交
2216 2217
	run(): Promise<any> {
		return Promise.resolve(null);
J
Joao Moreno 已提交
2218 2219 2220
	}
}

2221 2222
export class DisableAllAction extends Action {

2223
	static readonly ID = 'workbench.extensions.action.disableAll';
2224 2225 2226 2227 2228 2229
	static LABEL = localize('disableAll', "Disable All Installed Extensions");

	private disposables: IDisposable[] = [];

	constructor(
		id: string = DisableAllAction.ID, label: string = DisableAllAction.LABEL,
2230 2231
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
2232 2233 2234 2235 2236 2237 2238
	) {
		super(id, label);
		this.update();
		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
	}

	private update(): void {
2239
		this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled) && !!e.local && this.extensionEnablementService.canChangeEnablement(e.local));
2240 2241
	}

S
Sandeep Somavarapu 已提交
2242
	run(): Promise<any> {
2243
		return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User), EnablementState.Disabled);
2244 2245 2246 2247 2248 2249 2250 2251 2252 2253
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

export class DisableAllWorkpsaceAction extends Action {

2254
	static readonly ID = 'workbench.extensions.action.disableAllWorkspace';
2255 2256 2257 2258 2259 2260
	static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace");

	private disposables: IDisposable[] = [];

	constructor(
		id: string = DisableAllWorkpsaceAction.ID, label: string = DisableAllWorkpsaceAction.LABEL,
2261 2262
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService
2263 2264 2265
	) {
		super(id, label);
		this.update();
2266 2267
		this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
		this.extensionsWorkbenchService.onChange(() => this.update(), this, this.disposables);
2268 2269 2270
	}

	private update(): void {
2271
		this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled));
2272 2273
	}

S
Sandeep Somavarapu 已提交
2274
	run(): Promise<any> {
2275
		return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User), EnablementState.WorkspaceDisabled);
2276 2277 2278 2279 2280 2281 2282 2283 2284 2285
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

export class EnableAllAction extends Action {

2286
	static readonly ID = 'workbench.extensions.action.enableAll';
S
Sandeep Somavarapu 已提交
2287
	static LABEL = localize('enableAll', "Enable All Extensions");
2288 2289 2290 2291 2292

	private disposables: IDisposable[] = [];

	constructor(
		id: string = EnableAllAction.ID, label: string = EnableAllAction.LABEL,
2293 2294
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
2295 2296 2297 2298 2299 2300 2301
	) {
		super(id, label);
		this.update();
		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
	}

	private update(): void {
2302
		this.enabled = this.extensionsWorkbenchService.local.some(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
2303 2304
	}

S
Sandeep Somavarapu 已提交
2305
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
2306
		return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local, EnablementState.Enabled);
2307 2308 2309 2310 2311 2312 2313 2314 2315 2316
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

export class EnableAllWorkpsaceAction extends Action {

2317
	static readonly ID = 'workbench.extensions.action.enableAllWorkspace';
S
Sandeep Somavarapu 已提交
2318
	static LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace");
2319 2320 2321 2322 2323

	private disposables: IDisposable[] = [];

	constructor(
		id: string = EnableAllWorkpsaceAction.ID, label: string = EnableAllWorkpsaceAction.LABEL,
2324 2325 2326
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
2327 2328 2329
	) {
		super(id, label);
		this.update();
2330 2331
		this.extensionsWorkbenchService.onChange(() => this.update(), this, this.disposables);
		this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
2332 2333 2334
	}

	private update(): void {
2335
		this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
2336 2337
	}

S
Sandeep Somavarapu 已提交
2338
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
2339
		return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local, EnablementState.WorkspaceEnabled);
2340 2341 2342 2343 2344 2345
	}

	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
2346 2347
}

J
Joao Moreno 已提交
2348 2349 2350 2351 2352 2353 2354 2355
export class OpenExtensionsFolderAction extends Action {

	static readonly ID = 'workbench.extensions.action.openExtensionsFolder';
	static LABEL = localize('openExtensionsFolder', "Open Extensions Folder");

	constructor(
		id: string,
		label: string,
2356 2357 2358
		@IWindowsService private readonly windowsService: IWindowsService,
		@IFileService private readonly fileService: IFileService,
		@IEnvironmentService private readonly environmentService: IEnvironmentService
J
Joao Moreno 已提交
2359
	) {
R
Rob Lourens 已提交
2360
		super(id, label, undefined, true);
J
Joao Moreno 已提交
2361 2362
	}

S
Sandeep Somavarapu 已提交
2363
	run(): Promise<void> {
J
Joao Moreno 已提交
2364 2365
		const extensionsHome = this.environmentService.extensionsPath;

S
Sandeep Somavarapu 已提交
2366
		return Promise.resolve(this.fileService.resolveFile(URI.file(extensionsHome))).then(file => {
J
Joao Moreno 已提交
2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386
			let itemToShow: string;
			if (file.children && file.children.length > 0) {
				itemToShow = file.children[0].resource.fsPath;
			} else {
				itemToShow = paths.normalize(extensionsHome, true);
			}

			return this.windowsService.showItemInFolder(itemToShow);
		});
	}
}

export class InstallVSIXAction extends Action {

	static readonly ID = 'workbench.extensions.action.installVSIX';
	static LABEL = localize('installVSIX', "Install from VSIX...");

	constructor(
		id = InstallVSIXAction.ID,
		label = InstallVSIXAction.LABEL,
2387 2388
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@INotificationService private readonly notificationService: INotificationService,
S
Sandeep Somavarapu 已提交
2389 2390 2391
		@IWindowService private readonly windowService: IWindowService,
		@IExtensionService private readonly extensionService: IExtensionService,
		@IInstantiationService private readonly instantiationService: IInstantiationService
J
Joao Moreno 已提交
2392
	) {
S
Sandeep Somavarapu 已提交
2393
		super(id, label, 'extension-action install-vsix', true);
J
Joao Moreno 已提交
2394 2395
	}

S
Sandeep Somavarapu 已提交
2396 2397
	run(): Promise<any> {
		return Promise.resolve(this.windowService.showOpenDialog({
J
Joao Moreno 已提交
2398 2399 2400 2401
			title: localize('installFromVSIX', "Install from VSIX"),
			filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
			properties: ['openFile'],
			buttonLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
S
Sandeep Somavarapu 已提交
2402
		})).then(result => {
J
Joao Moreno 已提交
2403
			if (!result) {
S
Sandeep Somavarapu 已提交
2404
				return Promise.resolve(null);
J
Joao Moreno 已提交
2405 2406
			}

S
Sandeep Somavarapu 已提交
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
			return Promise.all(result.map(vsix => this.extensionsWorkbenchService.install(vsix)))
				.then(extensions => {
					for (const extension of extensions) {
						const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
						const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.identifier.id)
							: localize('InstallVSIXAction.success', "Installing the extension {0} is completed.", extension.identifier.id);
						const actions = requireReload ? [{
							label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
							run: () => this.windowService.reloadWindow()
						}] : [];
						this.notificationService.prompt(
							Severity.Info,
							message,
							actions,
							{ sticky: true }
						);
					}
					return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run();
				});
J
Joao Moreno 已提交
2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436
		});
	}
}

export class ReinstallAction extends Action {

	static readonly ID = 'workbench.extensions.action.reinstall';
	static LABEL = localize('reinstall', "Reinstall Extension...");

	constructor(
		id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL,
2437 2438 2439 2440
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
		@IWindowService private readonly windowService: IWindowService,
S
Sandeep Somavarapu 已提交
2441 2442
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IExtensionService private readonly extensionService: IExtensionService
J
Joao Moreno 已提交
2443 2444 2445 2446 2447
	) {
		super(id, label);
	}

	get enabled(): boolean {
2448
		return this.extensionsWorkbenchService.local.filter(l => l.type === ExtensionType.User && l.local).length > 0;
J
Joao Moreno 已提交
2449 2450
	}

J
Johannes Rieken 已提交
2451
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
2452
		return this.quickInputService.pick(this.getEntries(), { placeHolder: localize('selectExtensionToReinstall', "Select Extension to Reinstall") })
C
Christof Marti 已提交
2453
			.then(pick => pick && this.reinstallExtension(pick.extension));
J
Joao Moreno 已提交
2454 2455
	}

J
Johannes Rieken 已提交
2456
	private getEntries(): Promise<(IQuickPickItem & { extension: IExtension })[]> {
J
Joao Moreno 已提交
2457 2458
		return this.extensionsWorkbenchService.queryLocal()
			.then(local => {
C
Christof Marti 已提交
2459
				const entries = local
2460
					.filter(extension => extension.type === ExtensionType.User)
J
Joao Moreno 已提交
2461
					.map(extension => {
C
Christof Marti 已提交
2462
						return {
S
Sandeep Somavarapu 已提交
2463
							id: extension.identifier.id,
J
Joao Moreno 已提交
2464
							label: extension.displayName,
S
Sandeep Somavarapu 已提交
2465
							description: extension.identifier.id,
C
Christof Marti 已提交
2466 2467
							extension,
						} as (IQuickPickItem & { extension: IExtension });
J
Joao Moreno 已提交
2468 2469 2470 2471 2472
					});
				return entries;
			});
	}

J
Johannes Rieken 已提交
2473
	private reinstallExtension(extension: IExtension): Promise<void> {
S
Sandeep Somavarapu 已提交
2474 2475
		return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run()
			.then(() => {
2476
				return this.extensionsWorkbenchService.reinstall(extension)
S
Sandeep Somavarapu 已提交
2477 2478 2479 2480 2481 2482 2483 2484
					.then(extension => {
						const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
						const message = requireReload ? localize('ReinstallAction.successReload', "Please reload Visual Studio Code to complete reinstalling the extension {0}.", extension.identifier.id)
							: localize('ReinstallAction.success', "Reinstalling the extension {0} is completed.", extension.identifier.id);
						const actions = requireReload ? [{
							label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
							run: () => this.windowService.reloadWindow()
						}] : [];
2485 2486
						this.notificationService.prompt(
							Severity.Info,
S
Sandeep Somavarapu 已提交
2487 2488
							message,
							actions,
2489 2490 2491 2492
							{ sticky: true }
						);
					}, error => this.notificationService.error(error));
			});
J
Joao Moreno 已提交
2493 2494 2495
	}
}

S
Sandeep Somavarapu 已提交
2496
export class InstallSpecificVersionOfExtensionAction extends Action {
2497

S
Sandeep Somavarapu 已提交
2498 2499
	static readonly ID = 'workbench.extensions.action.install.specificVersion';
	static LABEL = localize('install previous version', "Install Specific Version of Extension...");
2500 2501

	constructor(
S
Sandeep Somavarapu 已提交
2502
		id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,
2503 2504 2505 2506 2507
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
		@IWindowService private readonly windowService: IWindowService,
S
Sandeep Somavarapu 已提交
2508 2509
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IExtensionService private readonly extensionService: IExtensionService
2510 2511 2512 2513 2514
	) {
		super(id, label);
	}

	get enabled(): boolean {
S
Sandeep Somavarapu 已提交
2515
		return this.extensionsWorkbenchService.local.some(l => this.isEnabled(l));
2516 2517
	}

S
Sandeep Somavarapu 已提交
2518 2519 2520
	async run(): Promise<any> {
		const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true });
		if (extensionPick && extensionPick.extension) {
2521
			const versionPick = await this.quickInputService.pick(extensionPick.versions.map(v => ({ id: v.version, label: v.version, description: `${getRelativeDateLabel(new Date(Date.parse(v.date)))}${v.version === extensionPick.extension.version ? ` (${localize('current', "Current")})` : ''}` })), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true });
S
Sandeep Somavarapu 已提交
2522 2523
			if (versionPick) {
				if (extensionPick.extension.version !== versionPick.id) {
2524
					await this.install(extensionPick.extension, versionPick.id);
S
Sandeep Somavarapu 已提交
2525 2526
				}
			}
S
Sandeep Somavarapu 已提交
2527 2528 2529 2530
		}
	}

	private isEnabled(extension: IExtension): boolean {
S
Sandeep Somavarapu 已提交
2531
		return !!extension.gallery && (extension.enablementState === EnablementState.Enabled || extension.enablementState === EnablementState.WorkspaceEnabled);
2532 2533
	}

S
Sandeep Somavarapu 已提交
2534
	private async getExtensionEntries(): Promise<(IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] })[]> {
2535
		const installed = await this.extensionsWorkbenchService.queryLocal();
2536
		const versionsPromises: Promise<{ extension: IExtension, versions: IGalleryExtensionVersion[] } | null>[] = [];
2537
		for (const extension of installed) {
S
Sandeep Somavarapu 已提交
2538
			if (this.isEnabled(extension)) {
2539
				versionsPromises.push(this.extensionGalleryService.getAllVersions(extension.gallery!, true)
S
Sandeep Somavarapu 已提交
2540
					.then(versions => (versions.length ? { extension, versions } : null)));
2541 2542 2543
			}
		}

S
Sandeep Somavarapu 已提交
2544
		const extensions = await Promise.all(versionsPromises);
M
Matt Bierner 已提交
2545
		return coalesce(extensions)
2546
			.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName))
S
Sandeep Somavarapu 已提交
2547
			.map(({ extension, versions }) => {
2548
				return {
S
Sandeep Somavarapu 已提交
2549 2550 2551
					id: extension.identifier.id,
					label: extension.displayName || extension.identifier.id,
					description: extension.identifier.id,
2552
					extension,
S
Sandeep Somavarapu 已提交
2553 2554
					versions
				} as (IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] });
2555 2556 2557
			});
	}

J
Johannes Rieken 已提交
2558
	private install(extension: IExtension, version: string): Promise<void> {
S
Sandeep Somavarapu 已提交
2559 2560
		return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run()
			.then(() => {
2561
				return this.extensionsWorkbenchService.installVersion(extension, version)
S
Sandeep Somavarapu 已提交
2562 2563 2564 2565 2566 2567 2568 2569
					.then(extension => {
						const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
						const message = requireReload ? localize('InstallAnotherVersionExtensionAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.identifier.id)
							: localize('InstallAnotherVersionExtensionAction.success', "Installing the extension {0} is completed.", extension.identifier.id);
						const actions = requireReload ? [{
							label: localize('InstallAnotherVersionExtensionAction.reloadNow', "Reload Now"),
							run: () => this.windowService.reloadWindow()
						}] : [];
2570 2571
						this.notificationService.prompt(
							Severity.Info,
S
Sandeep Somavarapu 已提交
2572 2573
							message,
							actions,
2574 2575 2576 2577
							{ sticky: true }
						);
					}, error => this.notificationService.error(error));
			});
J
Joao Moreno 已提交
2578 2579 2580
	}
}

S
Sandeep Somavarapu 已提交
2581
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {
2582 2583 2584 2585 2586 2587 2588 2589 2590
	const viewletService = accessor.get(IViewletService);

	return viewletService.openViewlet(VIEWLET_ID, true)
		.then(viewlet => viewlet as IExtensionsViewlet)
		.then(viewlet => {
			viewlet.search(`ext:${fileExtension.replace(/^\./, '')}`);
			viewlet.focus();
		});
});
B
Benjamin Pasero 已提交
2591

2592
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithIds', function (accessor: ServicesAccessor, extensionIds: string[]) {
2593 2594 2595 2596 2597
	const viewletService = accessor.get(IViewletService);

	return viewletService.openViewlet(VIEWLET_ID, true)
		.then(viewlet => viewlet as IExtensionsViewlet)
		.then(viewlet => {
2598 2599 2600 2601
			const query = extensionIds
				.map(id => `@id:${id}`)
				.join(' ');
			viewlet.search(query);
2602 2603 2604 2605
			viewlet.focus();
		});
});

B
Benjamin Pasero 已提交
2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624
export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', {
	dark: '#327e36',
	light: '#327e36',
	hc: null
}, localize('extensionButtonProminentBackground', "Button background color for actions extension that stand out (e.g. install button)."));

export const extensionButtonProminentForeground = registerColor('extensionButton.prominentForeground', {
	dark: Color.white,
	light: Color.white,
	hc: null
}, localize('extensionButtonProminentForeground', "Button foreground color for actions extension that stand out (e.g. install button)."));

export const extensionButtonProminentHoverBackground = registerColor('extensionButton.prominentHoverBackground', {
	dark: '#28632b',
	light: '#28632b',
	hc: null
}, localize('extensionButtonProminentHoverBackground', "Button background hover color for actions extension that stand out (e.g. install button)."));

registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
B
Benjamin Pasero 已提交
2625 2626
	const foregroundColor = theme.getColor(foreground);
	if (foregroundColor) {
2627 2628
		collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`);
B
Benjamin Pasero 已提交
2629 2630
	}

B
Benjamin Pasero 已提交
2631 2632
	const buttonBackgroundColor = theme.getColor(buttonBackground);
	if (buttonBackgroundColor) {
2633 2634
		collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`);
B
Benjamin Pasero 已提交
2635 2636 2637 2638
	}

	const buttonForegroundColor = theme.getColor(buttonForeground);
	if (buttonForegroundColor) {
2639 2640
		collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`);
B
Benjamin Pasero 已提交
2641 2642 2643 2644
	}

	const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground);
	if (buttonHoverBackgroundColor) {
2645 2646
		collector.addRule(`.extension .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`);
B
Benjamin Pasero 已提交
2647 2648 2649 2650
	}

	const contrastBorderColor = theme.getColor(contrastBorder);
	if (contrastBorderColor) {
2651 2652
		collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`);
B
Benjamin Pasero 已提交
2653 2654 2655 2656
	}

	const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground);
	if (extensionButtonProminentBackground) {
2657 2658
		collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`);
B
Benjamin Pasero 已提交
2659 2660 2661 2662
	}

	const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground);
	if (extensionButtonProminentForeground) {
2663 2664
		collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`);
B
Benjamin Pasero 已提交
2665 2666 2667 2668
	}

	const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground);
	if (extensionButtonProminentHoverBackground) {
2669 2670
		collector.addRule(`.extension .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`);
B
Benjamin Pasero 已提交
2671
	}
A
Alex Dima 已提交
2672
});