extensionsActions.ts 149.1 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import 'vs/css!./media/extensionActions';
7
import { localize } from 'vs/nls';
8
import { IAction, Action, Separator, SubmenuAction } from 'vs/base/common/actions';
9
import { Delayer } from 'vs/base/common/async';
10
import * as DOM from 'vs/base/browser/dom';
11
import { Event } from 'vs/base/common/event';
12
import * as json from 'vs/base/common/json';
13
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
M
Matt Bierner 已提交
14
import { dispose, Disposable } from 'vs/base/common/lifecycle';
15
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions';
16
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
17
import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
18
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
19
import { IExtensionIgnoredRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
20
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
21
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } 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';
B
Benjamin Pasero 已提交
26
import { IFileService, IFileContent } from 'vs/platform/files/common/files';
S
Sandeep Somavarapu 已提交
27
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
28
import { IHostService } from 'vs/workbench/services/host/browser/host';
29
import { IExtensionService, toExtension, toExtensionDescription } 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';
32
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
M
Martin Aeschlimann 已提交
33
import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
34 35
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';
S
Sandeep Somavarapu 已提交
39 40
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
41
import { MenuRegistry, MenuId, IMenuService } from 'vs/platform/actions/common/actions';
I
isidor 已提交
42
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
J
Joao Moreno 已提交
43 44 45
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
46
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
47
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
48
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
49
import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
50
import { CancellationToken } from 'vs/base/common/cancellation';
51
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
52
import { alert } from 'vs/base/browser/ui/aria/aria';
M
Matt Bierner 已提交
53
import { coalesce } from 'vs/base/common/arrays';
54
import { IWorkbenchThemeService, IWorkbenchTheme, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
55
import { ILabelService } from 'vs/platform/label/common/label';
56
import { prefersExecuteOnUI, prefersExecuteOnWorkspace, canExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil';
57
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
58
import { IProductService } from 'vs/platform/product/common/productService';
S
Sandeep Somavarapu 已提交
59
import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs';
60
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
61
import { Codicon } from 'vs/base/common/codicons';
S
Sandeep Somavarapu 已提交
62
import { IViewsService } from 'vs/workbench/common/views';
63
import { IActionViewItemOptions, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
64
import { EXTENSIONS_CONFIG } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig';
S
Sandeep Somavarapu 已提交
65
import { isPromiseCanceledError } from 'vs/base/common/errors';
66 67 68
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
J
Joao Moreno 已提交
69

70 71
const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService): Promise<any> => {
	return instantiationService.invokeFunction(accessor => {
S
Sandeep Somavarapu 已提交
72 73 74 75
		if (isPromiseCanceledError(error)) {
			return Promise.resolve();
		}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
		const productService = accessor.get(IProductService);
		const openerService = accessor.get(IOpenerService);
		const notificationService = accessor.get(INotificationService);
		const dialogService = accessor.get(IDialogService);
		const erorrsToShows = [INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_NOT_SUPPORTED];
		if (!extension || erorrsToShows.indexOf(error.name) !== -1 || !productService.extensionsGallery) {
			return dialogService.show(Severity.Error, error.message, []);
		} else {
			const downloadUrl = `${productService.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();
		}
	});
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 130 131 132 133 134 135 136 137 138 139 140
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 已提交
141
export abstract class ExtensionAction extends Action implements IExtensionContainer {
S
Sandeep Somavarapu 已提交
142 143 144 145
	static readonly EXTENSION_ACTION_CLASS = 'extension-action';
	static readonly TEXT_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} text`;
	static readonly LABEL_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} label`;
	static readonly ICON_ACTION_CLASS = `${ExtensionAction.EXTENSION_ACTION_CLASS} icon`;
S
Sandeep Somavarapu 已提交
146 147 148
	private _extension: IExtension | null = null;
	get extension(): IExtension | null { return this._extension; }
	set extension(extension: IExtension | null) { this._extension = extension; this.update(); }
S
Sandeep Somavarapu 已提交
149
	abstract update(): void;
150
}
151

152
export abstract class ActionWithDropDownAction extends ExtensionAction {
153

154
	private action: IAction | undefined;
155

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
	private _menuActions: IAction[] = [];
	get menuActions(): IAction[] { return [...this._menuActions]; }

	set extension(extension: IExtension | null) {
		this.actions.forEach(a => a.extension = extension);
		super.extension = extension;
	}

	constructor(
		id: string, label: string,
		protected readonly actions: ExtensionAction[],
	) {
		super(id, label);
		this.update();
		this._register(Event.any(...actions.map(a => a.onDidChange))(() => this.update(true)));
	}

	update(donotUpdateActions?: boolean): void {
		if (!donotUpdateActions) {
			this.actions.forEach(a => a.update());
		}

		const enabledActions = this.actions.filter(a => a.enabled);
		this.action = enabledActions[0];
		this._menuActions = enabledActions.slice(1);

		this.enabled = !!this.action;
		if (this.action) {
			this.label = this.action.label;
			this.tooltip = this.action.tooltip;
		}

		let clazz = (this.action || this.actions[0])?.class || '';
		clazz = clazz ? `${clazz} action-dropdown` : 'action-dropdown';
		if (this._menuActions.length === 0) {
			clazz += ' action-dropdown';
		}
		this.class = clazz;
	}
195

196 197 198 199 200 201 202 203 204 205 206
	run(): Promise<void> {
		const enabledActions = this.actions.filter(a => a.enabled);
		return enabledActions[0].run();
	}
}

export abstract class AbstractInstallAction extends ExtensionAction {

	static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install`;

	protected _manifest: IExtensionManifest | null = null;
207 208 209 210 211
	set manifest(manifest: IExtensionManifest) {
		this._manifest = manifest;
		this.updateLabel();
	}

212
	constructor(
213
		id: string, label: string, cssClass: string,
214 215 216
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@INotificationService private readonly notificationService: INotificationService,
S
Sandeep Somavarapu 已提交
217
		@IExtensionService private readonly runtimeExtensionService: IExtensionService,
218
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
219
		@ILabelService private readonly labelService: ILabelService,
220
	) {
221
		super(id, label, cssClass, false);
222
		this.update();
M
Matt Bierner 已提交
223
		this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));
224 225
	}

S
Sandeep Somavarapu 已提交
226
	update(): void {
S
Sandeep Somavarapu 已提交
227
		this.enabled = false;
228
		if (this.extension && !this.extension.isBuiltin) {
S
Sandeep Somavarapu 已提交
229 230 231
			if (this.extension.state === ExtensionState.Uninstalled && this.extensionsWorkbenchService.canInstall(this.extension)) {
				this.enabled = true;
				this.updateLabel();
232
			}
233 234 235
		}
	}

S
Sandeep Somavarapu 已提交
236
	async run(): Promise<any> {
S
Sandeep Somavarapu 已提交
237 238 239
		if (!this.extension) {
			return;
		}
240
		this.extensionsWorkbenchService.open(this.extension);
J
Joao Moreno 已提交
241

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

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

246
		alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName));
247

248
		if (extension && extension.local) {
S
Sandeep Somavarapu 已提交
249 250
			const runningExtension = await this.getRunningExtension(extension.local);
			if (runningExtension) {
251 252 253 254 255 256 257 258 259
				let action = await SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, extension)
					|| await SetFileIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension)
					|| await SetProductIconThemeAction.create(this.workbenchThemeService, this.instantiationService, extension);
				if (action) {
					try {
						return action.run({ showCurrentTheme: true, ignoreFocusLost: true });
					} finally {
						action.dispose();
					}
S
Sandeep Somavarapu 已提交
260 261
				}
			}
S
Sandeep Somavarapu 已提交
262 263
		}

J
Joao Moreno 已提交
264 265
	}

266
	private install(extension: IExtension): Promise<IExtension | void> {
267
		return this.extensionsWorkbenchService.install(extension, this.getInstallOptions())
S
Sandeep Somavarapu 已提交
268 269 270 271 272 273 274
			.then(null, err => {
				if (!extension.gallery) {
					return this.notificationService.error(err);
				}

				console.error(err);

275
				return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService);
S
Sandeep Somavarapu 已提交
276 277
			});
	}
J
Joao Moreno 已提交
278

S
Sandeep Somavarapu 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	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;
296
	}
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

	protected abstract updateLabel(): void;
	protected abstract getInstallOptions(): InstallOptions;
}

export class InstallAction extends AbstractInstallAction {

	constructor(
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService instantiationService: IInstantiationService,
		@INotificationService notificationService: INotificationService,
		@IExtensionService runtimeExtensionService: IExtensionService,
		@IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService,
		@ILabelService labelService: ILabelService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IProductService private readonly productService: IProductService,
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
		@IUserDataAutoSyncEnablementService protected readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
		@IUserDataSyncResourceEnablementService protected readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
	) {
		super(`extensions.installAndSync`, localize('install', "Install"), InstallAction.Class,
			extensionsWorkbenchService, instantiationService, notificationService, runtimeExtensionService, workbenchThemeService, labelService);
		this.updateLabel();
		this._register(labelService.onDidChangeFormatters(() => this.updateLabel(), this));
		this._register(Event.any(userDataAutoSyncEnablementService.onDidChangeEnablement,
			Event.filter(userDataSyncResourceEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update()));
	}

	protected updateLabel(): void {
		if (!this.extension) {
			return;
		}

		const isMachineScoped = this.getInstallOptions().isMachineScoped;
		this.label = isMachineScoped ? localize('install and do no sync', "Install (Do not sync)") : localize('install', "Install");

		// When remote connection exists
		if (this._manifest && this.extensionManagementServerService.remoteExtensionManagementServer) {

			// On Desktop and UI Extension
			if (this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnUI(this._manifest, this.productService, this.configurationService)) {
				this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally");
				return;
			}

			// On Web and Web Extension
			if (this.extensionManagementServerService.webExtensionManagementServer && prefersExecuteOnWeb(this._manifest, this.productService, this.configurationService)) {
				this.label = isMachineScoped ? localize('install locally and do not sync', "Install Locally (Do not sync)") : localize('install locally', "Install Locally");
				return;
			}

			const host = this.extensionManagementServerService.remoteExtensionManagementServer.label;
			this.label = isMachineScoped ? localize('install on remote and do not sync', "Install on {0} (Do not sync)", host) : localize('install on remote', "Install on {0}", host);
			return;
		}
	}

	protected getInstallOptions(): InstallOptions {
		return { isMachineScoped: this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions) };
	}

}

export class InstallAndSyncAction extends AbstractInstallAction {

	constructor(
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService instantiationService: IInstantiationService,
		@INotificationService notificationService: INotificationService,
		@IExtensionService runtimeExtensionService: IExtensionService,
		@IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService,
		@ILabelService labelService: ILabelService,
		@IProductService productService: IProductService,
		@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
		@IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
	) {
		super(`extensions.installAndSync`, localize('install', "Install"), InstallAndSyncAction.Class,
			extensionsWorkbenchService, instantiationService, notificationService, runtimeExtensionService, workbenchThemeService, labelService);
		this.tooltip = localize('install everywhere tooltip', "Install this extension in all your synced {0} instances", productService.nameLong);
		this._register(Event.any(userDataAutoSyncEnablementService.onDidChangeEnablement,
			Event.filter(userDataSyncResourceEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update()));
	}


	update(): void {
		super.update();
		if (this.enabled) {
			this.enabled = this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncResourceEnablementService.isResourceEnabled(SyncResource.Extensions);
		}
	}

	protected updateLabel(): void { }

	protected getInstallOptions(): InstallOptions {
		return { isMachineScoped: false };
	}
}

export class InstallDropdownAction extends ActionWithDropDownAction {

	set manifest(manifest: IExtensionManifest) {
		this.actions.forEach(a => (<AbstractInstallAction>a).manifest = manifest);
		this.actions.forEach(a => a.update());
		this.update();
	}

	constructor(
		@IInstantiationService instantiationService: IInstantiationService,
	) {
		super(`extensions.installActions`, '', [
			instantiationService.createInstance(InstallAndSyncAction),
			instantiationService.createInstance(InstallAction),
		]);
	}

}

export class InstallingLabelAction extends ExtensionAction {

	private static readonly LABEL = localize('installing', "Installing");
	private static readonly CLASS = `${ExtensionAction.LABEL_ACTION_CLASS} install installing`;

	constructor() {
		super('extension.installing', InstallingLabelAction.LABEL, InstallingLabelAction.CLASS, false);
	}

	update(): void {
		this.class = `${InstallingLabelAction.CLASS}${this.extension && this.extension.state === ExtensionState.Installing ? '' : ' hide'}`;
	}
426 427
}

S
Sandeep Somavarapu 已提交
428
export abstract class InstallInOtherServerAction extends ExtensionAction {
429

430 431
	protected static readonly INSTALL_LABEL = localize('install', "Install");
	protected static readonly INSTALLING_LABEL = localize('installing', "Installing");
432

S
Sandeep Somavarapu 已提交
433 434
	private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install`;
	private static readonly InstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} install installing`;
435

436
	updateWhenCounterExtensionChanges: boolean = true;
437 438

	constructor(
S
Sandeep Somavarapu 已提交
439 440
		id: string,
		private readonly server: IExtensionManagementServer | null,
S
Sandeep Somavarapu 已提交
441
		private readonly canInstallAnyWhere: boolean,
442
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
443 444 445
		@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
		@IProductService private readonly productService: IProductService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
446
	) {
S
Sandeep Somavarapu 已提交
447
		super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false);
448 449 450
		this.update();
	}

451
	update(): void {
452
		this.enabled = false;
S
Sandeep Somavarapu 已提交
453 454
		this.class = InstallInOtherServerAction.Class;

S
Sandeep Somavarapu 已提交
455
		if (this.canInstall()) {
S
Sandeep Somavarapu 已提交
456
			const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === this.server)[0];
S
Sandeep Somavarapu 已提交
457 458 459 460 461 462 463 464 465 466 467 468
			if (extensionInOtherServer) {
				// Getting installed in other server
				if (extensionInOtherServer.state === ExtensionState.Installing && !extensionInOtherServer.local) {
					this.enabled = true;
					this.label = InstallInOtherServerAction.INSTALLING_LABEL;
					this.class = InstallInOtherServerAction.InstallingClass;
				}
			} else {
				// Not installed in other server
				this.enabled = true;
				this.label = this.getInstallLabel();
			}
469 470 471
		}
	}

S
Sandeep Somavarapu 已提交
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
	private canInstall(): boolean {
		// Disable if extension is not installed or not an user extension
		if (
			!this.extension
			|| !this.server
			|| !this.extension.local
			|| this.extension.state !== ExtensionState.Installed
			|| this.extension.type !== ExtensionType.User
			|| this.extension.enablementState === EnablementState.DisabledByEnvironemt
		) {
			return false;
		}

		if (isLanguagePackExtension(this.extension.local.manifest)) {
			return true;
		}

		// Prefers to run on UI
		if (this.server === this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnUI(this.extension.local.manifest, this.productService, this.configurationService)) {
			return true;
		}

		// Prefers to run on Workspace
		if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && prefersExecuteOnWorkspace(this.extension.local.manifest, this.productService, this.configurationService)) {
			return true;
		}

		if (this.canInstallAnyWhere) {
			// Can run on UI
			if (this.server === this.extensionManagementServerService.localExtensionManagementServer && canExecuteOnUI(this.extension.local.manifest, this.productService, this.configurationService)) {
				return true;
			}

			// Can run on Workspace
			if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && canExecuteOnWorkspace(this.extension.local.manifest, this.productService, this.configurationService)) {
				return true;
			}
		}

		return false;
	}

514
	async run(): Promise<void> {
S
Sandeep Somavarapu 已提交
515 516 517
		if (!this.extension) {
			return;
		}
S
Sandeep Somavarapu 已提交
518
		if (this.server) {
519 520
			this.extensionsWorkbenchService.open(this.extension);
			alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
S
Sandeep Somavarapu 已提交
521 522 523 524 525
			if (this.extension.gallery) {
				await this.server.extensionManagementService.installFromGallery(this.extension.gallery);
			} else {
				const vsix = await this.extension.server!.extensionManagementService.zip(this.extension.local!);
				await this.server.extensionManagementService.install(vsix);
526 527 528
			}
		}
	}
S
Sandeep Somavarapu 已提交
529 530

	protected abstract getInstallLabel(): string;
531 532
}

S
Sandeep Somavarapu 已提交
533
export class RemoteInstallAction extends InstallInOtherServerAction {
534

S
Sandeep Somavarapu 已提交
535
	constructor(
S
Sandeep Somavarapu 已提交
536
		canInstallAnyWhere: boolean,
S
Sandeep Somavarapu 已提交
537
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
538 539 540
		@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
		@IProductService productService: IProductService,
		@IConfigurationService configurationService: IConfigurationService,
S
Sandeep Somavarapu 已提交
541
	) {
S
Sandeep Somavarapu 已提交
542
		super(`extensions.remoteinstall`, extensionManagementServerService.remoteExtensionManagementServer, canInstallAnyWhere, extensionsWorkbenchService, extensionManagementServerService, productService, configurationService);
S
Sandeep Somavarapu 已提交
543
	}
544

S
Sandeep Somavarapu 已提交
545
	protected getInstallLabel(): string {
546
		return this.extensionManagementServerService.remoteExtensionManagementServer ? localize('Install on Server', "Install in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label) : InstallInOtherServerAction.INSTALL_LABEL;
S
Sandeep Somavarapu 已提交
547 548
	}

S
Sandeep Somavarapu 已提交
549
}
550

S
Sandeep Somavarapu 已提交
551
export class LocalInstallAction extends InstallInOtherServerAction {
552 553

	constructor(
S
Sandeep Somavarapu 已提交
554
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
555 556 557
		@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
		@IProductService productService: IProductService,
		@IConfigurationService configurationService: IConfigurationService,
558
	) {
S
Sandeep Somavarapu 已提交
559
		super(`extensions.localinstall`, extensionManagementServerService.localExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, productService, configurationService);
560 561
	}

S
Sandeep Somavarapu 已提交
562 563
	protected getInstallLabel(): string {
		return localize('install locally', "Install Locally");
564 565 566 567
	}

}

S
Sandeep Somavarapu 已提交
568
export class UninstallAction extends ExtensionAction {
569

570 571
	private static readonly UninstallLabel = localize('uninstallAction', "Uninstall");
	private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling");
572

S
Sandeep Somavarapu 已提交
573 574
	private static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`;
	private static readonly UnInstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall uninstalling`;
S
Sandeep Somavarapu 已提交
575

576
	constructor(
S
Sandeep Somavarapu 已提交
577
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
578
	) {
S
Sandeep Somavarapu 已提交
579
		super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false);
580 581 582
		this.update();
	}

S
Sandeep Somavarapu 已提交
583
	update(): void {
584 585 586 587 588 589 590 591 592
		if (!this.extension) {
			this.enabled = false;
			return;
		}

		const state = this.extension.state;

		if (state === ExtensionState.Uninstalling) {
			this.label = UninstallAction.UninstallingLabel;
S
Sandeep Somavarapu 已提交
593
			this.class = UninstallAction.UnInstallingClass;
594 595 596 597 598
			this.enabled = false;
			return;
		}

		this.label = UninstallAction.UninstallLabel;
S
Sandeep Somavarapu 已提交
599
		this.class = UninstallAction.UninstallClass;
S
Sandeep Somavarapu 已提交
600
		this.tooltip = UninstallAction.UninstallLabel;
601

S
Sandeep Somavarapu 已提交
602 603 604 605 606
		if (state !== ExtensionState.Installed) {
			this.enabled = false;
			return;
		}

607
		if (this.extension.isBuiltin) {
608 609 610 611 612 613 614
			this.enabled = false;
			return;
		}

		this.enabled = true;
	}

S
Sandeep Somavarapu 已提交
615 616 617 618
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
619 620 621
		alert(localize('uninstallExtensionStart', "Uninstalling extension {0} started.", this.extension.displayName));

		return this.extensionsWorkbenchService.uninstall(this.extension).then(() => {
S
Sandeep Somavarapu 已提交
622
			alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension!.displayName));
623
		});
624 625 626
	}
}

S
Sandeep Somavarapu 已提交
627
export class UpdateAction extends ExtensionAction {
628

S
Sandeep Somavarapu 已提交
629
	private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent update`;
630
	private static readonly DisabledClass = `${UpdateAction.EnabledClass} disabled`;
631 632

	constructor(
633 634 635
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@INotificationService private readonly notificationService: INotificationService,
636
	) {
637
		super(`extensions.update`, '', UpdateAction.DisabledClass, false);
638 639 640
		this.update();
	}

S
Sandeep Somavarapu 已提交
641
	update(): void {
642 643 644
		if (!this.extension) {
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
645
			this.label = this.getUpdateLabel();
646 647 648
			return;
		}

649
		if (this.extension.type !== ExtensionType.User) {
650 651
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
652
			this.label = this.getUpdateLabel();
653 654 655 656 657 658 659 660
			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;
661
		this.label = this.extension.outdated ? this.getUpdateLabel(this.extension.latestVersion) : this.getUpdateLabel();
662 663
	}

S
Sandeep Somavarapu 已提交
664 665 666 667
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
668
		alert(localize('updateExtensionStart', "Updating extension {0} to version {1} started.", this.extension.displayName, this.extension.latestVersion));
J
Joao Moreno 已提交
669 670 671
		return this.install(this.extension);
	}

S
Sandeep Somavarapu 已提交
672
	private install(extension: IExtension): Promise<void> {
673
		return this.extensionsWorkbenchService.install(extension).then(() => {
S
Sandeep Somavarapu 已提交
674
			alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", extension.displayName, extension.latestVersion));
675
		}, err => {
676
			if (!extension.gallery) {
J
Joao Moreno 已提交
677 678 679 680
				return this.notificationService.error(err);
			}

			console.error(err);
681

682
			return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService);
J
Joao Moreno 已提交
683
		});
684 685
	}

686
	private getUpdateLabel(version?: string): string {
687
		return version ? localize('updateTo', "Update to {0}", version) : localize('updateAction', "Update");
688
	}
689 690
}

691
export interface IExtensionActionViewItemOptions extends IActionViewItemOptions {
692 693 694
	tabOnlyOnFocus?: boolean;
}

695
export class ExtensionActionViewItem extends ActionViewItem {
696

697
	constructor(context: any, action: IAction, options: IExtensionActionViewItemOptions = {}) {
698 699 700 701 702 703
		super(context, action, options);
	}

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

S
Sandeep Somavarapu 已提交
704
		if (this.label && (<IExtensionActionViewItemOptions>this.options).tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) {
705 706 707 708
			DOM.removeTabIndexAndUpdateFocus(this.label);
		}
	}

S
Sandeep Somavarapu 已提交
709
	private _hasFocus: boolean = false;
710
	setFocus(value: boolean): void {
S
Sandeep Somavarapu 已提交
711
		if (!(<IExtensionActionViewItemOptions>this.options).tabOnlyOnFocus || this._hasFocus === value) {
712 713 714
			return;
		}
		this._hasFocus = value;
715
		if (this.label && this.getAction().enabled) {
716 717 718 719 720 721 722 723 724
			if (this._hasFocus) {
				this.label.tabIndex = 0;
			} else {
				DOM.removeTabIndexAndUpdateFocus(this.label);
			}
		}
	}
}

725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
export class ExtensionActionWithDropdownActionViewItem extends ActionWithDropdownActionViewItem {

	constructor(
		action: ActionWithDropDownAction,
		options: IExtensionActionViewItemOptions & IActionWithDropdownActionViewItemOptions,
		contextMenuProvider: IContextMenuProvider
	) {
		super(null, action, options, contextMenuProvider);
	}

	render(container: HTMLElement): void {
		super.render(container);
		this.updateClass();
	}

	updateClass(): void {
		super.updateClass();
		if (this.dropdownMenuActionViewItem && this.dropdownMenuActionViewItem.element) {
			this.dropdownMenuActionViewItem.element.classList.toggle('hide', (<ActionWithDropDownAction>this._action).menuActions.length === 0);
		}
	}

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

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

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

}

S
Sandeep Somavarapu 已提交
772
export abstract class ExtensionDropDownAction extends ExtensionAction {
S
Explore  
Sandeep Somavarapu 已提交
773 774 775 776 777 778 779

	constructor(
		id: string,
		label: string,
		cssClass: string,
		enabled: boolean,
		private readonly tabOnlyOnFocus: boolean,
S
Sandeep Somavarapu 已提交
780
		@IInstantiationService protected instantiationService: IInstantiationService
S
Explore  
Sandeep Somavarapu 已提交
781 782 783 784
	) {
		super(id, label, cssClass, enabled);
	}

S
Sandeep Somavarapu 已提交
785
	private _actionViewItem: DropDownMenuActionViewItem | null = null;
786 787 788
	createActionViewItem(): DropDownMenuActionViewItem {
		this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this, this.tabOnlyOnFocus);
		return this._actionViewItem;
S
Explore  
Sandeep Somavarapu 已提交
789 790
	}

J
Johannes Rieken 已提交
791
	public run({ actionGroups, disposeActionsOnHide }: { actionGroups: IAction[][], disposeActionsOnHide: boolean }): Promise<any> {
792 793
		if (this._actionViewItem) {
			this._actionViewItem.showMenu(actionGroups, disposeActionsOnHide);
S
Explore  
Sandeep Somavarapu 已提交
794
		}
795
		return Promise.resolve();
S
Explore  
Sandeep Somavarapu 已提交
796 797 798
	}
}

799
export class DropDownMenuActionViewItem extends ExtensionActionViewItem {
800

S
Sandeep Somavarapu 已提交
801
	constructor(action: ExtensionDropDownAction,
802
		tabOnlyOnFocus: boolean,
803
		@IContextMenuService private readonly contextMenuService: IContextMenuService
S
Sandeep Somavarapu 已提交
804
	) {
805
		super(null, action, { icon: true, label: true, tabOnlyOnFocus });
806 807
	}

S
Sandeep Somavarapu 已提交
808
	public showMenu(menuActionGroups: IAction[][], disposeActionsOnHide: boolean): void {
809 810 811 812 813 814 815 816 817 818 819
		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); } }
			});
		}
820 821
	}

S
Sandeep Somavarapu 已提交
822
	private getActions(menuActionGroups: IAction[][]): IAction[] {
B
Benjamin Pasero 已提交
823
		let actions: IAction[] = [];
S
Sandeep Somavarapu 已提交
824
		for (const menuActions of menuActionGroups) {
S
Sandeep Somavarapu 已提交
825
			actions = [...actions, ...menuActions, new Separator()];
826 827 828 829 830
		}
		return actions.length ? actions.slice(0, actions.length - 1) : actions;
	}
}

831
export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): IAction[][] {
832 833
	const scopedContextKeyService = contextKeyService.createScoped();
	if (extension) {
834
		scopedContextKeyService.createKey<string>('extension', extension.identifier.id);
835
		scopedContextKeyService.createKey<boolean>('isBuiltinExtension', extension.isBuiltin);
836 837 838 839 840 841
		scopedContextKeyService.createKey<boolean>('extensionHasConfiguration', extension.local && !!extension.local.manifest.contributes && !!extension.local.manifest.contributes.configuration);
		if (extension.state === ExtensionState.Installed) {
			scopedContextKeyService.createKey<string>('extensionStatus', 'installed');
		}
	}

842
	const groups: IAction[][] = [];
843
	const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService);
844 845 846 847 848 849
	menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => {
		if (action instanceof SubmenuAction) {
			return action;
		}
		return instantiationService.createInstance(MenuItemExtensionAction, action);
	})));
S
Sandeep Somavarapu 已提交
850
	menu.dispose();
J
João Moreno 已提交
851
	scopedContextKeyService.dispose();
852

S
Sandeep Somavarapu 已提交
853 854 855
	return groups;
}

S
Sandeep Somavarapu 已提交
856
export class ManageExtensionAction extends ExtensionDropDownAction {
857

858
	static readonly ID = 'extensions.manage';
S
Sandeep Somavarapu 已提交
859 860

	private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`;
861
	private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`;
862 863

	constructor(
S
Sandeep Somavarapu 已提交
864
		@IInstantiationService instantiationService: IInstantiationService,
865
		@IExtensionService private readonly extensionService: IExtensionService,
866 867 868
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
		@IMenuService private readonly menuService: IMenuService,
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
869
	) {
S
Sandeep Somavarapu 已提交
870 871

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

S
Sandeep Somavarapu 已提交
873
		this.tooltip = localize('manage', "Manage");
874 875 876 877

		this.update();
	}

878
	async getActionGroups(runningExtensions: IExtensionDescription[]): Promise<IAction[][]> {
879
		const groups: IAction[][] = [];
880
		if (this.extension) {
881 882 883 884 885 886 887 888 889 890
			const actions = await Promise.all([
				SetColorThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension),
				SetFileIconThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension),
				SetProductIconThemeAction.create(this.workbenchThemeService, this.instantiationService, this.extension)
			]);

			const themesGroup: ExtensionAction[] = [];
			for (let action of actions) {
				if (action) {
					themesGroup.push(action);
891
				}
892 893
			}
			if (themesGroup.length) {
894 895 896
				groups.push(themesGroup);
			}
		}
S
Sandeep Somavarapu 已提交
897
		groups.push([
S
Sandeep Somavarapu 已提交
898
			this.instantiationService.createInstance(EnableGloballyAction),
899
			this.instantiationService.createInstance(EnableForWorkspaceAction)
S
Sandeep Somavarapu 已提交
900 901
		]);
		groups.push([
S
Sandeep Somavarapu 已提交
902 903
			this.instantiationService.createInstance(DisableGloballyAction, runningExtensions),
			this.instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions)
S
Sandeep Somavarapu 已提交
904
		]);
905
		groups.push([this.instantiationService.createInstance(UninstallAction)]);
S
Sandeep Somavarapu 已提交
906
		groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
P
Peng Lyu 已提交
907

S
Sandeep Somavarapu 已提交
908
		getContextMenuActions(this.menuService, this.contextKeyService, this.instantiationService, this.extension).forEach(actions => groups.push(actions));
P
Peng Lyu 已提交
909

910 911 912 913 914
		groups.forEach(group => group.forEach(extensionAction => {
			if (extensionAction instanceof ExtensionAction) {
				extensionAction.extension = this.extension;
			}
		}));
S
Sandeep Somavarapu 已提交
915 916 917 918

		return groups;
	}

919 920
	async run(): Promise<any> {
		const runtimeExtensions = await this.extensionService.getExtensions();
921
		return super.run({ actionGroups: await this.getActionGroups(runtimeExtensions), disposeActionsOnHide: true });
S
Sandeep Somavarapu 已提交
922 923
	}

S
Sandeep Somavarapu 已提交
924
	update(): void {
S
Sandeep Somavarapu 已提交
925
		this.class = ManageExtensionAction.HideManageExtensionClass;
926
		this.enabled = false;
S
Sandeep Somavarapu 已提交
927
		if (this.extension) {
928 929
			const state = this.extension.state;
			this.enabled = state === ExtensionState.Installed;
S
Sandeep Somavarapu 已提交
930
			this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass;
931 932 933 934 935
			this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : '';
		}
	}
}

936 937
export class MenuItemExtensionAction extends ExtensionAction {

S
Sandeep Somavarapu 已提交
938 939
	constructor(
		private readonly action: IAction,
940
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
941
	) {
942 943 944
		super(action.id, action.label);
	}

S
Sandeep Somavarapu 已提交
945 946 947 948 949
	update() {
		if (!this.extension) {
			return;
		}
		if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) {
950
			this.checked = !this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);
S
Sandeep Somavarapu 已提交
951 952
		}
	}
953 954 955

	async run(): Promise<void> {
		if (this.extension) {
S
Sandeep Somavarapu 已提交
956
			return this.action.run(this.extension.identifier.id);
957 958 959 960
		}
	}
}

S
Sandeep Somavarapu 已提交
961
export class InstallAnotherVersionAction extends ExtensionAction {
962

963
	static readonly ID = 'workbench.extensions.action.install.anotherVersion';
964
	static readonly LABEL = localize('install another version', "Install Another Version...");
965

S
Sandeep Somavarapu 已提交
966
	constructor(
967 968 969 970 971
		@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,
972 973
	) {
		super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL);
S
Sandeep Somavarapu 已提交
974 975 976 977
		this.update();
	}

	update(): void {
978
		this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery;
979 980
	}

J
Johannes Rieken 已提交
981
	run(): Promise<any> {
982 983 984
		if (!this.enabled) {
			return Promise.resolve();
		}
S
Sandeep Somavarapu 已提交
985
		return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true })
986 987
			.then(pick => {
				if (pick) {
S
Sandeep Somavarapu 已提交
988
					if (this.extension!.version === pick.id) {
S
Sandeep Somavarapu 已提交
989 990
						return Promise.resolve();
					}
S
Sandeep Somavarapu 已提交
991
					const promise: Promise<any> = pick.latest ? this.extensionsWorkbenchService.install(this.extension!) : this.extensionsWorkbenchService.installVersion(this.extension!, pick.id);
S
Sandeep Somavarapu 已提交
992 993
					return promise
						.then(null, err => {
S
Sandeep Somavarapu 已提交
994
							if (!this.extension!.gallery) {
995 996 997 998
								return this.notificationService.error(err);
							}

							console.error(err);
999

1000
							return promptDownloadManually(this.extension!.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension!.identifier.id), err, this.instantiationService);
1001 1002 1003 1004 1005
						});
				}
				return null;
			});
	}
S
Sandeep Somavarapu 已提交
1006

1007
	private getVersionEntries(): Promise<(IQuickPickItem & { latest: boolean, id: string })[]> {
S
Sandeep Somavarapu 已提交
1008 1009
		return this.extensionGalleryService.getAllVersions(this.extension!.gallery!, true)
			.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 已提交
1010
	}
1011 1012
}

S
Sandeep Somavarapu 已提交
1013
export class EnableForWorkspaceAction extends ExtensionAction {
1014

1015
	static readonly ID = 'extensions.enableForWorkspace';
1016
	static readonly LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");
1017

S
Sandeep Somavarapu 已提交
1018
	constructor(
1019
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
1020
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
1021
	) {
1022
		super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);
1023 1024 1025
		this.update();
	}

S
Sandeep Somavarapu 已提交
1026
	update(): void {
1027
		this.enabled = false;
1028 1029
		if (this.extension && this.extension.local) {
			this.enabled = this.extension.state === ExtensionState.Installed
1030
				&& !this.extensionEnablementService.isEnabled(this.extension.local)
S
Sandeep Somavarapu 已提交
1031
				&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);
1032 1033 1034
		}
	}

S
Sandeep Somavarapu 已提交
1035 1036 1037 1038
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
1039
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledWorkspace);
1040 1041 1042
	}
}

S
Sandeep Somavarapu 已提交
1043
export class EnableGloballyAction extends ExtensionAction {
1044

1045
	static readonly ID = 'extensions.enableGlobally';
1046
	static readonly LABEL = localize('enableGloballyAction', "Enable");
1047

S
Sandeep Somavarapu 已提交
1048
	constructor(
1049
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
1050
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
1051
	) {
1052
		super(EnableGloballyAction.ID, EnableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);
1053 1054 1055
		this.update();
	}

S
Sandeep Somavarapu 已提交
1056
	update(): void {
1057
		this.enabled = false;
1058
		if (this.extension && this.extension.local) {
1059
			this.enabled = this.extension.state === ExtensionState.Installed
S
Sandeep Somavarapu 已提交
1060
				&& this.extensionEnablementService.isDisabledGlobally(this.extension.local)
1061
				&& this.extensionEnablementService.canChangeEnablement(this.extension.local);
1062 1063 1064
		}
	}

S
Sandeep Somavarapu 已提交
1065 1066 1067 1068
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
1069
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.EnabledGlobally);
1070 1071 1072
	}
}

S
Sandeep Somavarapu 已提交
1073
export class DisableForWorkspaceAction extends ExtensionAction {
1074

1075
	static readonly ID = 'extensions.disableForWorkspace';
1076
	static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");
1077

S
Sandeep Somavarapu 已提交
1078
	constructor(readonly runningExtensions: IExtensionDescription[],
1079 1080
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
1081
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
1082
	) {
1083
		super(DisableForWorkspaceAction.ID, DisableForWorkspaceAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);
1084 1085 1086
		this.update();
	}

S
Sandeep Somavarapu 已提交
1087
	update(): void {
1088
		this.enabled = false;
S
Sandeep Somavarapu 已提交
1089
		if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
1090
			this.enabled = this.extension.state === ExtensionState.Installed
1091
				&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)
S
Sandeep Somavarapu 已提交
1092
				&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);
1093 1094 1095
		}
	}

S
Sandeep Somavarapu 已提交
1096 1097 1098 1099
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
1100
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledWorkspace);
1101 1102 1103
	}
}

S
Sandeep Somavarapu 已提交
1104
export class DisableGloballyAction extends ExtensionAction {
1105

1106
	static readonly ID = 'extensions.disableGlobally';
1107
	static readonly LABEL = localize('disableGloballyAction', "Disable");
1108

S
Sandeep Somavarapu 已提交
1109
	constructor(readonly runningExtensions: IExtensionDescription[],
1110
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
1111
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
1112
	) {
1113
		super(DisableGloballyAction.ID, DisableGloballyAction.LABEL, ExtensionAction.LABEL_ACTION_CLASS);
1114 1115 1116
		this.update();
	}

S
Sandeep Somavarapu 已提交
1117
	update(): void {
1118
		this.enabled = false;
S
Sandeep Somavarapu 已提交
1119
		if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))) {
1120
			this.enabled = this.extension.state === ExtensionState.Installed
1121
				&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)
1122
				&& this.extensionEnablementService.canChangeEnablement(this.extension.local);
1123 1124 1125
		}
	}

S
Sandeep Somavarapu 已提交
1126 1127 1128 1129
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
1130
		return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.DisabledGlobally);
1131 1132 1133
	}
}

S
Sandeep Somavarapu 已提交
1134
export class EnableDropDownAction extends ActionWithDropDownAction {
S
Sandeep Somavarapu 已提交
1135 1136

	constructor(
S
Sandeep Somavarapu 已提交
1137
		@IInstantiationService instantiationService: IInstantiationService
S
Sandeep Somavarapu 已提交
1138
	) {
S
Sandeep Somavarapu 已提交
1139 1140
		super('extensions.enable', localize('enableAction', "Enable"), [
			instantiationService.createInstance(EnableGloballyAction),
1141
			instantiationService.createInstance(EnableForWorkspaceAction)
S
Sandeep Somavarapu 已提交
1142
		]);
S
Sandeep Somavarapu 已提交
1143 1144 1145
	}
}

S
Sandeep Somavarapu 已提交
1146
export class DisableDropDownAction extends ActionWithDropDownAction {
S
Sandeep Somavarapu 已提交
1147 1148

	constructor(
S
Sandeep Somavarapu 已提交
1149 1150
		runningExtensions: IExtensionDescription[],
		@IInstantiationService instantiationService: IInstantiationService
S
Sandeep Somavarapu 已提交
1151
	) {
S
Sandeep Somavarapu 已提交
1152 1153 1154
		super('extensions.disable', localize('disableAction', "Disable"), [
			instantiationService.createInstance(DisableGloballyAction, runningExtensions),
			instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions)
S
Sandeep Somavarapu 已提交
1155
		]);
1156
	}
S
Sandeep Somavarapu 已提交
1157

1158 1159
}

J
Joao Moreno 已提交
1160 1161
export class CheckForUpdatesAction extends Action {

1162
	static readonly ID = 'workbench.extensions.action.checkForUpdates';
1163
	static readonly LABEL = localize('checkForUpdates', "Check for Extension Updates");
J
Joao Moreno 已提交
1164 1165

	constructor(
1166 1167
		id = CheckForUpdatesAction.ID,
		label = CheckForUpdatesAction.LABEL,
1168
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
1169
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
1170 1171
		@IViewletService private readonly viewletService: IViewletService,
		@INotificationService private readonly notificationService: INotificationService
J
Joao Moreno 已提交
1172 1173 1174 1175
	) {
		super(id, label, '', true);
	}

1176
	private checkUpdatesAndNotify(): void {
1177 1178
		const outdated = this.extensionsWorkbenchService.outdated;
		if (!outdated.length) {
1179
			this.notificationService.info(localize('noUpdatesAvailable', "All extensions are up to date."));
1180 1181
			return;
		}
1182

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

1185
		const disabledExtensionsCount = outdated.filter(ext => ext.local && !this.extensionEnablementService.isEnabled(ext.local)).length;
1186 1187 1188 1189 1190 1191 1192 1193 1194
		if (disabledExtensionsCount) {
			if (outdated.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.", outdated.length);
			} else if (disabledExtensionsCount === outdated.length) {
				msgAvailableExtensions = localize('updatesAvailableAllDisabled', "{0} extension updates are available. All of them are for disabled extensions.", outdated.length);
			} else {
				msgAvailableExtensions = localize('updatesAvailableIncludingDisabled', "{0} extension updates are available. {1} of them are for disabled extensions.", outdated.length, disabledExtensionsCount);
1195
			}
1196 1197 1198
		}

		this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1199
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1200 1201 1202
			.then(viewlet => viewlet.search(''));

		this.notificationService.info(msgAvailableExtensions);
1203 1204
	}

S
Sandeep Somavarapu 已提交
1205
	run(): Promise<any> {
1206
		return this.extensionsWorkbenchService.checkForUpdates().then(() => this.checkUpdatesAndNotify());
J
Joao Moreno 已提交
1207 1208 1209
	}
}

S
Sandeep Somavarapu 已提交
1210 1211 1212 1213 1214 1215
export class ToggleAutoUpdateAction extends Action {

	constructor(
		id: string,
		label: string,
		private autoUpdateValue: boolean,
1216
		@IConfigurationService private readonly configurationService: IConfigurationService
S
Sandeep Somavarapu 已提交
1217 1218 1219
	) {
		super(id, label, '', true);
		this.updateEnablement();
1220
		configurationService.onDidChangeConfiguration(() => this.updateEnablement());
S
Sandeep Somavarapu 已提交
1221 1222 1223
	}

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

S
Sandeep Somavarapu 已提交
1227
	run(): Promise<any> {
1228
		return this.configurationService.updateValue(AutoUpdateConfigurationKey, this.autoUpdateValue);
S
Sandeep Somavarapu 已提交
1229 1230 1231 1232 1233
	}
}

export class EnableAutoUpdateAction extends ToggleAutoUpdateAction {

1234
	static readonly ID = 'workbench.extensions.action.enableAutoUpdate';
1235
	static readonly LABEL = localize('enableAutoUpdate', "Enable Auto Updating Extensions");
S
Sandeep Somavarapu 已提交
1236 1237 1238 1239

	constructor(
		id = EnableAutoUpdateAction.ID,
		label = EnableAutoUpdateAction.LABEL,
1240
		@IConfigurationService configurationService: IConfigurationService
S
Sandeep Somavarapu 已提交
1241
	) {
1242
		super(id, label, true, configurationService);
S
Sandeep Somavarapu 已提交
1243 1244 1245 1246 1247
	}
}

export class DisableAutoUpdateAction extends ToggleAutoUpdateAction {

1248
	static readonly ID = 'workbench.extensions.action.disableAutoUpdate';
1249
	static readonly LABEL = localize('disableAutoUpdate', "Disable Auto Updating Extensions");
S
Sandeep Somavarapu 已提交
1250 1251 1252 1253

	constructor(
		id = EnableAutoUpdateAction.ID,
		label = EnableAutoUpdateAction.LABEL,
1254
		@IConfigurationService configurationService: IConfigurationService
S
Sandeep Somavarapu 已提交
1255
	) {
1256
		super(id, label, false, configurationService);
S
Sandeep Somavarapu 已提交
1257 1258 1259
	}
}

1260 1261
export class UpdateAllAction extends Action {

1262
	static readonly ID = 'workbench.extensions.action.updateAllExtensions';
1263
	static readonly LABEL = localize('updateAll', "Update All Extensions");
1264 1265

	constructor(
S
Sandeep Somavarapu 已提交
1266
		id: string, label: string, isPrimary: boolean,
1267 1268 1269
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@INotificationService private readonly notificationService: INotificationService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
1270 1271 1272
	) {
		super(id, label, '', false);

S
Sandeep Somavarapu 已提交
1273 1274 1275
		if (isPrimary) {
			this._register(this.extensionsWorkbenchService.onChange(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
1276 1277
	}

S
Sandeep Somavarapu 已提交
1278 1279
	get enabled(): boolean {
		return this.extensionsWorkbenchService.outdated.length > 0;
1280 1281
	}

S
Sandeep Somavarapu 已提交
1282
	run(): Promise<any> {
1283
		return Promise.all(this.extensionsWorkbenchService.outdated.map(e => this.install(e)));
J
Joao Moreno 已提交
1284 1285
	}

J
Johannes Rieken 已提交
1286
	private install(extension: IExtension): Promise<any> {
R
Rob Lourens 已提交
1287
		return this.extensionsWorkbenchService.install(extension).then(undefined, err => {
1288
			if (!extension.gallery) {
J
Joao Moreno 已提交
1289 1290 1291 1292
				return this.notificationService.error(err);
			}

			console.error(err);
1293

1294
			return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.identifier.id), err, this.instantiationService);
J
Joao Moreno 已提交
1295
		});
1296 1297 1298
	}
}

S
Sandeep Somavarapu 已提交
1299
export class ReloadAction extends ExtensionAction {
1300

S
Sandeep Somavarapu 已提交
1301
	private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} reload`;
1302
	private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`;
1303

1304
	updateWhenCounterExtensionChanges: boolean = true;
1305
	private _runningExtensions: IExtensionDescription[] | null = null;
1306 1307

	constructor(
1308
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
1309
		@IHostService private readonly hostService: IHostService,
1310
		@IExtensionService private readonly extensionService: IExtensionService,
S
rename  
Sandeep Somavarapu 已提交
1311
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
1312 1313 1314
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
		@IProductService private readonly productService: IProductService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
1315 1316
	) {
		super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false);
M
Matt Bierner 已提交
1317
		this._register(this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this));
1318
		this.updateRunningExtensions();
1319 1320
	}

1321
	private updateRunningExtensions(): void {
1322
		this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
1323 1324 1325 1326 1327
	}

	update(): void {
		this.enabled = false;
		this.tooltip = '';
1328
		if (!this.extension || !this._runningExtensions) {
1329 1330 1331 1332 1333 1334
			return;
		}
		const state = this.extension.state;
		if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
			return;
		}
1335
		if (this.extension.local && this.extension.local.manifest && this.extension.local.manifest.contributes && this.extension.local.manifest.contributes.localizations && this.extension.local.manifest.contributes.localizations.length > 0) {
1336 1337
			return;
		}
1338
		this.computeReloadState();
1339
		this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
1340 1341
	}

1342
	private computeReloadState(): void {
S
Sandeep Somavarapu 已提交
1343
		if (!this._runningExtensions || !this.extension) {
1344 1345
			return;
		}
S
Sandeep Somavarapu 已提交
1346

1347
		const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
S
Sandeep Somavarapu 已提交
1348
		const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];
1349
		const isSameExtensionRunning = runningExtension && this.extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension));
1350

1351
		if (isUninstalled) {
S
Sandeep Somavarapu 已提交
1352
			if (isSameExtensionRunning && !this.extensionService.canRemoveExtension(runningExtension)) {
1353 1354 1355 1356 1357 1358 1359
				this.enabled = true;
				this.label = localize('reloadRequired', "Reload Required");
				this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension.");
				alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName));
			}
			return;
		}
1360 1361
		if (this.extension.local) {
			const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
S
Sandeep Somavarapu 已提交
1362

1363
			// Extension is running
1364
			if (runningExtension) {
1365
				if (isEnabled) {
1366 1367 1368 1369
					// No Reload is required if extension can run without reload
					if (this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
						return;
					}
1370
					const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension));
S
Sandeep Somavarapu 已提交
1371

1372 1373 1374 1375 1376 1377
					if (isSameExtensionRunning) {
						// Different version of same extension is running. Requires reload to run the current version
						if (this.extension.version !== runningExtension.version) {
							this.enabled = true;
							this.label = localize('reloadRequired', "Reload Required");
							this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to enable the updated extension.");
S
Sandeep Somavarapu 已提交
1378
							return;
1379
						}
S
Sandeep Somavarapu 已提交
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399

						const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)[0];
						if (extensionInOtherServer) {
							// This extension prefers to run on UI/Local side but is running in remote
							if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer && prefersExecuteOnUI(this.extension.local!.manifest, this.productService, this.configurationService)) {
								this.enabled = true;
								this.label = localize('reloadRequired', "Reload Required");
								this.tooltip = localize('enable locally', "Please reload Visual Studio Code to enable this extension locally.");
								return;
							}

							// This extension prefers to run on Workspace/Remote side but is running in local
							if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) {
								this.enabled = true;
								this.label = localize('reloadRequired', "Reload Required");
								this.tooltip = localize('enable remote', "Please reload Visual Studio Code to enable this extension in {0}.", this.extensionManagementServerService.remoteExtensionManagementServer?.label);
								return;
							}
						}

1400
					} else {
S
Sandeep Somavarapu 已提交
1401

1402 1403 1404
						if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
							// This extension prefers to run on UI/Local side but is running in remote
							if (prefersExecuteOnUI(this.extension.local!.manifest, this.productService, this.configurationService)) {
1405 1406
								this.enabled = true;
								this.label = localize('reloadRequired', "Reload Required");
1407 1408 1409 1410 1411 1412 1413 1414 1415
								this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension.");
							}
						}
						if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
							// This extension prefers to run on Workspace/Remote side but is running in local
							if (prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) {
								this.enabled = true;
								this.label = localize('reloadRequired', "Reload Required");
								this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension.");
1416 1417
							}
						}
1418
					}
1419
					return;
1420 1421 1422 1423 1424
				} else {
					if (isSameExtensionRunning) {
						this.enabled = true;
						this.label = localize('reloadRequired', "Reload Required");
						this.tooltip = localize('postDisableTooltip', "Please reload Visual Studio Code to disable this extension.");
S
Sandeep Somavarapu 已提交
1425
					}
1426
				}
1427
				return;
S
Sandeep Somavarapu 已提交
1428 1429 1430 1431
			}

			// Extension is not running
			else {
1432
				if (isEnabled && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
1433
					this.enabled = true;
S
Sandeep Somavarapu 已提交
1434
					this.label = localize('reloadRequired', "Reload Required");
1435
					this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension.");
1436
					return;
1437
				}
S
Sandeep Somavarapu 已提交
1438 1439 1440

				const otherServer = this.extension.server ? this.extension.server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer : null;
				if (otherServer && this.extension.enablementState === EnablementState.DisabledByExtensionKind) {
S
Sandeep Somavarapu 已提交
1441
					const extensionInOtherServer = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === otherServer)[0];
S
Sandeep Somavarapu 已提交
1442 1443 1444 1445 1446
					// Same extension in other server exists and
					if (extensionInOtherServer && extensionInOtherServer.local && this.extensionEnablementService.isEnabled(extensionInOtherServer.local)) {
						this.enabled = true;
						this.label = localize('reloadRequired', "Reload Required");
						this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension.");
1447
						alert(localize('installExtensionCompletedAndReloadRequired', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName));
S
Sandeep Somavarapu 已提交
1448
						return;
S
Explore  
Sandeep Somavarapu 已提交
1449
					}
1450
				}
1451 1452 1453 1454
			}
		}
	}

S
Sandeep Somavarapu 已提交
1455
	run(): Promise<any> {
1456
		return Promise.resolve(this.hostService.reload());
1457 1458 1459
	}
}

1460 1461 1462
function isThemeFromExtension(theme: IWorkbenchTheme, extension: IExtension | undefined | null): boolean {
	return !!(extension && theme.extensionData && ExtensionIdentifier.equals(theme.extensionData.extensionId, extension.identifier.id));
}
S
Sandeep Somavarapu 已提交
1463

1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
function getQuickPickEntries(themes: IWorkbenchTheme[], currentTheme: IWorkbenchTheme, extension: IExtension | null | undefined, showCurrentTheme: boolean): (IQuickPickItem | IQuickPickSeparator)[] {
	const picks: (IQuickPickItem | IQuickPickSeparator)[] = [];
	for (const theme of themes) {
		if (isThemeFromExtension(theme, extension) && !(showCurrentTheme && theme === currentTheme)) {
			picks.push({ label: theme.label, id: theme.id });
		}
	}
	if (showCurrentTheme) {
		picks.push(<IQuickPickSeparator>{ type: 'separator', label: localize('current', "Current") });
		picks.push(<IQuickPickItem>{ label: currentTheme.label, id: currentTheme.id });
1474
	}
1475 1476 1477 1478 1479
	return picks;
}


export class SetColorThemeAction extends ExtensionAction {
1480

S
Sandeep Somavarapu 已提交
1481
	private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;
S
Sandeep Somavarapu 已提交
1482 1483
	private static readonly DisabledClass = `${SetColorThemeAction.EnabledClass} disabled`;

1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
	static async create(workbenchThemeService: IWorkbenchThemeService, instantiationService: IInstantiationService, extension: IExtension): Promise<SetColorThemeAction | undefined> {
		const themes = await workbenchThemeService.getColorThemes();
		if (themes.some(th => isThemeFromExtension(th, extension))) {
			const action = instantiationService.createInstance(SetColorThemeAction, themes);
			action.extension = extension;
			return action;
		}
		return undefined;
	}

S
Sandeep Somavarapu 已提交
1494
	constructor(
1495
		private colorThemes: IWorkbenchColorTheme[],
S
Sandeep Somavarapu 已提交
1496 1497 1498 1499 1500
		@IExtensionService extensionService: IExtensionService,
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
	) {
		super(`extensions.colorTheme`, localize('color theme', "Set Color Theme"), SetColorThemeAction.DisabledClass, false);
M
Matt Bierner 已提交
1501
		this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidColorThemeChange)(() => this.update(), this));
S
Sandeep Somavarapu 已提交
1502 1503 1504
		this.update();
	}

1505
	update(): void {
1506
		this.enabled = !!this.extension && (this.extension.state === ExtensionState.Installed) && this.colorThemes.some(th => isThemeFromExtension(th, this.extension));
S
Sandeep Somavarapu 已提交
1507 1508 1509
		this.class = this.enabled ? SetColorThemeAction.EnabledClass : SetColorThemeAction.DisabledClass;
	}

S
Sandeep Somavarapu 已提交
1510
	async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean, ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {
1511 1512
		this.colorThemes = await this.workbenchThemeService.getColorThemes();

1513
		this.update();
S
Sandeep Somavarapu 已提交
1514 1515 1516
		if (!this.enabled) {
			return;
		}
1517
		const currentTheme = this.workbenchThemeService.getColorTheme();
S
Sandeep Somavarapu 已提交
1518 1519

		const delayer = new Delayer<any>(100);
1520
		const picks = getQuickPickEntries(this.colorThemes, currentTheme, this.extension, showCurrentTheme);
S
Sandeep Somavarapu 已提交
1521 1522 1523 1524
		const pickedTheme = await this.quickInputService.pick(
			picks,
			{
				placeHolder: localize('select color theme', "Select Color Theme"),
S
Sandeep Somavarapu 已提交
1525 1526
				onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setColorTheme(item.id, undefined)),
				ignoreFocusLost
S
Sandeep Somavarapu 已提交
1527
			});
1528
		return this.workbenchThemeService.setColorTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');
S
Sandeep Somavarapu 已提交
1529 1530 1531 1532 1533
	}
}

export class SetFileIconThemeAction extends ExtensionAction {

S
Sandeep Somavarapu 已提交
1534
	private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;
S
Sandeep Somavarapu 已提交
1535 1536
	private static readonly DisabledClass = `${SetFileIconThemeAction.EnabledClass} disabled`;

1537 1538 1539 1540 1541 1542 1543 1544
	static async create(workbenchThemeService: IWorkbenchThemeService, instantiationService: IInstantiationService, extension: IExtension): Promise<SetFileIconThemeAction | undefined> {
		const themes = await workbenchThemeService.getFileIconThemes();
		if (themes.some(th => isThemeFromExtension(th, extension))) {
			const action = instantiationService.createInstance(SetFileIconThemeAction, themes);
			action.extension = extension;
			return action;
		}
		return undefined;
1545 1546
	}

S
Sandeep Somavarapu 已提交
1547
	constructor(
1548
		private fileIconThemes: IWorkbenchFileIconTheme[],
S
Sandeep Somavarapu 已提交
1549 1550
		@IExtensionService extensionService: IExtensionService,
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
1551
		@IQuickInputService private readonly quickInputService: IQuickInputService
S
Sandeep Somavarapu 已提交
1552 1553
	) {
		super(`extensions.fileIconTheme`, localize('file icon theme', "Set File Icon Theme"), SetFileIconThemeAction.DisabledClass, false);
M
Matt Bierner 已提交
1554
		this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidFileIconThemeChange)(() => this.update(), this));
S
Sandeep Somavarapu 已提交
1555 1556 1557
		this.update();
	}

1558
	update(): void {
1559
		this.enabled = !!this.extension && (this.extension.state === ExtensionState.Installed) && this.fileIconThemes.some(th => isThemeFromExtension(th, this.extension));
S
Sandeep Somavarapu 已提交
1560 1561 1562
		this.class = this.enabled ? SetFileIconThemeAction.EnabledClass : SetFileIconThemeAction.DisabledClass;
	}

S
Sandeep Somavarapu 已提交
1563
	async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean, ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {
1564 1565
		this.fileIconThemes = await this.workbenchThemeService.getFileIconThemes();
		this.update();
S
Sandeep Somavarapu 已提交
1566 1567 1568
		if (!this.enabled) {
			return;
		}
1569
		const currentTheme = this.workbenchThemeService.getFileIconTheme();
S
Sandeep Somavarapu 已提交
1570 1571

		const delayer = new Delayer<any>(100);
1572
		const picks = getQuickPickEntries(this.fileIconThemes, currentTheme, this.extension, showCurrentTheme);
S
Sandeep Somavarapu 已提交
1573 1574 1575 1576
		const pickedTheme = await this.quickInputService.pick(
			picks,
			{
				placeHolder: localize('select file icon theme', "Select File Icon Theme"),
S
Sandeep Somavarapu 已提交
1577 1578
				onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setFileIconTheme(item.id, undefined)),
				ignoreFocusLost
S
Sandeep Somavarapu 已提交
1579
			});
1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
		return this.workbenchThemeService.setFileIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');
	}
}

export class SetProductIconThemeAction extends ExtensionAction {

	private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`;
	private static readonly DisabledClass = `${SetProductIconThemeAction.EnabledClass} disabled`;

	static async create(workbenchThemeService: IWorkbenchThemeService, instantiationService: IInstantiationService, extension: IExtension): Promise<SetProductIconThemeAction | undefined> {
		const themes = await workbenchThemeService.getProductIconThemes();
		if (themes.some(th => isThemeFromExtension(th, extension))) {
			const action = instantiationService.createInstance(SetProductIconThemeAction, themes);
			action.extension = extension;
			return action;
		}
		return undefined;
	}

	constructor(
		private productIconThemes: IWorkbenchProductIconTheme[],
		@IExtensionService extensionService: IExtensionService,
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
		@IQuickInputService private readonly quickInputService: IQuickInputService
	) {
		super(`extensions.productIconTheme`, localize('product icon theme', "Set Product Icon Theme"), SetProductIconThemeAction.DisabledClass, false);
		this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidProductIconThemeChange)(() => this.update(), this));
		this.enabled = true; // enabled by default
		this.class = SetProductIconThemeAction.EnabledClass;
		//		this.update();
	}

	update(): void {
		this.enabled = !!this.extension && (this.extension.state === ExtensionState.Installed) && this.productIconThemes.some(th => isThemeFromExtension(th, this.extension));
		this.class = this.enabled ? SetProductIconThemeAction.EnabledClass : SetProductIconThemeAction.DisabledClass;
	}

	async run({ showCurrentTheme, ignoreFocusLost }: { showCurrentTheme: boolean, ignoreFocusLost: boolean } = { showCurrentTheme: false, ignoreFocusLost: false }): Promise<any> {
		this.productIconThemes = await this.workbenchThemeService.getProductIconThemes();
		this.update();
		if (!this.enabled) {
			return;
		}

		const currentTheme = this.workbenchThemeService.getProductIconTheme();

		const delayer = new Delayer<any>(100);
		const picks = getQuickPickEntries(this.productIconThemes, currentTheme, this.extension, showCurrentTheme);
		const pickedTheme = await this.quickInputService.pick(
			picks,
			{
				placeHolder: localize('select product icon theme', "Select Product Icon Theme"),
				onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setProductIconTheme(item.id, undefined)),
				ignoreFocusLost
			});
		return this.workbenchThemeService.setProductIconTheme(pickedTheme ? pickedTheme.id : currentTheme.id, 'auto');
S
Sandeep Somavarapu 已提交
1636 1637 1638
	}
}

B
Benjamin Pasero 已提交
1639
export class OpenExtensionsViewletAction extends ShowViewletAction {
1640 1641 1642 1643 1644 1645 1646 1647

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

	constructor(
		id: string,
		label: string,
		@IViewletService viewletService: IViewletService,
B
Benjamin Pasero 已提交
1648
		@IEditorGroupsService editorGroupService: IEditorGroupsService,
1649
		@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
1650
	) {
1651
		super(id, label, VIEWLET_ID, viewletService, editorGroupService, layoutService);
1652 1653 1654 1655 1656 1657 1658 1659
	}
}

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

1660 1661
export class ShowEnabledExtensionsAction extends Action {

1662
	static readonly ID = 'workbench.extensions.action.showEnabledExtensions';
1663
	static readonly LABEL = localize('showEnabledExtensions', "Show Enabled Extensions");
1664 1665 1666 1667

	constructor(
		id: string,
		label: string,
1668
		@IViewletService private readonly viewletService: IViewletService
1669
	) {
R
Rob Lourens 已提交
1670
		super(id, label, undefined, true);
1671 1672
	}

J
Johannes Rieken 已提交
1673
	run(): Promise<void> {
1674
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1675
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1676
			.then(viewlet => {
1677
				viewlet.search('@enabled ');
1678 1679 1680 1681 1682
				viewlet.focus();
			});
	}
}

1683 1684
export class ShowInstalledExtensionsAction extends Action {

1685
	static readonly ID = 'workbench.extensions.action.showInstalledExtensions';
1686
	static readonly LABEL = localize('showInstalledExtensions', "Show Installed Extensions");
1687 1688 1689 1690

	constructor(
		id: string,
		label: string,
1691
		@IViewletService private readonly viewletService: IViewletService
1692
	) {
R
Rob Lourens 已提交
1693
		super(id, label, undefined, true);
1694 1695
	}

1696
	run(refresh?: boolean): Promise<void> {
1697
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1698
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1699
			.then(viewlet => {
1700
				viewlet.search('@installed ', refresh);
1701 1702 1703 1704 1705 1706 1707
				viewlet.focus();
			});
	}
}

export class ShowDisabledExtensionsAction extends Action {

1708
	static readonly ID = 'workbench.extensions.action.showDisabledExtensions';
1709
	static readonly LABEL = localize('showDisabledExtensions', "Show Disabled Extensions");
1710 1711 1712 1713

	constructor(
		id: string,
		label: string,
1714
		@IViewletService private readonly viewletService: IViewletService
1715 1716 1717 1718
	) {
		super(id, label, 'null', true);
	}

J
Johannes Rieken 已提交
1719
	run(): Promise<void> {
1720
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1721
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1722 1723 1724 1725 1726 1727 1728
			.then(viewlet => {
				viewlet.search('@disabled ');
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
1729
export class ClearExtensionsSearchResultsAction extends Action {
1730

S
Sandeep Somavarapu 已提交
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752
	static readonly ID = 'workbench.extensions.action.clearExtensionsSearchResults';
	static readonly LABEL = localize('clearExtensionsSearchResults', "Clear Extensions Search Results");

	constructor(
		id: string,
		label: string,
		@IViewsService private readonly viewsService: IViewsService
	) {
		super(id, label, 'codicon-clear-all', true);
	}

	async run(): Promise<void> {
		const viewPaneContainer = this.viewsService.getActiveViewPaneContainerWithId(VIEWLET_ID);
		if (viewPaneContainer) {
			const extensionsViewPaneContainer = viewPaneContainer as IExtensionsViewPaneContainer;
			extensionsViewPaneContainer.search('');
			extensionsViewPaneContainer.focus();
		}
	}
}

export class ClearExtensionsInputAction extends ClearExtensionsSearchResultsAction {
1753 1754 1755 1756 1757

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
1758
		value: string,
S
Sandeep Somavarapu 已提交
1759
		@IViewsService viewsService: IViewsService
1760
	) {
S
Sandeep Somavarapu 已提交
1761
		super(id, label, viewsService);
1762
		this.onSearchChange(value);
M
Matt Bierner 已提交
1763
		this._register(onSearchChange(this.onSearchChange, this));
1764 1765 1766 1767 1768 1769 1770 1771
	}

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

}

S
Sandeep Somavarapu 已提交
1772 1773 1774
export class ShowBuiltInExtensionsAction extends Action {

	static readonly ID = 'workbench.extensions.action.listBuiltInExtensions';
1775
	static readonly LABEL = localize('showBuiltInExtensions', "Show Built-in Extensions");
S
Sandeep Somavarapu 已提交
1776 1777 1778 1779

	constructor(
		id: string,
		label: string,
1780
		@IViewletService private readonly viewletService: IViewletService
S
Sandeep Somavarapu 已提交
1781
	) {
R
Rob Lourens 已提交
1782
		super(id, label, undefined, true);
S
Sandeep Somavarapu 已提交
1783 1784
	}

J
Johannes Rieken 已提交
1785
	run(): Promise<void> {
S
Sandeep Somavarapu 已提交
1786
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1787
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
S
Sandeep Somavarapu 已提交
1788 1789 1790 1791 1792 1793 1794
			.then(viewlet => {
				viewlet.search('@builtin ');
				viewlet.focus();
			});
	}
}

1795 1796
export class ShowOutdatedExtensionsAction extends Action {

1797
	static readonly ID = 'workbench.extensions.action.listOutdatedExtensions';
1798
	static readonly LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions");
1799 1800 1801 1802

	constructor(
		id: string,
		label: string,
1803
		@IViewletService private readonly viewletService: IViewletService
1804
	) {
R
Rob Lourens 已提交
1805
		super(id, label, undefined, true);
1806 1807
	}

J
Johannes Rieken 已提交
1808
	run(): Promise<void> {
1809
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1810
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1811 1812 1813 1814 1815 1816 1817 1818 1819
			.then(viewlet => {
				viewlet.search('@outdated ');
				viewlet.focus();
			});
	}
}

export class ShowPopularExtensionsAction extends Action {

1820
	static readonly ID = 'workbench.extensions.action.showPopularExtensions';
1821
	static readonly LABEL = localize('showPopularExtensions', "Show Popular Extensions");
1822 1823 1824 1825

	constructor(
		id: string,
		label: string,
1826
		@IViewletService private readonly viewletService: IViewletService
1827
	) {
R
Rob Lourens 已提交
1828
		super(id, label, undefined, true);
1829 1830
	}

J
Johannes Rieken 已提交
1831
	run(): Promise<void> {
1832
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1833
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1834
			.then(viewlet => {
S
Sandeep Somavarapu 已提交
1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
				viewlet.search('@popular ');
				viewlet.focus();
			});
	}
}

export class PredefinedExtensionFilterAction extends Action {

	constructor(
		id: string,
		label: string,
		private readonly filter: string,
		@IViewletService private readonly viewletService: IViewletService
	) {
		super(id, label, undefined, true);
	}

	run(): Promise<void> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
			.then(viewlet => {
				viewlet.search(`${this.filter} `);
1857 1858 1859 1860 1861
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884
export class RecentlyPublishedExtensionsAction extends Action {

	static readonly ID = 'workbench.extensions.action.recentlyPublishedExtensions';
	static readonly LABEL = localize('recentlyPublishedExtensions', "Recently Published Extensions");

	constructor(
		id: string,
		label: string,
		@IViewletService private readonly viewletService: IViewletService
	) {
		super(id, label, undefined, true);
	}

	run(): Promise<void> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
			.then(viewlet => {
				viewlet.search('@sort:publishedDate ');
				viewlet.focus();
			});
	}
}

1885 1886
export class ShowRecommendedExtensionsAction extends Action {

1887
	static readonly ID = 'workbench.extensions.action.showRecommendedExtensions';
1888
	static readonly LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions");
1889 1890 1891 1892

	constructor(
		id: string,
		label: string,
1893
		@IViewletService private readonly viewletService: IViewletService
1894
	) {
R
Rob Lourens 已提交
1895
		super(id, label, undefined, true);
1896 1897
	}

J
Johannes Rieken 已提交
1898
	run(): Promise<void> {
1899
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1900
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1901
			.then(viewlet => {
S
Sandeep Somavarapu 已提交
1902
				viewlet.search('@recommended ', true);
1903 1904 1905 1906 1907
				viewlet.focus();
			});
	}
}

1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919
export class ShowRecommendedExtensionAction extends Action {

	static readonly ID = 'workbench.extensions.action.showRecommendedExtension';
	static readonly LABEL = localize('showRecommendedExtension', "Show Recommended Extension");

	private extensionId: string;

	constructor(
		extensionId: string,
		@IViewletService private readonly viewletService: IViewletService,
		@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
	) {
1920
		super(ShowRecommendedExtensionAction.ID, ShowRecommendedExtensionAction.LABEL, undefined, false);
1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941
		this.extensionId = extensionId;
	}

	run(): Promise<any> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
			.then(viewlet => {
				viewlet.search(`@id:${this.extensionId}`);
				viewlet.focus();
				return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None)
					.then(pager => {
						if (pager && pager.firstPage && pager.firstPage.length) {
							const extension = pager.firstPage[0];
							return this.extensionWorkbenchService.open(extension);
						}
						return null;
					});
			});
	}
}

1942
export class InstallRecommendedExtensionAction extends Action {
1943

1944
	static readonly ID = 'workbench.extensions.action.installRecommendedExtension';
1945
	static readonly LABEL = localize('installRecommendedExtension', "Install Recommended Extension");
1946 1947 1948 1949

	private extensionId: string;

	constructor(
1950
		extensionId: string,
1951 1952
		@IViewletService private readonly viewletService: IViewletService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
1953
		@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
1954
	) {
R
Rob Lourens 已提交
1955
		super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, undefined, false);
1956 1957 1958
		this.extensionId = extensionId;
	}

J
Johannes Rieken 已提交
1959
	run(): Promise<any> {
1960
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
1961
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
1962
			.then(viewlet => {
1963
				viewlet.search(`@id:${this.extensionId}`);
1964
				viewlet.focus();
S
Sandeep Somavarapu 已提交
1965
				return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None)
S
Sandeep Somavarapu 已提交
1966 1967
					.then(pager => {
						if (pager && pager.firstPage && pager.firstPage.length) {
1968 1969
							const extension = pager.firstPage[0];
							return this.extensionWorkbenchService.install(extension)
1970
								.then(() => this.extensionWorkbenchService.open(extension))
1971 1972
								.then(() => null, err => {
									console.error(err);
1973
									return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService);
1974
								});
S
Sandeep Somavarapu 已提交
1975
						}
1976
						return null;
S
Sandeep Somavarapu 已提交
1977
					});
1978
			});
1979 1980 1981
	}
}

1982 1983 1984 1985
export class IgnoreExtensionRecommendationAction extends Action {

	static readonly ID = 'extensions.ignore';

S
Sandeep Somavarapu 已提交
1986
	private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} ignore`;
1987 1988

	constructor(
S
Sandeep Somavarapu 已提交
1989
		private readonly extension: IExtension,
1990
		@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
1991
	) {
1992
		super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation');
1993 1994 1995 1996 1997 1998

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

S
Sandeep Somavarapu 已提交
1999
	public run(): Promise<any> {
2000
		this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, true);
2001
		return Promise.resolve();
2002 2003 2004 2005 2006 2007 2008
	}
}

export class UndoIgnoreExtensionRecommendationAction extends Action {

	static readonly ID = 'extensions.ignore';

S
Sandeep Somavarapu 已提交
2009
	private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} undo-ignore`;
2010 2011

	constructor(
S
Sandeep Somavarapu 已提交
2012
		private readonly extension: IExtension,
2013
		@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
2014 2015 2016 2017 2018 2019 2020 2021
	) {
		super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo');

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

S
Sandeep Somavarapu 已提交
2022
	public run(): Promise<any> {
2023
		this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, false);
2024
		return Promise.resolve();
2025 2026 2027
	}
}

2028 2029
export class ShowRecommendedKeymapExtensionsAction extends Action {

2030
	static readonly ID = 'workbench.extensions.action.showRecommendedKeymapExtensions';
2031
	static readonly LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps");
2032 2033 2034 2035

	constructor(
		id: string,
		label: string,
2036
		@IViewletService private readonly viewletService: IViewletService
2037
	) {
R
Rob Lourens 已提交
2038
		super(id, label, undefined, true);
2039 2040
	}

J
Johannes Rieken 已提交
2041
	run(): Promise<void> {
2042
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
2043
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
2044 2045 2046 2047 2048 2049 2050
			.then(viewlet => {
				viewlet.search('@recommended:keymaps ');
				viewlet.focus();
			});
	}
}

2051
export class ShowLanguageExtensionsAction extends Action {
2052

2053
	static readonly ID = 'workbench.extensions.action.showLanguageExtensions';
2054
	static readonly LABEL = localize('showLanguageExtensionsShort', "Language Extensions");
2055 2056 2057 2058

	constructor(
		id: string,
		label: string,
2059
		@IViewletService private readonly viewletService: IViewletService
2060
	) {
R
Rob Lourens 已提交
2061
		super(id, label, undefined, true);
2062 2063
	}

J
Johannes Rieken 已提交
2064
	run(): Promise<void> {
2065
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
2066
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
2067
			.then(viewlet => {
C
Christof Marti 已提交
2068
				viewlet.search('@category:"programming languages" @sort:installs ');
2069 2070 2071 2072 2073
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085
export class SearchCategoryAction extends Action {

	constructor(
		id: string,
		label: string,
		private readonly category: string,
		@IViewletService private readonly viewletService: IViewletService
	) {
		super(id, label, undefined, true);
	}

	run(): Promise<void> {
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102
		return new SearchExtensionsAction(`@category:"${this.category.toLowerCase()}"`, this.viewletService).run();
	}
}

export class SearchExtensionsAction extends Action {

	constructor(
		private readonly searchValue: string,
		@IViewletService private readonly viewletService: IViewletService
	) {
		super('extensions.searchExtensions', localize('search recommendations', "Search Extensions"), undefined, true);
	}

	async run(): Promise<void> {
		const viewPaneContainer = (await this.viewletService.openViewlet(VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer;
		viewPaneContainer.search(this.searchValue);
		viewPaneContainer.focus();
S
Sandeep Somavarapu 已提交
2103 2104 2105
	}
}

2106 2107 2108 2109 2110 2111 2112 2113 2114
export class ChangeSortAction extends Action {

	private query: Query;

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
		private sortBy: string,
2115
		@IViewletService private readonly viewletService: IViewletService
2116
	) {
R
Rob Lourens 已提交
2117
		super(id, label, undefined, true);
2118

J
Joao Moreno 已提交
2119
		if (sortBy === undefined) {
2120 2121 2122 2123 2124
			throw new Error('bad arguments');
		}

		this.query = Query.parse('');
		this.enabled = false;
M
Matt Bierner 已提交
2125
		this._register(onSearchChange(this.onSearchChange, this));
2126 2127 2128 2129
	}

	private onSearchChange(value: string): void {
		const query = Query.parse(value);
2130
		this.query = new Query(query.value, this.sortBy || query.sortBy, query.groupBy);
2131
		this.enabled = !!value && this.query.isValid() && !this.query.equals(query);
2132 2133
	}

J
Johannes Rieken 已提交
2134
	run(): Promise<void> {
2135
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
2136
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
2137 2138 2139 2140 2141 2142 2143
			.then(viewlet => {
				viewlet.search(this.query.toString());
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
2144 2145 2146 2147
export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution {

	private workspaceContextKey = new RawContextKey<boolean>('workspaceRecommendations', true);
	private workspaceFolderContextKey = new RawContextKey<boolean>('workspaceFolderRecommendations', true);
2148 2149
	private addToWorkspaceRecommendationsContextKey = new RawContextKey<boolean>('addToWorkspaceRecommendations', false);
	private addToWorkspaceFolderRecommendationsContextKey = new RawContextKey<boolean>('addToWorkspaceFolderRecommendations', false);
S
Sandeep Somavarapu 已提交
2150 2151 2152

	constructor(
		@IContextKeyService contextKeyService: IContextKeyService,
2153 2154
		@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService
S
Sandeep Somavarapu 已提交
2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
	) {
		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)));

2165 2166 2167 2168 2169 2170 2171 2172 2173 2174
		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)));
2175

S
Sandeep Somavarapu 已提交
2176 2177 2178 2179 2180 2181 2182 2183 2184 2185
		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,
2186
				title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' },
2187
				category: localize('extensions', "Extensions")
S
Sandeep Somavarapu 已提交
2188 2189 2190 2191 2192 2193 2194 2195 2196 2197
			},
			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,
2198
				title: { value: ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace Folder)' },
2199
				category: localize('extensions', "Extensions")
S
Sandeep Somavarapu 已提交
2200 2201 2202
			},
			when: this.workspaceFolderContextKey
		});
2203

2204 2205 2206 2207
		CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.ADD_ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService)
				.createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.ADD_ID, AddToWorkspaceRecommendationsAction.ADD_LABEL)
				.run(AddToWorkspaceRecommendationsAction.ADD);
2208 2209 2210
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
2211
				id: AddToWorkspaceRecommendationsAction.ADD_ID,
2212
				title: { value: AddToWorkspaceRecommendationsAction.ADD_LABEL, original: 'Add to Recommended Extensions (Workspace)' },
2213
				category: localize('extensions', "Extensions")
2214
			},
2215
			when: this.addToWorkspaceRecommendationsContextKey
2216 2217
		});

2218 2219 2220 2221 2222 2223 2224 2225
		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,
2226
				title: { value: AddToWorkspaceFolderRecommendationsAction.ADD_LABEL, original: 'Extensions: Add to Recommended Extensions (Workspace Folder)' },
2227
				category: localize('extensions', "Extensions")
2228 2229 2230
			},
			when: this.addToWorkspaceFolderRecommendationsContextKey
		});
S
Sandeep Somavarapu 已提交
2231

2232 2233 2234 2235 2236 2237 2238 2239
		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,
2240
				title: { value: AddToWorkspaceRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace)' },
2241
				category: localize('extensions', "Extensions")
2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253
			},
			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,
2254
				title: { value: AddToWorkspaceFolderRecommendationsAction.IGNORE_LABEL, original: 'Extensions: Ignore Recommended Extension (Workspace Folder)' },
2255
				category: localize('extensions', "Extensions")
2256 2257 2258 2259
			},
			when: this.addToWorkspaceFolderRecommendationsContextKey
		});
	}
2260 2261 2262 2263 2264 2265 2266 2267
}

export abstract class AbstractConfigureRecommendedExtensionsAction extends Action {

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService protected contextService: IWorkspaceContextService,
2268
		@IFileService private readonly fileService: IFileService,
2269
		@ITextFileService private readonly textFileService: ITextFileService,
2270
		@IEditorService protected editorService: IEditorService,
2271 2272
		@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
		@ITextModelService private readonly textModelResolverService: ITextModelService
2273
	) {
2274
		super(id, label);
2275 2276
	}

S
Sandeep Somavarapu 已提交
2277
	protected openExtensionsFile(extensionsFileResource: URI): Promise<any> {
2278
		return this.getOrCreateExtensionsFile(extensionsFileResource)
S
Sandeep Somavarapu 已提交
2279 2280 2281 2282 2283 2284 2285 2286 2287
			.then(({ created, content }) =>
				this.getSelectionPosition(content, extensionsFileResource, ['recommendations'])
					.then(selection => this.editorService.openEditor({
						resource: extensionsFileResource,
						options: {
							pinned: created,
							selection
						}
					})),
S
Sandeep Somavarapu 已提交
2288
				error => Promise.reject(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error))));
2289 2290
	}

S
Sandeep Somavarapu 已提交
2291
	protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {
2292
		return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
B
Benjamin Pasero 已提交
2293
			.then(content => this.getSelectionPosition(content.value.toString(), content.resource, ['extensions', 'recommendations']))
2294 2295 2296
			.then(selection => this.editorService.openEditor({
				resource: workspaceConfigurationFile,
				options: {
B
Benjamin Pasero 已提交
2297 2298
					selection,
					forceReload: true // because content has changed
2299 2300 2301 2302
				}
			}));
	}

2303 2304 2305 2306
	protected addExtensionToWorkspaceConfig(workspaceConfigurationFile: URI, extensionId: string, shouldRecommend: boolean) {
		return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
			.then(content => {
				const extensionIdLowerCase = extensionId.toLowerCase();
B
Benjamin Pasero 已提交
2307
				const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {};
2308 2309 2310 2311
				let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || [];
				let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || [];

				if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) {
S
Sandeep Somavarapu 已提交
2312
					return Promise.resolve(null);
2313 2314 2315 2316 2317 2318
				}

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

				return this.jsonEditingService.write(workspaceConfigurationFile,
2319
					[{
S
Sandeep Somavarapu 已提交
2320
						path: ['extensions'],
2321 2322 2323 2324
						value: {
							recommendations: shouldRecommend ? insertInto : removeFrom,
							unwantedRecommendations: shouldRecommend ? removeFrom : insertInto
						}
2325
					}],
2326 2327 2328 2329
					true);
			});
	}

S
Sandeep Somavarapu 已提交
2330
	protected addExtensionToWorkspaceFolderConfig(extensionsFileResource: URI, extensionId: string, shouldRecommend: boolean): Promise<any> {
2331 2332 2333
		return this.getOrCreateExtensionsFile(extensionsFileResource)
			.then(({ content }) => {
				const extensionIdLowerCase = extensionId.toLowerCase();
2334 2335 2336
				const extensionsConfigContent: IExtensionsConfigContent = json.parse(content) || {};
				let insertInto = shouldRecommend ? extensionsConfigContent.recommendations || [] : extensionsConfigContent.unwantedRecommendations || [];
				let removeFrom = shouldRecommend ? extensionsConfigContent.unwantedRecommendations || [] : extensionsConfigContent.recommendations || [];
2337

2338
				if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) {
S
Sandeep Somavarapu 已提交
2339
					return Promise.resolve(null);
2340 2341
				}

2342 2343
				insertInto.push(extensionId);

2344
				let removeFromPromise: Promise<void> = Promise.resolve();
2345 2346 2347
				if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) {
					removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase);
					removeFromPromise = this.jsonEditingService.write(extensionsFileResource,
2348
						[{
S
Sandeep Somavarapu 已提交
2349
							path: shouldRecommend ? ['unwantedRecommendations'] : ['recommendations'],
2350
							value: removeFrom
2351
						}],
2352 2353 2354
						true);
				}

2355
				return removeFromPromise.then(() =>
2356
					this.jsonEditingService.write(extensionsFileResource,
2357
						[{
S
Sandeep Somavarapu 已提交
2358
							path: shouldRecommend ? ['recommendations'] : ['unwantedRecommendations'],
2359
							value: insertInto
2360
						}],
2361 2362 2363 2364 2365
						true)
				);
			});
	}

S
Sandeep Somavarapu 已提交
2366
	protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
B
Benjamin Pasero 已提交
2367
		return Promise.resolve(this.fileService.readFile(extensionsFileResource))
2368
			.then(content => {
B
Benjamin Pasero 已提交
2369
				return (json.parse(content.value.toString()) || {})['extensions'] || {};
2370 2371 2372
			}, err => ({ recommendations: [], unwantedRecommendations: [] }));
	}

S
Sandeep Somavarapu 已提交
2373
	protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
B
Benjamin Pasero 已提交
2374
		return Promise.resolve(this.fileService.readFile(extensionsFileResource))
2375
			.then(content => {
2376
				return (<IExtensionsConfigContent>json.parse(content.value.toString()) || {});
2377
			}, err => ({ recommendations: [], unwantedRecommendations: [] }));
2378 2379
	}

B
Benjamin Pasero 已提交
2380 2381
	private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IFileContent> {
		return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile))
2382
			.then(content => {
B
Benjamin Pasero 已提交
2383
				const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value.toString())['extensions'];
2384
				if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {
S
Sandeep Somavarapu 已提交
2385
					return this.jsonEditingService.write(workspaceConfigurationFile, [{ path: ['extensions'], value: { recommendations: [] } }], true)
B
Benjamin Pasero 已提交
2386
						.then(() => this.fileService.readFile(workspaceConfigurationFile));
2387 2388 2389 2390 2391
				}
				return content;
			});
	}

J
Johannes Rieken 已提交
2392
	private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): Promise<ITextEditorSelection | undefined> {
S
Sandeep Somavarapu 已提交
2393 2394
		const tree = json.parseTree(content);
		const node = json.findNodeAtLocation(tree, path);
2395
		if (node && node.parent && node.parent.children) {
S
Sandeep Somavarapu 已提交
2396 2397 2398
			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 已提交
2399
			return Promise.resolve(this.textModelResolverService.createModelReference(resource))
2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410
				.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 已提交
2411
		return Promise.resolve(undefined);
2412 2413
	}

S
Sandeep Somavarapu 已提交
2414
	private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> {
B
Benjamin Pasero 已提交
2415 2416
		return Promise.resolve(this.fileService.readFile(extensionsFileResource)).then(content => {
			return { created: false, extensionsFileResource, content: content.value.toString() };
2417
		}, err => {
B
Benjamin Pasero 已提交
2418
			return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
S
Sandeep Somavarapu 已提交
2419
				return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
2420 2421 2422 2423 2424 2425
			});
		});
	}
}

export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {
2426

2427
	static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';
2428
	static readonly LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)");
2429 2430 2431 2432

	constructor(
		id: string,
		label: string,
2433
		@IFileService fileService: IFileService,
2434
		@ITextFileService textFileService: ITextFileService,
2435
		@IWorkspaceContextService contextService: IWorkspaceContextService,
2436
		@IEditorService editorService: IEditorService,
2437 2438
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService
2439
	) {
2440
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
M
Matt Bierner 已提交
2441
		this._register(this.contextService.onDidChangeWorkbenchState(() => this.update(), this));
2442 2443 2444 2445 2446
		this.update();
	}

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

2449
	public run(): Promise<void> {
2450 2451
		switch (this.contextService.getWorkbenchState()) {
			case WorkbenchState.FOLDER:
2452
				return this.openExtensionsFile(this.contextService.getWorkspace().folders[0].toResource(EXTENSIONS_CONFIG));
2453
			case WorkbenchState.WORKSPACE:
2454
				return this.openWorkspaceConfigurationFile(this.contextService.getWorkspace().configuration!);
2455
		}
2456
		return Promise.resolve();
2457
	}
2458
}
2459

2460 2461
export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {

2462
	static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';
2463
	static readonly LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)");
2464 2465 2466 2467 2468

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
2469
		@ITextFileService textFileService: ITextFileService,
2470
		@IWorkspaceContextService contextService: IWorkspaceContextService,
2471
		@IEditorService editorService: IEditorService,
2472
		@IJSONEditingService jsonEditingService: IJSONEditingService,
S
Sandeep Somavarapu 已提交
2473
		@ITextModelService textModelResolverService: ITextModelService,
2474
		@ICommandService private readonly commandService: ICommandService
2475
	) {
2476
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
M
Matt Bierner 已提交
2477
		this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update(), this));
2478
		this.update();
2479 2480
	}

2481 2482 2483
	private update(): void {
		this.enabled = this.contextService.getWorkspace().folders.length > 0;
	}
2484

S
Sandeep Somavarapu 已提交
2485
	public run(): Promise<any> {
2486
		const folderCount = this.contextService.getWorkspace().folders.length;
S
Sandeep Somavarapu 已提交
2487 2488
		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 已提交
2489 2490
			.then(workspaceFolder => {
				if (workspaceFolder) {
2491
					return this.openExtensionsFile(workspaceFolder.toResource(EXTENSIONS_CONFIG));
2492
				}
S
Sandeep Somavarapu 已提交
2493
				return null;
2494
			});
2495 2496 2497
	}
}

2498 2499 2500 2501 2502 2503 2504
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)");
2505 2506 2507 2508 2509

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
2510
		@ITextFileService textFileService: ITextFileService,
2511 2512 2513 2514
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService,
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService,
2515 2516
		@ICommandService private readonly commandService: ICommandService,
		@INotificationService private readonly notificationService: INotificationService
2517
	) {
2518
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
2519 2520
	}

S
Sandeep Somavarapu 已提交
2521
	run(shouldRecommend: boolean): Promise<void> {
2522
		if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension) {
2523
			return Promise.resolve();
2524 2525 2526
		}
		const folders = this.contextService.getWorkspace().folders;
		if (!folders || !folders.length) {
2527
			this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.noWorkspace', 'There are no workspace folders open to add recommendations.'));
2528
			return Promise.resolve();
2529 2530
		}

S
Sandeep Somavarapu 已提交
2531
		const extensionId = this.editorService.activeEditor.extension.identifier;
2532
		const pickFolderPromise = folders.length === 1
S
Sandeep Somavarapu 已提交
2533
			? Promise.resolve(folders[0])
2534
			: this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID);
S
Sandeep Somavarapu 已提交
2535
		return Promise.resolve(pickFolderPromise)
2536 2537
			.then(workspaceFolder => {
				if (!workspaceFolder) {
2538
					return Promise.resolve();
2539
				}
2540
				const configurationFile = workspaceFolder.toResource(EXTENSIONS_CONFIG);
2541
				return this.getWorkspaceFolderExtensionsConfigContent(configurationFile).then(content => {
S
Sandeep Somavarapu 已提交
2542
					const extensionIdLowerCase = extensionId.id.toLowerCase();
2543
					if (shouldRecommend) {
2544
						if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
2545
							this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s recommendations.'));
2546
							return Promise.resolve();
2547 2548
						}

S
Sandeep Somavarapu 已提交
2549
						return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => {
2550 2551 2552 2553 2554 2555
							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)
								}]);
2556 2557 2558
						}, err => {
							this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
						});
2559
					}
2560
					else {
2561
						if ((content.unwantedRecommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
2562
							this.notificationService.info(localize('AddToWorkspaceFolderIgnoredRecommendations.alreadyExists', 'This extension is already present in this workspace folder\'s unwanted recommendations.'));
2563
							return Promise.resolve();
2564
						}
2565

S
Sandeep Somavarapu 已提交
2566
						return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => {
2567 2568 2569 2570 2571 2572
							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)
								}]);
2573 2574 2575 2576
						}, err => {
							this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
						});
					}
2577 2578 2579 2580 2581
				});
			});
	}
}

2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593
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,
2594
		@ITextFileService textFileService: ITextFileService,
2595 2596 2597 2598
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService,
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService,
2599
		@INotificationService private readonly notificationService: INotificationService
2600
	) {
2601
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
2602 2603
	}

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

2607 2608 2609 2610
		if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension || !workspaceConfig) {
			return Promise.resolve();
		}

S
Sandeep Somavarapu 已提交
2611
		const extensionId = this.editorService.activeEditor.extension.identifier;
2612 2613

		return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => {
S
Sandeep Somavarapu 已提交
2614
			const extensionIdLowerCase = extensionId.id.toLowerCase();
2615 2616 2617
			if (shouldRecommend) {
				if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
					this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.'));
2618
					return Promise.resolve();
2619 2620
				}

S
Sandeep Somavarapu 已提交
2621
				return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => {
2622 2623 2624 2625 2626 2627 2628
					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)
						}]);

2629 2630 2631 2632 2633 2634
				}, 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.'));
2635
					return Promise.resolve();
2636 2637
				}

S
Sandeep Somavarapu 已提交
2638
				return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => {
2639 2640 2641 2642 2643 2644
					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)
						}]);
2645 2646 2647 2648 2649 2650 2651 2652
				}, err => {
					this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err));
				});
			}
		});
	}
}

S
#66931  
Sandeep Somavarapu 已提交
2653 2654
export class StatusLabelAction extends Action implements IExtensionContainer {

S
Sandeep Somavarapu 已提交
2655
	private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`;
S
#66931  
Sandeep Somavarapu 已提交
2656 2657
	private static readonly DISABLED_CLASS = `${StatusLabelAction.ENABLED_CLASS} hide`;

S
Sandeep Somavarapu 已提交
2658
	private initialStatus: ExtensionState | null = null;
S
#66931  
Sandeep Somavarapu 已提交
2659 2660 2661
	private status: ExtensionState | null = null;
	private enablementState: EnablementState | null = null;

S
Sandeep Somavarapu 已提交
2662 2663 2664
	private _extension: IExtension | null = null;
	get extension(): IExtension | null { return this._extension; }
	set extension(extension: IExtension | null) {
S
Sandeep Somavarapu 已提交
2665
		if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) {
S
#66931  
Sandeep Somavarapu 已提交
2666
			// Different extension. Reset
S
Sandeep Somavarapu 已提交
2667
			this.initialStatus = null;
S
#66931  
Sandeep Somavarapu 已提交
2668 2669 2670 2671 2672 2673 2674 2675
			this.status = null;
			this.enablementState = null;
		}
		this._extension = extension;
		this.update();
	}

	constructor(
2676 2677
		@IExtensionService private readonly extensionService: IExtensionService,
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
S
#66931  
Sandeep Somavarapu 已提交
2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697
	) {
		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;
		this.status = this.extension.state;
S
Sandeep Somavarapu 已提交
2698 2699 2700
		if (this.initialStatus === null) {
			this.initialStatus = this.status;
		}
S
#66931  
Sandeep Somavarapu 已提交
2701 2702 2703
		this.enablementState = this.extension.enablementState;

		const runningExtensions = await this.extensionService.getExtensions();
S
Sandeep Somavarapu 已提交
2704
		const canAddExtension = () => {
S
Sandeep Somavarapu 已提交
2705 2706 2707
			const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];
			if (this.extension!.local) {
				if (runningExtension && this.extension!.version === runningExtension.version) {
S
Sandeep Somavarapu 已提交
2708 2709
					return true;
				}
S
Sandeep Somavarapu 已提交
2710
				return this.extensionService.canAddExtension(toExtensionDescription(this.extension!.local));
S
Sandeep Somavarapu 已提交
2711 2712 2713 2714
			}
			return false;
		};
		const canRemoveExtension = () => {
S
Sandeep Somavarapu 已提交
2715
			if (this.extension!.local) {
2716
				if (runningExtensions.every(e => !(areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.extension!.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(e))))) {
S
Sandeep Somavarapu 已提交
2717 2718
					return true;
				}
S
Sandeep Somavarapu 已提交
2719
				return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension!.local));
S
Sandeep Somavarapu 已提交
2720 2721 2722
			}
			return false;
		};
S
#66931  
Sandeep Somavarapu 已提交
2723 2724 2725

		if (currentStatus !== null) {
			if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) {
S
Sandeep Somavarapu 已提交
2726
				return canAddExtension() ? this.initialStatus === ExtensionState.Installed ? localize('updated', "Updated") : localize('installed', "Installed") : null;
S
#66931  
Sandeep Somavarapu 已提交
2727 2728
			}
			if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {
S
Sandeep Somavarapu 已提交
2729
				this.initialStatus = this.status;
S
#66931  
Sandeep Somavarapu 已提交
2730 2731 2732 2733 2734
				return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;
			}
		}

		if (currentEnablementState !== null) {
2735 2736
			const currentlyEnabled = currentEnablementState === EnablementState.EnabledGlobally || currentEnablementState === EnablementState.EnabledWorkspace;
			const enabled = this.enablementState === EnablementState.EnabledGlobally || this.enablementState === EnablementState.EnabledWorkspace;
S
#66931  
Sandeep Somavarapu 已提交
2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749
			if (!currentlyEnabled && enabled) {
				return canAddExtension() ? localize('enabled', "Enabled") : null;
			}
			if (currentlyEnabled && !enabled) {
				return canRemoveExtension() ? localize('disabled', "Disabled") : null;
			}

		}

		return null;
	}

	run(): Promise<any> {
2750
		return Promise.resolve();
S
#66931  
Sandeep Somavarapu 已提交
2751 2752 2753 2754
	}

}

S
Sandeep Somavarapu 已提交
2755
export class MaliciousStatusLabelAction extends ExtensionAction {
J
Joao Moreno 已提交
2756

S
Sandeep Somavarapu 已提交
2757
	private static readonly Class = `${ExtensionAction.TEXT_ACTION_CLASS} malicious-status`;
J
Joao Moreno 已提交
2758 2759

	constructor(long: boolean) {
J
Joao Moreno 已提交
2760
		const tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
2761
		const label = long ? tooltip : localize({ key: 'malicious', comment: ['Refers to a malicious extension'] }, "Malicious");
J
Joao Moreno 已提交
2762
		super('extensions.install', label, '', false);
J
Joao Moreno 已提交
2763
		this.tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
J
Joao Moreno 已提交
2764 2765
	}

S
Sandeep Somavarapu 已提交
2766
	update(): void {
J
Joao Moreno 已提交
2767 2768 2769 2770 2771 2772 2773
		if (this.extension && this.extension.isMalicious) {
			this.class = `${MaliciousStatusLabelAction.Class} malicious`;
		} else {
			this.class = `${MaliciousStatusLabelAction.Class} not-malicious`;
		}
	}

S
Sandeep Somavarapu 已提交
2774
	run(): Promise<any> {
2775
		return Promise.resolve();
J
Joao Moreno 已提交
2776 2777 2778
	}
}

S
Sandeep Somavarapu 已提交
2779
export class ToggleSyncExtensionAction extends ExtensionDropDownAction {
S
Sandeep Somavarapu 已提交
2780

S
Sandeep Somavarapu 已提交
2781 2782
	private static readonly IGNORED_SYNC_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-sync codicon-sync-ignored`;
	private static readonly SYNC_CLASS = `${ToggleSyncExtensionAction.ICON_ACTION_CLASS} extension-sync codicon-sync`;
S
Sandeep Somavarapu 已提交
2783 2784

	constructor(
2785 2786
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
2787 2788
		@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
		@IInstantiationService instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
2789
	) {
S
Sandeep Somavarapu 已提交
2790
		super('extensions.sync', '', ToggleSyncExtensionAction.SYNC_CLASS, false, true, instantiationService);
S
Sandeep Somavarapu 已提交
2791
		this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.update()));
S
Sandeep Somavarapu 已提交
2792
		this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.update()));
S
Sandeep Somavarapu 已提交
2793 2794 2795 2796
		this.update();
	}

	update(): void {
S
Sandeep Somavarapu 已提交
2797 2798 2799 2800 2801
		this.enabled = !!this.extension && this.userDataAutoSyncEnablementService.isEnabled();
		if (this.extension) {
			const isIgnored = this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);
			this.class = isIgnored ? ToggleSyncExtensionAction.IGNORED_SYNC_CLASS : ToggleSyncExtensionAction.SYNC_CLASS;
			this.tooltip = isIgnored ? localize('ignored', "This extension is ignored during sync") : localize('synced', "This extension is synced");
2802
		}
S
Sandeep Somavarapu 已提交
2803 2804
	}

S
Sandeep Somavarapu 已提交
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815
	async run(): Promise<any> {
		return super.run({
			actionGroups: [
				[
					new Action(
						'extensions.syncignore',
						this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension!) ? localize('sync', "Sync this extension") : localize('do not sync', "Do not sync this extension")
						, undefined, true, () => this.extensionsWorkbenchService.toggleExtensionIgnoredToSync(this.extension!))
				]
			], disposeActionsOnHide: true
		});
S
Sandeep Somavarapu 已提交
2816 2817 2818
	}
}

S
Sandeep Somavarapu 已提交
2819
export class ExtensionToolTipAction extends ExtensionAction {
2820

S
Sandeep Somavarapu 已提交
2821
	private static readonly Class = `${ExtensionAction.TEXT_ACTION_CLASS} disable-status`;
2822 2823

	updateWhenCounterExtensionChanges: boolean = true;
2824
	private _runningExtensions: IExtensionDescription[] | null = null;
2825

2826 2827
	constructor(
		private readonly warningAction: SystemDisabledWarningAction,
S
Sandeep Somavarapu 已提交
2828
		private readonly reloadAction: ReloadAction,
S
rename  
Sandeep Somavarapu 已提交
2829
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
2830
		@IExtensionService private readonly extensionService: IExtensionService,
S
Sandeep Somavarapu 已提交
2831
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
2832
	) {
S
Sandeep Somavarapu 已提交
2833
		super('extensions.tooltip', warningAction.tooltip, `${ExtensionToolTipAction.Class} hide`, false);
M
Matt Bierner 已提交
2834 2835
		this._register(warningAction.onDidChange(() => this.update(), this));
		this._register(this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this));
2836 2837 2838 2839 2840
		this.updateRunningExtensions();
	}

	private updateRunningExtensions(): void {
		this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
2841 2842 2843
	}

	update(): void {
S
Sandeep Somavarapu 已提交
2844 2845 2846 2847
		this.label = this.getTooltip();
		this.class = ExtensionToolTipAction.Class;
		if (!this.label) {
			this.class = `${ExtensionToolTipAction.Class} hide`;
2848
		}
S
Sandeep Somavarapu 已提交
2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859
	}

	private getTooltip(): string {
		if (!this.extension) {
			return '';
		}
		if (this.reloadAction.enabled) {
			return this.reloadAction.tooltip;
		}
		if (this.warningAction.tooltip) {
			return this.warningAction.tooltip;
2860
		}
S
Sandeep Somavarapu 已提交
2861
		if (this.extension && this.extension.local && this.extension.state === ExtensionState.Installed && this._runningExtensions) {
S
Sandeep Somavarapu 已提交
2862
			const isRunning = this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier));
2863
			const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
S
Sandeep Somavarapu 已提交
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885

			if (isEnabled && isRunning) {
				if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
					if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
						return localize('extension enabled on remote', "Extension is enabled on '{0}'", this.extension.server.label);
					}
				}
				if (this.extension.enablementState === EnablementState.EnabledGlobally) {
					return localize('globally enabled', "This extension is enabled globally.");
				}
				if (this.extension.enablementState === EnablementState.EnabledWorkspace) {
					return localize('workspace enabled', "This extension is enabled for this workspace by the user.");
				}
			}

			if (!isEnabled && !isRunning) {
				if (this.extension.enablementState === EnablementState.DisabledGlobally) {
					return localize('globally disabled', "This extension is disabled globally by the user.");
				}
				if (this.extension.enablementState === EnablementState.DisabledWorkspace) {
					return localize('workspace disabled', "This extension is disabled for this workspace by the user.");
				}
2886
			}
2887
		}
S
Sandeep Somavarapu 已提交
2888
		return '';
2889 2890 2891 2892 2893 2894 2895 2896 2897
	}

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

export class SystemDisabledWarningAction extends ExtensionAction {

S
Sandeep Somavarapu 已提交
2898
	private static readonly CLASS = `${ExtensionAction.ICON_ACTION_CLASS} system-disable`;
2899 2900
	private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.warning.classNames}`;
	private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.info.classNames}`;
2901 2902

	updateWhenCounterExtensionChanges: boolean = true;
2903
	private _runningExtensions: IExtensionDescription[] | null = null;
2904

2905 2906 2907
	constructor(
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
		@ILabelService private readonly labelService: ILabelService,
2908
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
2909 2910 2911
		@IExtensionService private readonly extensionService: IExtensionService,
		@IProductService private readonly productService: IProductService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
2912
	) {
2913
		super('extensions.install', '', `${SystemDisabledWarningAction.CLASS} hide`, false);
M
Matt Bierner 已提交
2914 2915
		this._register(this.labelService.onDidChangeFormatters(() => this.update(), this));
		this._register(this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this));
2916
		this.updateRunningExtensions();
2917 2918 2919
		this.update();
	}

2920 2921 2922 2923
	private updateRunningExtensions(): void {
		this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
	}

2924
	update(): void {
2925
		this.class = `${SystemDisabledWarningAction.CLASS} hide`;
2926
		this.tooltip = '';
2927 2928 2929 2930 2931
		if (
			!this.extension ||
			!this.extension.local ||
			!this.extension.server ||
			!this._runningExtensions ||
2932
			this.extension.state !== ExtensionState.Installed
2933
		) {
2934 2935
			return;
		}
2936 2937 2938 2939 2940
		if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
			if (isLanguagePackExtension(this.extension.local.manifest)) {
				if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {
					this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
					this.tooltip = this.extension.server === this.extensionManagementServerService.localExtensionManagementServer
D
Daniel Imms 已提交
2941 2942
						? localize('Install language pack also in remote server', "Install the language pack extension on '{0}' to enable it there also.", this.extensionManagementServerService.remoteExtensionManagementServer.label)
						: localize('Install language pack also locally', "Install the language pack extension locally to enable it there also.");
2943 2944
				}
				return;
2945 2946
			}
		}
S
Sandeep Somavarapu 已提交
2947
		if (this.extension.enablementState === EnablementState.DisabledByExtensionKind) {
S
Sandeep Somavarapu 已提交
2948
			if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {
S
Sandeep Somavarapu 已提交
2949
				const server = this.extensionManagementServerService.localExtensionManagementServer === this.extension.server ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer;
2950
				this.class = `${SystemDisabledWarningAction.WARNING_CLASS}`;
2951 2952 2953
				if (server) {
					this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label);
				} else {
S
Sandeep Somavarapu 已提交
2954
					this.tooltip = localize('disabled because of extension kind', "This extension has defined that it cannot run on the remote server");
2955
				}
S
Sandeep Somavarapu 已提交
2956 2957
				return;
			}
2958
		}
2959 2960
		if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
			const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0];
2961
			const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)) : null;
2962 2963 2964 2965 2966 2967
			if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
				if (prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) {
					this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
					this.tooltip = localize('disabled locally', "Extension is enabled on '{0}' and disabled locally.", this.extensionManagementServerService.remoteExtensionManagementServer.label);
				}
				return;
2968
			}
2969 2970 2971 2972 2973 2974
			if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
				if (prefersExecuteOnUI(this.extension.local!.manifest, this.productService, this.configurationService)) {
					this.class = `${SystemDisabledWarningAction.INFO_CLASS}`;
					this.tooltip = localize('disabled remotely', "Extension is enabled locally and disabled on '{0}'.", this.extensionManagementServerService.remoteExtensionManagementServer.label);
				}
				return;
2975
			}
2976 2977 2978 2979 2980 2981 2982 2983
		}
	}

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

2984 2985
export class DisableAllAction extends Action {

2986
	static readonly ID = 'workbench.extensions.action.disableAll';
2987
	static readonly LABEL = localize('disableAll', "Disable All Installed Extensions");
2988 2989

	constructor(
S
Sandeep Somavarapu 已提交
2990
		id: string, label: string, isPrimary: boolean,
2991
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
2992
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
2993 2994
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
2995 2996 2997
		if (isPrimary) {
			this._register(this.extensionsWorkbenchService.onChange(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
2998 2999
	}

S
Sandeep Somavarapu 已提交
3000
	private getExtensionsToDisable(): IExtension[] {
3001
		return this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local));
S
Sandeep Somavarapu 已提交
3002 3003
	}

S
Sandeep Somavarapu 已提交
3004 3005
	get enabled(): boolean {
		return this.getExtensionsToDisable().length > 0;
3006 3007
	}

S
Sandeep Somavarapu 已提交
3008
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3009
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToDisable(), EnablementState.DisabledGlobally);
3010 3011 3012
	}
}

3013
export class DisableAllWorkspaceAction extends Action {
3014

3015
	static readonly ID = 'workbench.extensions.action.disableAllWorkspace';
3016
	static readonly LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace");
3017 3018

	constructor(
S
Sandeep Somavarapu 已提交
3019
		id: string, label: string, isPrimary: boolean,
3020
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
3021
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
3022
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
3023 3024
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
3025 3026 3027
		if (isPrimary) {
			this._register(Event.any(this.workspaceContextService.onDidChangeWorkbenchState, this.extensionsWorkbenchService.onChange)(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
3028 3029
	}

S
Sandeep Somavarapu 已提交
3030
	private getExtensionsToDisable(): IExtension[] {
3031
		return this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && !!e.local && this.extensionEnablementService.isEnabled(e.local) && this.extensionEnablementService.canChangeEnablement(e.local));
S
Sandeep Somavarapu 已提交
3032 3033
	}

S
Sandeep Somavarapu 已提交
3034 3035
	get enabled(): boolean {
		return this.getExtensionsToDisable().length > 0;
3036 3037
	}

S
Sandeep Somavarapu 已提交
3038
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3039
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToDisable(), EnablementState.DisabledWorkspace);
3040 3041 3042 3043 3044
	}
}

export class EnableAllAction extends Action {

3045
	static readonly ID = 'workbench.extensions.action.enableAll';
3046
	static readonly LABEL = localize('enableAll', "Enable All Extensions");
3047 3048

	constructor(
S
Sandeep Somavarapu 已提交
3049
		id: string, label: string, isPrimary: boolean,
3050
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
3051
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
3052 3053
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
3054 3055 3056
		if (isPrimary) {
			this._register(this.extensionsWorkbenchService.onChange(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
3057 3058
	}

S
Sandeep Somavarapu 已提交
3059 3060 3061 3062
	private getExtensionsToEnable(): IExtension[] {
		return this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local));
	}

S
Sandeep Somavarapu 已提交
3063 3064
	get enabled(): boolean {
		return this.getExtensionsToEnable().length > 0;
3065 3066
	}

S
Sandeep Somavarapu 已提交
3067
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3068
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToEnable(), EnablementState.EnabledGlobally);
3069 3070 3071
	}
}

3072
export class EnableAllWorkspaceAction extends Action {
3073

3074
	static readonly ID = 'workbench.extensions.action.enableAllWorkspace';
3075
	static readonly LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace");
3076 3077

	constructor(
S
Sandeep Somavarapu 已提交
3078
		id: string, label: string, isPrimary: boolean,
3079 3080
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
3081
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
3082 3083
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
3084 3085 3086
		if (isPrimary) {
			this._register(Event.any(this.workspaceContextService.onDidChangeWorkbenchState, this.extensionsWorkbenchService.onChange)(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
3087 3088
	}

S
Sandeep Somavarapu 已提交
3089 3090 3091 3092
	private getExtensionsToEnable(): IExtension[] {
		return this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local));
	}

S
Sandeep Somavarapu 已提交
3093 3094
	get enabled(): boolean {
		return this.getExtensionsToEnable().length > 0;
3095 3096
	}

S
Sandeep Somavarapu 已提交
3097
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3098
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToEnable(), EnablementState.EnabledWorkspace);
3099
	}
3100 3101
}

J
Joao Moreno 已提交
3102 3103 3104
export class InstallVSIXAction extends Action {

	static readonly ID = 'workbench.extensions.action.installVSIX';
3105
	static readonly LABEL = localize('installVSIX', "Install from VSIX...");
J
Joao Moreno 已提交
3106 3107 3108 3109

	constructor(
		id = InstallVSIXAction.ID,
		label = InstallVSIXAction.LABEL,
3110
		@IFileDialogService private readonly fileDialogService: IFileDialogService,
3111
		@ICommandService private readonly commandService: ICommandService
J
Joao Moreno 已提交
3112
	) {
S
Sandeep Somavarapu 已提交
3113
		super(id, label, 'extension-action install-vsix', true);
J
Joao Moreno 已提交
3114 3115
	}

3116 3117 3118 3119 3120 3121 3122
	async run(): Promise<void> {
		const vsixPaths = await this.fileDialogService.showOpenDialog({
			title: localize('installFromVSIX', "Install from VSIX"),
			filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }],
			canSelectFiles: true,
			openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install"))
		});
3123

3124 3125
		if (!vsixPaths) {
			return;
3126
		}
J
Joao Moreno 已提交
3127

3128
		// Install extension(s), display notification(s), display @installed extensions
3129
		await this.commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths);
J
Joao Moreno 已提交
3130 3131 3132 3133 3134 3135
	}
}

export class ReinstallAction extends Action {

	static readonly ID = 'workbench.extensions.action.reinstall';
3136
	static readonly LABEL = localize('reinstall', "Reinstall Extension...");
J
Joao Moreno 已提交
3137 3138 3139

	constructor(
		id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL,
3140 3141 3142
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
3143
		@IHostService private readonly hostService: IHostService,
S
Sandeep Somavarapu 已提交
3144 3145
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IExtensionService private readonly extensionService: IExtensionService
J
Joao Moreno 已提交
3146 3147 3148 3149 3150
	) {
		super(id, label);
	}

	get enabled(): boolean {
3151
		return this.extensionsWorkbenchService.local.filter(l => !l.isBuiltin && l.local).length > 0;
J
Joao Moreno 已提交
3152 3153
	}

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

J
Johannes Rieken 已提交
3159
	private getEntries(): Promise<(IQuickPickItem & { extension: IExtension })[]> {
J
Joao Moreno 已提交
3160 3161
		return this.extensionsWorkbenchService.queryLocal()
			.then(local => {
C
Christof Marti 已提交
3162
				const entries = local
3163
					.filter(extension => !extension.isBuiltin)
J
Joao Moreno 已提交
3164
					.map(extension => {
C
Christof Marti 已提交
3165
						return {
S
Sandeep Somavarapu 已提交
3166
							id: extension.identifier.id,
J
Joao Moreno 已提交
3167
							label: extension.displayName,
S
Sandeep Somavarapu 已提交
3168
							description: extension.identifier.id,
C
Christof Marti 已提交
3169 3170
							extension,
						} as (IQuickPickItem & { extension: IExtension });
J
Joao Moreno 已提交
3171 3172 3173 3174 3175
					});
				return entries;
			});
	}

J
Johannes Rieken 已提交
3176
	private reinstallExtension(extension: IExtension): Promise<void> {
S
Sandeep Somavarapu 已提交
3177 3178
		return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run()
			.then(() => {
3179
				return this.extensionsWorkbenchService.reinstall(extension)
S
Sandeep Somavarapu 已提交
3180 3181 3182 3183 3184 3185
					.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"),
3186
							run: () => this.hostService.reload()
S
Sandeep Somavarapu 已提交
3187
						}] : [];
3188 3189
						this.notificationService.prompt(
							Severity.Info,
S
Sandeep Somavarapu 已提交
3190 3191
							message,
							actions,
3192 3193 3194 3195
							{ sticky: true }
						);
					}, error => this.notificationService.error(error));
			});
J
Joao Moreno 已提交
3196 3197 3198
	}
}

S
Sandeep Somavarapu 已提交
3199
export class InstallSpecificVersionOfExtensionAction extends Action {
3200

S
Sandeep Somavarapu 已提交
3201
	static readonly ID = 'workbench.extensions.action.install.specificVersion';
3202
	static readonly LABEL = localize('install previous version', "Install Specific Version of Extension...");
3203 3204

	constructor(
S
Sandeep Somavarapu 已提交
3205
		id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,
3206 3207 3208 3209
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
3210
		@IHostService private readonly hostService: IHostService,
S
Sandeep Somavarapu 已提交
3211
		@IInstantiationService private readonly instantiationService: IInstantiationService,
3212
		@IExtensionService private readonly extensionService: IExtensionService,
S
rename  
Sandeep Somavarapu 已提交
3213
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
3214 3215 3216 3217 3218
	) {
		super(id, label);
	}

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

S
Sandeep Somavarapu 已提交
3222 3223 3224
	async run(): Promise<any> {
		const extensionPick = await this.quickInputService.pick(this.getExtensionEntries(), { placeHolder: localize('selectExtension', "Select Extension"), matchOnDetail: true });
		if (extensionPick && extensionPick.extension) {
3225
			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 已提交
3226 3227
			if (versionPick) {
				if (extensionPick.extension.version !== versionPick.id) {
3228
					await this.install(extensionPick.extension, versionPick.id);
S
Sandeep Somavarapu 已提交
3229 3230
				}
			}
S
Sandeep Somavarapu 已提交
3231 3232 3233 3234
		}
	}

	private isEnabled(extension: IExtension): boolean {
3235
		return !!extension.gallery && !!extension.local && this.extensionEnablementService.isEnabled(extension.local);
3236 3237
	}

S
Sandeep Somavarapu 已提交
3238
	private async getExtensionEntries(): Promise<(IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] })[]> {
3239
		const installed = await this.extensionsWorkbenchService.queryLocal();
3240
		const versionsPromises: Promise<{ extension: IExtension, versions: IGalleryExtensionVersion[] } | null>[] = [];
3241
		for (const extension of installed) {
S
Sandeep Somavarapu 已提交
3242
			if (this.isEnabled(extension)) {
3243
				versionsPromises.push(this.extensionGalleryService.getAllVersions(extension.gallery!, true)
S
Sandeep Somavarapu 已提交
3244
					.then(versions => (versions.length ? { extension, versions } : null)));
3245 3246 3247
			}
		}

S
Sandeep Somavarapu 已提交
3248
		const extensions = await Promise.all(versionsPromises);
M
Matt Bierner 已提交
3249
		return coalesce(extensions)
3250
			.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName))
S
Sandeep Somavarapu 已提交
3251
			.map(({ extension, versions }) => {
3252
				return {
S
Sandeep Somavarapu 已提交
3253 3254 3255
					id: extension.identifier.id,
					label: extension.displayName || extension.identifier.id,
					description: extension.identifier.id,
3256
					extension,
S
Sandeep Somavarapu 已提交
3257 3258
					versions
				} as (IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] });
3259 3260 3261
			});
	}

J
Johannes Rieken 已提交
3262
	private install(extension: IExtension, version: string): Promise<void> {
S
Sandeep Somavarapu 已提交
3263 3264
		return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run()
			.then(() => {
3265
				return this.extensionsWorkbenchService.installVersion(extension, version)
S
Sandeep Somavarapu 已提交
3266 3267 3268 3269 3270 3271
					.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"),
3272
							run: () => this.hostService.reload()
S
Sandeep Somavarapu 已提交
3273
						}] : [];
3274 3275
						this.notificationService.prompt(
							Severity.Info,
S
Sandeep Somavarapu 已提交
3276 3277
							message,
							actions,
3278 3279 3280 3281
							{ sticky: true }
						);
					}, error => this.notificationService.error(error));
			});
J
Joao Moreno 已提交
3282 3283 3284
	}
}

3285 3286 3287 3288
interface IExtensionPickItem extends IQuickPickItem {
	extension?: IExtension;
}

3289
export class InstallLocalExtensionsInRemoteAction extends Action {
3290

3291 3292
	private extensions: IExtension[] | undefined = undefined;

3293 3294 3295 3296 3297 3298
	constructor(
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
3299
		@IHostService private readonly hostService: IHostService,
3300 3301
		@IProgressService private readonly progressService: IProgressService,
		@IInstantiationService private readonly instantiationService: IInstantiationService
3302
	) {
3303
		super('workbench.extensions.actions.installLocalExtensionsInRemote');
3304
		this.update();
3305 3306 3307 3308 3309 3310
		this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions());
		this._register(this.extensionsWorkbenchService.onChange(() => {
			if (this.extensions) {
				this.updateExtensions();
			}
		}));
3311 3312 3313
	}

	get label(): string {
S
Sandeep Somavarapu 已提交
3314
		if (this.extensionManagementServerService.remoteExtensionManagementServer) {
3315
			return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label);
S
Sandeep Somavarapu 已提交
3316 3317
		}
		return '';
3318 3319
	}

3320 3321 3322 3323 3324
	private updateExtensions(): void {
		this.extensions = this.extensionsWorkbenchService.local;
		this.update();
	}

3325
	private update(): void {
3326
		this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0;
S
Sandeep Somavarapu 已提交
3327
		this.tooltip = this.label;
3328 3329
	}

3330
	async run(): Promise<void> {
3331
		return this.selectAndInstallLocalExtensions();
3332 3333 3334 3335 3336 3337 3338 3339 3340
	}

	private async queryExtensionsToInstall(): Promise<IExtension[]> {
		const local = await this.extensionsWorkbenchService.queryLocal();
		return this.getExtensionsToInstall(local);
	}

	private getExtensionsToInstall(local: IExtension[]): IExtension[] {
		return local.filter(extension => {
S
Sandeep Somavarapu 已提交
3341
			const action = this.instantiationService.createInstance(RemoteInstallAction, true);
3342 3343 3344
			action.extension = extension;
			return action.enabled;
		});
3345 3346
	}

3347
	private async selectAndInstallLocalExtensions(): Promise<void> {
3348 3349 3350 3351 3352 3353 3354 3355 3356
		const quickPick = this.quickInputService.createQuickPick<IExtensionPickItem>();
		quickPick.busy = true;
		const disposable = quickPick.onDidAccept(() => {
			disposable.dispose();
			quickPick.hide();
			quickPick.dispose();
			this.onDidAccept(quickPick.selectedItems);
		});
		quickPick.show();
3357
		const localExtensionsToInstall = await this.queryExtensionsToInstall();
3358 3359
		quickPick.busy = false;
		if (localExtensionsToInstall.length) {
3360
			quickPick.title = localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label);
3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371
			quickPick.placeholder = localize('select extensions to install', "Select extensions to install");
			quickPick.canSelectMany = true;
			localExtensionsToInstall.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName));
			quickPick.items = localExtensionsToInstall.map<IExtensionPickItem>(extension => ({ extension, label: extension.displayName, description: extension.version }));
		} else {
			quickPick.hide();
			quickPick.dispose();
			this.notificationService.notify({
				severity: Severity.Info,
				message: localize('no local extensions', "There are no extensions to install.")
			});
3372 3373 3374
		}
	}

3375 3376 3377
	private onDidAccept(selectedItems: ReadonlyArray<IExtensionPickItem>): void {
		if (selectedItems.length) {
			const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension!);
3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408
			if (localExtensionsToInstall.length) {
				this.progressService.withProgress(
					{
						location: ProgressLocation.Notification,
						title: localize('installing extensions', "Installing Extensions...")
					},
					() => this.installLocalExtensions(localExtensionsToInstall));
			}
		}
	}

	private async installLocalExtensions(localExtensionsToInstall: IExtension[]): Promise<void> {
		const galleryExtensions: IGalleryExtension[] = [];
		const vsixs: URI[] = [];
		await Promise.all(localExtensionsToInstall.map(async extension => {
			if (this.extensionGalleryService.isEnabled()) {
				const gallery = await this.extensionGalleryService.getCompatibleExtension(extension.identifier, extension.version);
				if (gallery) {
					galleryExtensions.push(gallery);
					return;
				}
			}
			const vsix = await this.extensionManagementServerService.localExtensionManagementServer!.extensionManagementService.zip(extension.local!);
			vsixs.push(vsix);
		}));

		await Promise.all(galleryExtensions.map(gallery => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(gallery)));
		await Promise.all(vsixs.map(vsix => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.install(vsix)));

		this.notificationService.notify({
			severity: Severity.Info,
3409
			message: localize('finished installing', "Successfully installed extensions in {0}. Please reload the window to enable them.", this.extensionManagementServerService.remoteExtensionManagementServer!.label),
3410
			actions: {
S
Sandeep Somavarapu 已提交
3411
				primary: [new Action('realod', localize('reload', "Reload Window"), '', true,
3412
					() => this.hostService.reload())]
3413 3414 3415 3416 3417
			}
		});
	}
}

S
Sandeep Somavarapu 已提交
3418
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {
3419 3420 3421
	const viewletService = accessor.get(IViewletService);

	return viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
3422
		.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
3423 3424 3425 3426 3427
		.then(viewlet => {
			viewlet.search(`ext:${fileExtension.replace(/^\./, '')}`);
			viewlet.focus();
		});
});
B
Benjamin Pasero 已提交
3428

3429
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithIds', function (accessor: ServicesAccessor, extensionIds: string[]) {
3430 3431 3432
	const viewletService = accessor.get(IViewletService);

	return viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
3433
		.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
3434
		.then(viewlet => {
3435 3436 3437 3438
			const query = extensionIds
				.map(id => `@id:${id}`)
				.join(' ');
			viewlet.search(query);
3439 3440 3441 3442
			viewlet.focus();
		});
});

B
Benjamin Pasero 已提交
3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460
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)."));

M
Martin Aeschlimann 已提交
3461
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
B
Benjamin Pasero 已提交
3462 3463
	const foregroundColor = theme.getColor(foreground);
	if (foregroundColor) {
3464
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`);
3465
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`);
B
Benjamin Pasero 已提交
3466 3467
	}

B
Benjamin Pasero 已提交
3468 3469
	const buttonBackgroundColor = theme.getColor(buttonBackground);
	if (buttonBackgroundColor) {
S
Sandeep Somavarapu 已提交
3470 3471
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.label { background-color: ${buttonBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.label { background-color: ${buttonBackgroundColor}; }`);
B
Benjamin Pasero 已提交
3472 3473 3474 3475
	}

	const buttonForegroundColor = theme.getColor(buttonForeground);
	if (buttonForegroundColor) {
S
Sandeep Somavarapu 已提交
3476 3477
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.label { color: ${buttonForegroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.label { color: ${buttonForegroundColor}; }`);
B
Benjamin Pasero 已提交
3478 3479 3480 3481
	}

	const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground);
	if (buttonHoverBackgroundColor) {
S
Sandeep Somavarapu 已提交
3482 3483
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item:hover .action-label.extension-action.label { background-color: ${buttonHoverBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action.label { background-color: ${buttonHoverBackgroundColor}; }`);
B
Benjamin Pasero 已提交
3484 3485 3486 3487
	}

	const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground);
	if (extensionButtonProminentBackground) {
S
Sandeep Somavarapu 已提交
3488 3489
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.label.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.label.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`);
B
Benjamin Pasero 已提交
3490 3491 3492 3493
	}

	const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground);
	if (extensionButtonProminentForeground) {
S
Sandeep Somavarapu 已提交
3494 3495
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.label.prominent { color: ${extensionButtonProminentForegroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.label.prominent { color: ${extensionButtonProminentForegroundColor}; }`);
B
Benjamin Pasero 已提交
3496 3497 3498 3499
	}

	const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground);
	if (extensionButtonProminentHoverBackground) {
S
Sandeep Somavarapu 已提交
3500 3501 3502 3503 3504 3505
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item:hover .action-label.extension-action.label.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action.label.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`);
	}

	const contrastBorderColor = theme.getColor(contrastBorder);
	if (contrastBorderColor) {
S
Sandeep Somavarapu 已提交
3506 3507
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action:not(.disabled) { border: 1px solid ${contrastBorderColor}; }`);
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action:not(.disabled) { border: 1px solid ${contrastBorderColor}; }`);
B
Benjamin Pasero 已提交
3508
	}
A
Alex Dima 已提交
3509
});