extensionsActions.ts 149.2 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';
S
Sandeep Somavarapu 已提交
17
import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions, InstallOperation } 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';
S
Sandeep Somavarapu 已提交
43
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
J
Joao Moreno 已提交
44 45
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 { getErrorMessage, 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';
S
Sandeep Somavarapu 已提交
69 70
import { ILogService } from 'vs/platform/log/common/log';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
71

72 73 74 75 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 105
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 已提交
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
class PromptExtensionInstallFailureAction extends Action {

	constructor(
		private readonly extension: IExtension,
		private readonly installOperation: InstallOperation,
		private readonly error: Error,
		@IProductService private readonly productService: IProductService,
		@IOpenerService private readonly openerService: IOpenerService,
		@INotificationService private readonly notificationService: INotificationService,
		@IDialogService private readonly dialogService: IDialogService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@ILogService private readonly logService: ILogService,
	) {
		super('extension.promptExtensionInstallFailure');
	}

	async run(): Promise<void> {
		if (isPromiseCanceledError(this.error)) {
			return;
		}

		this.logService.error(this.error);
		const operationMessage = this.installOperation === InstallOperation.Update ? localize('update operation', "Error while updating '{0}' extension.", this.extension.displayName || this.extension.identifier.id)
			: localize('install operation', "Error while installing '{0}' extension.", this.extension.displayName || this.extension.identifier.id);

		if ([INSTALL_ERROR_INCOMPATIBLE, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_NOT_SUPPORTED].includes(this.error.name)) {
			await this.dialogService.show(Severity.Error, `${operationMessage}\n${getErrorMessage(this.error)}`, []);
			return;
		}

		const promptChoices: IPromptChoice[] = [];
		if (this.extension.gallery && this.productService.extensionsGallery) {
			promptChoices.push({
				label: localize('download', "Try Downloading Manually..."),
				run: () => this.openerService.open(URI.parse(`${this.productService.extensionsGallery!.serviceUrl}/publishers/${this.extension.publisher}/vsextensions/${this.extension.name}/${this.extension.version}/vspackage`)).then(() => {
					this.notificationService.prompt(
						Severity.Info,
						localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', this.extension.identifier.id),
						[{
							label: InstallVSIXAction.LABEL,
							run: () => {
								const action = this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL);
								action.run();
								action.dispose();
							}
						}]
					);
				})
			});
		}
		const checkLogsMessage = localize('check logs', "Please check [logs]({0}) for more details.", `command:${Constants.showWindowLogActionId}`);
		this.notificationService.prompt(Severity.Error, `${operationMessage} ${checkLogsMessage}`, promptChoices);
	}
}

S
Sandeep Somavarapu 已提交
161
export abstract class ExtensionAction extends Action implements IExtensionContainer {
S
Sandeep Somavarapu 已提交
162 163 164 165
	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 已提交
166 167 168
	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 已提交
169
	abstract update(): void;
170
}
171

172
export abstract class ActionWithDropDownAction extends ExtensionAction {
173

174
	private action: IAction | undefined;
175

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
	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;
	}
215

216 217 218 219 220 221 222 223 224 225 226
	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;
227 228 229 230 231
	set manifest(manifest: IExtensionManifest) {
		this._manifest = manifest;
		this.updateLabel();
	}

232
	constructor(
233
		id: string, label: string, cssClass: string,
234 235
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
236
		@IExtensionService private readonly runtimeExtensionService: IExtensionService,
237
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
238
		@ILabelService private readonly labelService: ILabelService,
239
	) {
240
		super(id, label, cssClass, false);
241
		this.update();
M
Matt Bierner 已提交
242
		this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));
243 244
	}

S
Sandeep Somavarapu 已提交
245
	update(): void {
S
Sandeep Somavarapu 已提交
246
		this.enabled = false;
247
		if (this.extension && !this.extension.isBuiltin) {
S
Sandeep Somavarapu 已提交
248 249 250
			if (this.extension.state === ExtensionState.Uninstalled && this.extensionsWorkbenchService.canInstall(this.extension)) {
				this.enabled = true;
				this.updateLabel();
251
			}
252 253 254
		}
	}

S
Sandeep Somavarapu 已提交
255
	async run(): Promise<any> {
S
Sandeep Somavarapu 已提交
256 257 258
		if (!this.extension) {
			return;
		}
259
		this.extensionsWorkbenchService.open(this.extension);
J
Joao Moreno 已提交
260

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

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

S
Sandeep Somavarapu 已提交
265 266
		if (extension?.local) {
			alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName));
S
Sandeep Somavarapu 已提交
267
			const runningExtension = await this.getRunningExtension(extension.local);
S
Sandeep Somavarapu 已提交
268
			if (runningExtension && !(runningExtension.activationEvents && runningExtension.activationEvents.some(activationEent => activationEent.startsWith('onLanguage')))) {
269 270 271 272 273 274 275 276 277
				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 已提交
278 279
				}
			}
S
Sandeep Somavarapu 已提交
280 281
		}

J
Joao Moreno 已提交
282 283
	}

S
Sandeep Somavarapu 已提交
284 285 286 287 288 289 290
	private async install(extension: IExtension): Promise<IExtension | undefined> {
		try {
			return await this.extensionsWorkbenchService.install(extension, this.getInstallOptions());
		} catch (error) {
			await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Install, error).run();
			return undefined;
		}
S
Sandeep Somavarapu 已提交
291
	}
J
Joao Moreno 已提交
292

S
Sandeep Somavarapu 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	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;
310
	}
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

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

export class InstallAction extends AbstractInstallAction {

	constructor(
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService instantiationService: IInstantiationService,
		@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,
S
Sandeep Somavarapu 已提交
331
			extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService);
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
		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,
		@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,
S
Sandeep Somavarapu 已提交
386
			extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService);
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 426 427 428 429 430 431 432 433 434 435 436 437
		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'}`;
	}
438 439
}

S
Sandeep Somavarapu 已提交
440
export abstract class InstallInOtherServerAction extends ExtensionAction {
441

442 443
	protected static readonly INSTALL_LABEL = localize('install', "Install");
	protected static readonly INSTALLING_LABEL = localize('installing', "Installing");
444

S
Sandeep Somavarapu 已提交
445 446
	private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install`;
	private static readonly InstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} install installing`;
447

448
	updateWhenCounterExtensionChanges: boolean = true;
449 450

	constructor(
S
Sandeep Somavarapu 已提交
451 452
		id: string,
		private readonly server: IExtensionManagementServer | null,
S
Sandeep Somavarapu 已提交
453
		private readonly canInstallAnyWhere: boolean,
454
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
455 456 457
		@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
		@IProductService private readonly productService: IProductService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
458
	) {
S
Sandeep Somavarapu 已提交
459
		super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false);
460 461 462
		this.update();
	}

463
	update(): void {
464
		this.enabled = false;
S
Sandeep Somavarapu 已提交
465 466
		this.class = InstallInOtherServerAction.Class;

S
Sandeep Somavarapu 已提交
467
		if (this.canInstall()) {
S
Sandeep Somavarapu 已提交
468
			const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === this.server)[0];
S
Sandeep Somavarapu 已提交
469 470 471 472 473 474 475 476 477 478 479 480
			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();
			}
481 482 483
		}
	}

S
Sandeep Somavarapu 已提交
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 514 515 516 517 518 519 520 521 522 523 524 525
	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;
	}

526
	async run(): Promise<void> {
S
Sandeep Somavarapu 已提交
527 528 529
		if (!this.extension) {
			return;
		}
S
Sandeep Somavarapu 已提交
530
		if (this.server) {
531 532
			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 已提交
533 534 535 536 537
			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);
538 539 540
			}
		}
	}
S
Sandeep Somavarapu 已提交
541 542

	protected abstract getInstallLabel(): string;
543 544
}

S
Sandeep Somavarapu 已提交
545
export class RemoteInstallAction extends InstallInOtherServerAction {
546

S
Sandeep Somavarapu 已提交
547
	constructor(
S
Sandeep Somavarapu 已提交
548
		canInstallAnyWhere: boolean,
S
Sandeep Somavarapu 已提交
549
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
550 551 552
		@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
		@IProductService productService: IProductService,
		@IConfigurationService configurationService: IConfigurationService,
S
Sandeep Somavarapu 已提交
553
	) {
S
Sandeep Somavarapu 已提交
554
		super(`extensions.remoteinstall`, extensionManagementServerService.remoteExtensionManagementServer, canInstallAnyWhere, extensionsWorkbenchService, extensionManagementServerService, productService, configurationService);
S
Sandeep Somavarapu 已提交
555
	}
556

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

S
Sandeep Somavarapu 已提交
561
}
562

S
Sandeep Somavarapu 已提交
563
export class LocalInstallAction extends InstallInOtherServerAction {
564 565

	constructor(
S
Sandeep Somavarapu 已提交
566
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
567 568 569
		@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService,
		@IProductService productService: IProductService,
		@IConfigurationService configurationService: IConfigurationService,
570
	) {
S
Sandeep Somavarapu 已提交
571
		super(`extensions.localinstall`, extensionManagementServerService.localExtensionManagementServer, false, extensionsWorkbenchService, extensionManagementServerService, productService, configurationService);
572 573
	}

S
Sandeep Somavarapu 已提交
574 575
	protected getInstallLabel(): string {
		return localize('install locally', "Install Locally");
576 577 578 579
	}

}

S
Sandeep Somavarapu 已提交
580
export class UninstallAction extends ExtensionAction {
581

582 583
	private static readonly UninstallLabel = localize('uninstallAction', "Uninstall");
	private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling");
584

S
Sandeep Somavarapu 已提交
585 586
	private static readonly UninstallClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall`;
	private static readonly UnInstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} uninstall uninstalling`;
S
Sandeep Somavarapu 已提交
587

588
	constructor(
S
Sandeep Somavarapu 已提交
589
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
590
	) {
S
Sandeep Somavarapu 已提交
591
		super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false);
592 593 594
		this.update();
	}

S
Sandeep Somavarapu 已提交
595
	update(): void {
596 597 598 599 600 601 602 603 604
		if (!this.extension) {
			this.enabled = false;
			return;
		}

		const state = this.extension.state;

		if (state === ExtensionState.Uninstalling) {
			this.label = UninstallAction.UninstallingLabel;
S
Sandeep Somavarapu 已提交
605
			this.class = UninstallAction.UnInstallingClass;
606 607 608 609 610
			this.enabled = false;
			return;
		}

		this.label = UninstallAction.UninstallLabel;
S
Sandeep Somavarapu 已提交
611
		this.class = UninstallAction.UninstallClass;
S
Sandeep Somavarapu 已提交
612
		this.tooltip = UninstallAction.UninstallLabel;
613

S
Sandeep Somavarapu 已提交
614 615 616 617 618
		if (state !== ExtensionState.Installed) {
			this.enabled = false;
			return;
		}

619
		if (this.extension.isBuiltin) {
620 621 622 623 624 625 626
			this.enabled = false;
			return;
		}

		this.enabled = true;
	}

S
Sandeep Somavarapu 已提交
627 628 629 630
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
631 632 633
		alert(localize('uninstallExtensionStart', "Uninstalling extension {0} started.", this.extension.displayName));

		return this.extensionsWorkbenchService.uninstall(this.extension).then(() => {
S
Sandeep Somavarapu 已提交
634
			alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension!.displayName));
635
		});
636 637 638
	}
}

S
Sandeep Somavarapu 已提交
639
export class UpdateAction extends ExtensionAction {
640

S
Sandeep Somavarapu 已提交
641
	private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent update`;
642
	private static readonly DisabledClass = `${UpdateAction.EnabledClass} disabled`;
643 644

	constructor(
645 646
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
647
	) {
648
		super(`extensions.update`, '', UpdateAction.DisabledClass, false);
649 650 651
		this.update();
	}

S
Sandeep Somavarapu 已提交
652
	update(): void {
653 654 655
		if (!this.extension) {
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
656
			this.label = this.getUpdateLabel();
657 658 659
			return;
		}

660
		if (this.extension.type !== ExtensionType.User) {
661 662
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
663
			this.label = this.getUpdateLabel();
664 665 666 667 668 669 670 671
			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;
672
		this.label = this.extension.outdated ? this.getUpdateLabel(this.extension.latestVersion) : this.getUpdateLabel();
673 674
	}

S
Sandeep Somavarapu 已提交
675 676 677 678
	async run(): Promise<any> {
		if (!this.extension) {
			return;
		}
679
		alert(localize('updateExtensionStart', "Updating extension {0} to version {1} started.", this.extension.displayName, this.extension.latestVersion));
J
Joao Moreno 已提交
680 681 682
		return this.install(this.extension);
	}

S
Sandeep Somavarapu 已提交
683 684 685
	private async install(extension: IExtension): Promise<void> {
		try {
			await this.extensionsWorkbenchService.install(extension);
S
Sandeep Somavarapu 已提交
686
			alert(localize('updateExtensionComplete', "Updating extension {0} to version {1} completed.", extension.displayName, extension.latestVersion));
S
Sandeep Somavarapu 已提交
687 688 689
		} catch (err) {
			this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Update, err).run();
		}
690 691
	}

692
	private getUpdateLabel(version?: string): string {
693
		return version ? localize('updateTo', "Update to {0}", version) : localize('updateAction', "Update");
694
	}
695 696
}

697
export interface IExtensionActionViewItemOptions extends IActionViewItemOptions {
698 699 700
	tabOnlyOnFocus?: boolean;
}

701
export class ExtensionActionViewItem extends ActionViewItem {
702

703
	constructor(context: any, action: IAction, options: IExtensionActionViewItemOptions = {}) {
704 705 706 707 708 709
		super(context, action, options);
	}

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

S
Sandeep Somavarapu 已提交
710
		if (this.label && (<IExtensionActionViewItemOptions>this.options).tabOnlyOnFocus && this.getAction().enabled && !this._hasFocus) {
711 712 713 714
			DOM.removeTabIndexAndUpdateFocus(this.label);
		}
	}

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

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 772 773 774 775 776 777
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 已提交
778
export abstract class ExtensionDropDownAction extends ExtensionAction {
S
Explore  
Sandeep Somavarapu 已提交
779 780 781 782 783 784 785

	constructor(
		id: string,
		label: string,
		cssClass: string,
		enabled: boolean,
		private readonly tabOnlyOnFocus: boolean,
S
Sandeep Somavarapu 已提交
786
		@IInstantiationService protected instantiationService: IInstantiationService
S
Explore  
Sandeep Somavarapu 已提交
787 788 789 790
	) {
		super(id, label, cssClass, enabled);
	}

S
Sandeep Somavarapu 已提交
791
	private _actionViewItem: DropDownMenuActionViewItem | null = null;
792 793 794
	createActionViewItem(): DropDownMenuActionViewItem {
		this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this, this.tabOnlyOnFocus);
		return this._actionViewItem;
S
Explore  
Sandeep Somavarapu 已提交
795 796
	}

J
Johannes Rieken 已提交
797
	public run({ actionGroups, disposeActionsOnHide }: { actionGroups: IAction[][], disposeActionsOnHide: boolean }): Promise<any> {
798 799
		if (this._actionViewItem) {
			this._actionViewItem.showMenu(actionGroups, disposeActionsOnHide);
S
Explore  
Sandeep Somavarapu 已提交
800
		}
801
		return Promise.resolve();
S
Explore  
Sandeep Somavarapu 已提交
802 803 804
	}
}

805
export class DropDownMenuActionViewItem extends ExtensionActionViewItem {
806

S
Sandeep Somavarapu 已提交
807
	constructor(action: ExtensionDropDownAction,
808
		tabOnlyOnFocus: boolean,
809
		@IContextMenuService private readonly contextMenuService: IContextMenuService
S
Sandeep Somavarapu 已提交
810
	) {
811
		super(null, action, { icon: true, label: true, tabOnlyOnFocus });
812 813
	}

S
Sandeep Somavarapu 已提交
814
	public showMenu(menuActionGroups: IAction[][], disposeActionsOnHide: boolean): void {
815 816 817 818 819 820 821 822 823 824 825
		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); } }
			});
		}
826 827
	}

S
Sandeep Somavarapu 已提交
828
	private getActions(menuActionGroups: IAction[][]): IAction[] {
B
Benjamin Pasero 已提交
829
		let actions: IAction[] = [];
S
Sandeep Somavarapu 已提交
830
		for (const menuActions of menuActionGroups) {
S
Sandeep Somavarapu 已提交
831
			actions = [...actions, ...menuActions, new Separator()];
832 833 834 835 836
		}
		return actions.length ? actions.slice(0, actions.length - 1) : actions;
	}
}

837
export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): IAction[][] {
838 839
	const scopedContextKeyService = contextKeyService.createScoped();
	if (extension) {
840
		scopedContextKeyService.createKey<string>('extension', extension.identifier.id);
841
		scopedContextKeyService.createKey<boolean>('isBuiltinExtension', extension.isBuiltin);
842 843 844 845 846 847
		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');
		}
	}

848
	const groups: IAction[][] = [];
849
	const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService);
850 851 852 853 854 855
	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 已提交
856
	menu.dispose();
J
João Moreno 已提交
857
	scopedContextKeyService.dispose();
858

S
Sandeep Somavarapu 已提交
859 860 861
	return groups;
}

S
Sandeep Somavarapu 已提交
862
export class ManageExtensionAction extends ExtensionDropDownAction {
863

864
	static readonly ID = 'extensions.manage';
S
Sandeep Somavarapu 已提交
865 866

	private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage codicon-gear`;
867
	private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`;
868 869

	constructor(
S
Sandeep Somavarapu 已提交
870
		@IInstantiationService instantiationService: IInstantiationService,
871
		@IExtensionService private readonly extensionService: IExtensionService,
872 873 874
		@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
		@IMenuService private readonly menuService: IMenuService,
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
875
	) {
S
Sandeep Somavarapu 已提交
876 877

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

S
Sandeep Somavarapu 已提交
879
		this.tooltip = localize('manage', "Manage");
880 881 882 883

		this.update();
	}

884
	async getActionGroups(runningExtensions: IExtensionDescription[]): Promise<IAction[][]> {
885
		const groups: IAction[][] = [];
886
		if (this.extension) {
887 888 889 890 891 892 893 894 895 896
			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);
897
				}
898 899
			}
			if (themesGroup.length) {
900 901 902
				groups.push(themesGroup);
			}
		}
S
Sandeep Somavarapu 已提交
903
		groups.push([
S
Sandeep Somavarapu 已提交
904
			this.instantiationService.createInstance(EnableGloballyAction),
905
			this.instantiationService.createInstance(EnableForWorkspaceAction)
S
Sandeep Somavarapu 已提交
906 907
		]);
		groups.push([
S
Sandeep Somavarapu 已提交
908 909
			this.instantiationService.createInstance(DisableGloballyAction, runningExtensions),
			this.instantiationService.createInstance(DisableForWorkspaceAction, runningExtensions)
S
Sandeep Somavarapu 已提交
910
		]);
911
		groups.push([this.instantiationService.createInstance(UninstallAction)]);
S
Sandeep Somavarapu 已提交
912
		groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
P
Peng Lyu 已提交
913

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

916 917 918 919 920
		groups.forEach(group => group.forEach(extensionAction => {
			if (extensionAction instanceof ExtensionAction) {
				extensionAction.extension = this.extension;
			}
		}));
S
Sandeep Somavarapu 已提交
921 922 923 924

		return groups;
	}

925 926
	async run(): Promise<any> {
		const runtimeExtensions = await this.extensionService.getExtensions();
927
		return super.run({ actionGroups: await this.getActionGroups(runtimeExtensions), disposeActionsOnHide: true });
S
Sandeep Somavarapu 已提交
928 929
	}

S
Sandeep Somavarapu 已提交
930
	update(): void {
S
Sandeep Somavarapu 已提交
931
		this.class = ManageExtensionAction.HideManageExtensionClass;
932
		this.enabled = false;
S
Sandeep Somavarapu 已提交
933
		if (this.extension) {
934 935
			const state = this.extension.state;
			this.enabled = state === ExtensionState.Installed;
S
Sandeep Somavarapu 已提交
936
			this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass;
937 938 939 940 941
			this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : '';
		}
	}
}

942 943
export class MenuItemExtensionAction extends ExtensionAction {

S
Sandeep Somavarapu 已提交
944 945
	constructor(
		private readonly action: IAction,
946
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
947
	) {
948 949 950
		super(action.id, action.label);
	}

S
Sandeep Somavarapu 已提交
951 952 953 954 955
	update() {
		if (!this.extension) {
			return;
		}
		if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) {
956
			this.checked = !this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);
S
Sandeep Somavarapu 已提交
957 958
		}
	}
959 960 961

	async run(): Promise<void> {
		if (this.extension) {
S
Sandeep Somavarapu 已提交
962
			return this.action.run(this.extension.identifier.id);
963 964 965 966
		}
	}
}

S
Sandeep Somavarapu 已提交
967
export class InstallAnotherVersionAction extends ExtensionAction {
968

969
	static readonly ID = 'workbench.extensions.action.install.anotherVersion';
970
	static readonly LABEL = localize('install another version', "Install Another Version...");
971

S
Sandeep Somavarapu 已提交
972
	constructor(
973 974 975 976
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
977 978
	) {
		super(InstallAnotherVersionAction.ID, InstallAnotherVersionAction.LABEL);
S
Sandeep Somavarapu 已提交
979 980 981 982
		this.update();
	}

	update(): void {
983
		this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery;
984 985
	}

J
Johannes Rieken 已提交
986
	run(): Promise<any> {
987 988 989
		if (!this.enabled) {
			return Promise.resolve();
		}
S
Sandeep Somavarapu 已提交
990
		return this.quickInputService.pick(this.getVersionEntries(), { placeHolder: localize('selectVersion', "Select Version to Install"), matchOnDetail: true })
S
Sandeep Somavarapu 已提交
991
			.then(async pick => {
992
				if (pick) {
S
Sandeep Somavarapu 已提交
993
					if (this.extension!.version === pick.id) {
S
Sandeep Somavarapu 已提交
994 995
						return Promise.resolve();
					}
S
Sandeep Somavarapu 已提交
996 997 998 999 1000 1001 1002 1003 1004
					try {
						if (pick.latest) {
							await this.extensionsWorkbenchService.install(this.extension!);
						} else {
							await this.extensionsWorkbenchService.installVersion(this.extension!, pick.id);
						}
					} catch (error) {
						this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension!, InstallOperation.Install, error).run();
					}
1005 1006 1007 1008
				}
				return null;
			});
	}
S
Sandeep Somavarapu 已提交
1009

1010
	private getVersionEntries(): Promise<(IQuickPickItem & { latest: boolean, id: string })[]> {
S
Sandeep Somavarapu 已提交
1011 1012
		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 已提交
1013
	}
1014 1015
}

S
Sandeep Somavarapu 已提交
1016
export class EnableForWorkspaceAction extends ExtensionAction {
1017

1018
	static readonly ID = 'extensions.enableForWorkspace';
1019
	static readonly LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");
1020

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

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

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

S
Sandeep Somavarapu 已提交
1046
export class EnableGloballyAction extends ExtensionAction {
1047

1048
	static readonly ID = 'extensions.enableGlobally';
1049
	static readonly LABEL = localize('enableGloballyAction', "Enable");
1050

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

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

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

S
Sandeep Somavarapu 已提交
1076
export class DisableForWorkspaceAction extends ExtensionAction {
1077

1078
	static readonly ID = 'extensions.disableForWorkspace';
1079
	static readonly LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");
1080

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

S
Sandeep Somavarapu 已提交
1090
	update(): void {
1091
		this.enabled = false;
S
Sandeep Somavarapu 已提交
1092
		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)) {
1093
			this.enabled = this.extension.state === ExtensionState.Installed
1094
				&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)
S
Sandeep Somavarapu 已提交
1095
				&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);
1096 1097 1098
		}
	}

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

S
Sandeep Somavarapu 已提交
1107
export class DisableGloballyAction extends ExtensionAction {
1108

1109
	static readonly ID = 'extensions.disableGlobally';
1110
	static readonly LABEL = localize('disableGloballyAction', "Disable");
1111

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

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

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

S
Sandeep Somavarapu 已提交
1137
export class EnableDropDownAction extends ActionWithDropDownAction {
S
Sandeep Somavarapu 已提交
1138 1139

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

S
Sandeep Somavarapu 已提交
1149
export class DisableDropDownAction extends ActionWithDropDownAction {
S
Sandeep Somavarapu 已提交
1150 1151

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

1161 1162
}

J
Joao Moreno 已提交
1163 1164
export class CheckForUpdatesAction extends Action {

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

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

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

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

1188
		const disabledExtensionsCount = outdated.filter(ext => ext.local && !this.extensionEnablementService.isEnabled(ext.local)).length;
1189 1190 1191 1192 1193 1194 1195 1196 1197
		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);
1198
			}
1199 1200 1201
		}

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

		this.notificationService.info(msgAvailableExtensions);
1206 1207
	}

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

S
Sandeep Somavarapu 已提交
1213 1214 1215 1216 1217 1218
export class ToggleAutoUpdateAction extends Action {

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

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

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

export class EnableAutoUpdateAction extends ToggleAutoUpdateAction {

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

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

export class DisableAutoUpdateAction extends ToggleAutoUpdateAction {

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

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

1263 1264
export class UpdateAllAction extends Action {

1265
	static readonly ID = 'workbench.extensions.action.updateAllExtensions';
1266
	static readonly LABEL = localize('updateAll', "Update All Extensions");
1267 1268

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

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

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

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

S
Sandeep Somavarapu 已提交
1288 1289 1290 1291 1292 1293
	private async install(extension: IExtension): Promise<void> {
		try {
			await this.extensionsWorkbenchService.install(extension);
		} catch (err) {
			this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Update, err).run();
		}
1294 1295 1296
	}
}

S
Sandeep Somavarapu 已提交
1297
export class ReloadAction extends ExtensionAction {
1298

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

1302
	updateWhenCounterExtensionChanges: boolean = true;
1303
	private _runningExtensions: IExtensionDescription[] | null = null;
1304 1305

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

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

	update(): void {
		this.enabled = false;
		this.tooltip = '';
1326
		if (!this.extension || !this._runningExtensions) {
1327 1328 1329 1330 1331 1332
			return;
		}
		const state = this.extension.state;
		if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) {
			return;
		}
1333
		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) {
1334 1335
			return;
		}
1336
		this.computeReloadState();
1337
		this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass;
1338 1339
	}

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

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

1349
		if (isUninstalled) {
S
Sandeep Somavarapu 已提交
1350
			if (isSameExtensionRunning && !this.extensionService.canRemoveExtension(runningExtension)) {
1351 1352 1353 1354 1355 1356 1357
				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;
		}
1358 1359
		if (this.extension.local) {
			const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
S
Sandeep Somavarapu 已提交
1360

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

1370 1371 1372 1373 1374 1375
					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 已提交
1376
							return;
1377
						}
S
Sandeep Somavarapu 已提交
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397

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

1398
					} else {
S
Sandeep Somavarapu 已提交
1399

1400 1401 1402
						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)) {
1403 1404
								this.enabled = true;
								this.label = localize('reloadRequired', "Reload Required");
1405 1406 1407 1408 1409 1410 1411 1412 1413
								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.");
1414 1415
							}
						}
1416
					}
1417
					return;
1418 1419 1420 1421 1422
				} 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 已提交
1423
					}
1424
				}
1425
				return;
S
Sandeep Somavarapu 已提交
1426 1427 1428 1429
			}

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

				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 已提交
1439
					const extensionInOtherServer = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server === otherServer)[0];
S
Sandeep Somavarapu 已提交
1440 1441 1442 1443 1444
					// 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.");
1445
						alert(localize('installExtensionCompletedAndReloadRequired', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName));
S
Sandeep Somavarapu 已提交
1446
						return;
S
Explore  
Sandeep Somavarapu 已提交
1447
					}
1448
				}
1449 1450 1451 1452
			}
		}
	}

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

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

1462 1463 1464 1465 1466 1467 1468 1469 1470 1471
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 });
1472
	}
1473 1474 1475 1476 1477
	return picks;
}


export class SetColorThemeAction extends ExtensionAction {
1478

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

1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
	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 已提交
1492
	constructor(
1493
		private colorThemes: IWorkbenchColorTheme[],
S
Sandeep Somavarapu 已提交
1494 1495 1496 1497 1498
		@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 已提交
1499
		this._register(Event.any<any>(extensionService.onDidChangeExtensions, workbenchThemeService.onDidColorThemeChange)(() => this.update(), this));
S
Sandeep Somavarapu 已提交
1500 1501 1502
		this.update();
	}

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

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

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

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

export class SetFileIconThemeAction extends ExtensionAction {

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

1535 1536 1537 1538 1539 1540 1541 1542
	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;
1543 1544
	}

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

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

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

		const delayer = new Delayer<any>(100);
1570
		const picks = getQuickPickEntries(this.fileIconThemes, currentTheme, this.extension, showCurrentTheme);
S
Sandeep Somavarapu 已提交
1571 1572 1573 1574
		const pickedTheme = await this.quickInputService.pick(
			picks,
			{
				placeHolder: localize('select file icon theme', "Select File Icon Theme"),
S
Sandeep Somavarapu 已提交
1575 1576
				onDidFocus: item => delayer.trigger(() => this.workbenchThemeService.setFileIconTheme(item.id, undefined)),
				ignoreFocusLost
S
Sandeep Somavarapu 已提交
1577
			});
1578 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
		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 已提交
1634 1635 1636
	}
}

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

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

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

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

1658 1659
export class ShowEnabledExtensionsAction extends Action {

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

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

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

1681 1682
export class ShowInstalledExtensionsAction extends Action {

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

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

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

export class ShowDisabledExtensionsAction extends Action {

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

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

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

S
Sandeep Somavarapu 已提交
1727
export class ClearExtensionsSearchResultsAction extends Action {
1728

S
Sandeep Somavarapu 已提交
1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
	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 {
1751 1752 1753 1754 1755

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

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

}

S
Sandeep Somavarapu 已提交
1770 1771 1772
export class ShowBuiltInExtensionsAction extends Action {

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

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

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

1793 1794
export class ShowOutdatedExtensionsAction extends Action {

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

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

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

export class ShowPopularExtensionsAction extends Action {

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

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

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

S
Sandeep Somavarapu 已提交
1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882
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();
			});
	}
}

1883 1884
export class ShowRecommendedExtensionsAction extends Action {

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

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

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

1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917
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,
	) {
1918
		super(ShowRecommendedExtensionAction.ID, ShowRecommendedExtensionAction.LABEL, undefined, false);
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939
		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;
					});
			});
	}
}

1940
export class InstallRecommendedExtensionAction extends Action {
1941

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

	private extensionId: string;

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

S
Sandeep Somavarapu 已提交
1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971
	async run(): Promise<any> {
		const viewlet = await this.viewletService.openViewlet(VIEWLET_ID, true);
		const viewPaneContainer = viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer;
		viewPaneContainer.search(`@id:${this.extensionId}`);
		viewPaneContainer.focus();
		const pager = await this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None);
		if (pager && pager.firstPage && pager.firstPage.length) {
			const extension = pager.firstPage[0];
			await this.extensionWorkbenchService.open(extension);
			try {
				await this.extensionWorkbenchService.install(extension);
			} catch (err) {
				this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, InstallOperation.Install, err).run();
			}
		}
1972 1973 1974
	}
}

1975 1976 1977 1978
export class IgnoreExtensionRecommendationAction extends Action {

	static readonly ID = 'extensions.ignore';

S
Sandeep Somavarapu 已提交
1979
	private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} ignore`;
1980 1981

	constructor(
S
Sandeep Somavarapu 已提交
1982
		private readonly extension: IExtension,
1983
		@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
1984
	) {
1985
		super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation');
1986 1987 1988 1989 1990 1991

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

S
Sandeep Somavarapu 已提交
1992
	public run(): Promise<any> {
1993
		this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, true);
1994
		return Promise.resolve();
1995 1996 1997 1998 1999 2000 2001
	}
}

export class UndoIgnoreExtensionRecommendationAction extends Action {

	static readonly ID = 'extensions.ignore';

S
Sandeep Somavarapu 已提交
2002
	private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} undo-ignore`;
2003 2004

	constructor(
S
Sandeep Somavarapu 已提交
2005
		private readonly extension: IExtension,
2006
		@IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService,
2007 2008 2009 2010 2011 2012 2013 2014
	) {
		super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo');

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

S
Sandeep Somavarapu 已提交
2015
	public run(): Promise<any> {
2016
		this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, false);
2017
		return Promise.resolve();
2018 2019 2020
	}
}

2021 2022
export class ShowRecommendedKeymapExtensionsAction extends Action {

2023
	static readonly ID = 'workbench.extensions.action.showRecommendedKeymapExtensions';
2024
	static readonly LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps");
2025 2026 2027 2028

	constructor(
		id: string,
		label: string,
2029
		@IViewletService private readonly viewletService: IViewletService
2030
	) {
R
Rob Lourens 已提交
2031
		super(id, label, undefined, true);
2032 2033
	}

J
Johannes Rieken 已提交
2034
	run(): Promise<void> {
2035
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
2036
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
2037 2038 2039 2040 2041 2042 2043
			.then(viewlet => {
				viewlet.search('@recommended:keymaps ');
				viewlet.focus();
			});
	}
}

2044
export class ShowLanguageExtensionsAction extends Action {
2045

2046
	static readonly ID = 'workbench.extensions.action.showLanguageExtensions';
2047
	static readonly LABEL = localize('showLanguageExtensionsShort', "Language Extensions");
2048 2049 2050 2051

	constructor(
		id: string,
		label: string,
2052
		@IViewletService private readonly viewletService: IViewletService
2053
	) {
R
Rob Lourens 已提交
2054
		super(id, label, undefined, true);
2055 2056
	}

J
Johannes Rieken 已提交
2057
	run(): Promise<void> {
2058
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
2059
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
2060
			.then(viewlet => {
C
Christof Marti 已提交
2061
				viewlet.search('@category:"programming languages" @sort:installs ');
2062 2063 2064 2065 2066
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078
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> {
2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095
		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 已提交
2096 2097 2098
	}
}

2099 2100 2101 2102 2103 2104 2105 2106 2107
export class ChangeSortAction extends Action {

	private query: Query;

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
		private sortBy: string,
2108
		@IViewletService private readonly viewletService: IViewletService
2109
	) {
R
Rob Lourens 已提交
2110
		super(id, label, undefined, true);
2111

J
Joao Moreno 已提交
2112
		if (sortBy === undefined) {
2113 2114 2115 2116 2117
			throw new Error('bad arguments');
		}

		this.query = Query.parse('');
		this.enabled = false;
M
Matt Bierner 已提交
2118
		this._register(onSearchChange(this.onSearchChange, this));
2119 2120 2121 2122
	}

	private onSearchChange(value: string): void {
		const query = Query.parse(value);
2123
		this.query = new Query(query.value, this.sortBy || query.sortBy, query.groupBy);
2124
		this.enabled = !!value && this.query.isValid() && !this.query.equals(query);
2125 2126
	}

J
Johannes Rieken 已提交
2127
	run(): Promise<void> {
2128
		return this.viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
2129
			.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
2130 2131 2132 2133 2134 2135 2136
			.then(viewlet => {
				viewlet.search(this.query.toString());
				viewlet.focus();
			});
	}
}

S
Sandeep Somavarapu 已提交
2137 2138 2139 2140
export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution {

	private workspaceContextKey = new RawContextKey<boolean>('workspaceRecommendations', true);
	private workspaceFolderContextKey = new RawContextKey<boolean>('workspaceFolderRecommendations', true);
2141 2142
	private addToWorkspaceRecommendationsContextKey = new RawContextKey<boolean>('addToWorkspaceRecommendations', false);
	private addToWorkspaceFolderRecommendationsContextKey = new RawContextKey<boolean>('addToWorkspaceFolderRecommendations', false);
S
Sandeep Somavarapu 已提交
2143 2144 2145

	constructor(
		@IContextKeyService contextKeyService: IContextKeyService,
2146 2147
		@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService
S
Sandeep Somavarapu 已提交
2148 2149 2150 2151 2152 2153 2154 2155 2156 2157
	) {
		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)));

2158 2159 2160 2161 2162 2163 2164 2165 2166 2167
		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)));
2168

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

2197 2198 2199 2200
		CommandsRegistry.registerCommand(AddToWorkspaceRecommendationsAction.ADD_ID, serviceAccessor => {
			serviceAccessor.get(IInstantiationService)
				.createInstance(AddToWorkspaceRecommendationsAction, AddToWorkspaceRecommendationsAction.ADD_ID, AddToWorkspaceRecommendationsAction.ADD_LABEL)
				.run(AddToWorkspaceRecommendationsAction.ADD);
2201 2202 2203
		});
		MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
			command: {
2204
				id: AddToWorkspaceRecommendationsAction.ADD_ID,
2205
				title: { value: AddToWorkspaceRecommendationsAction.ADD_LABEL, original: 'Add to Recommended Extensions (Workspace)' },
2206
				category: localize('extensions', "Extensions")
2207
			},
2208
			when: this.addToWorkspaceRecommendationsContextKey
2209 2210
		});

2211 2212 2213 2214 2215 2216 2217 2218
		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,
2219
				title: { value: AddToWorkspaceFolderRecommendationsAction.ADD_LABEL, original: 'Extensions: Add to Recommended Extensions (Workspace Folder)' },
2220
				category: localize('extensions', "Extensions")
2221 2222 2223
			},
			when: this.addToWorkspaceFolderRecommendationsContextKey
		});
S
Sandeep Somavarapu 已提交
2224

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

export abstract class AbstractConfigureRecommendedExtensionsAction extends Action {

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService protected contextService: IWorkspaceContextService,
2261
		@IFileService private readonly fileService: IFileService,
2262
		@ITextFileService private readonly textFileService: ITextFileService,
2263
		@IEditorService protected editorService: IEditorService,
2264 2265
		@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
		@ITextModelService private readonly textModelResolverService: ITextModelService
2266
	) {
2267
		super(id, label);
2268 2269
	}

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

S
Sandeep Somavarapu 已提交
2284
	protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {
2285
		return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
B
Benjamin Pasero 已提交
2286
			.then(content => this.getSelectionPosition(content.value.toString(), content.resource, ['extensions', 'recommendations']))
2287 2288 2289
			.then(selection => this.editorService.openEditor({
				resource: workspaceConfigurationFile,
				options: {
B
Benjamin Pasero 已提交
2290 2291
					selection,
					forceReload: true // because content has changed
2292 2293 2294 2295
				}
			}));
	}

2296 2297 2298 2299
	protected addExtensionToWorkspaceConfig(workspaceConfigurationFile: URI, extensionId: string, shouldRecommend: boolean) {
		return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
			.then(content => {
				const extensionIdLowerCase = extensionId.toLowerCase();
B
Benjamin Pasero 已提交
2300
				const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {};
2301 2302 2303 2304
				let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || [];
				let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || [];

				if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) {
S
Sandeep Somavarapu 已提交
2305
					return Promise.resolve(null);
2306 2307 2308 2309 2310 2311
				}

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

				return this.jsonEditingService.write(workspaceConfigurationFile,
2312
					[{
S
Sandeep Somavarapu 已提交
2313
						path: ['extensions'],
2314 2315 2316 2317
						value: {
							recommendations: shouldRecommend ? insertInto : removeFrom,
							unwantedRecommendations: shouldRecommend ? removeFrom : insertInto
						}
2318
					}],
2319 2320 2321 2322
					true);
			});
	}

S
Sandeep Somavarapu 已提交
2323
	protected addExtensionToWorkspaceFolderConfig(extensionsFileResource: URI, extensionId: string, shouldRecommend: boolean): Promise<any> {
2324 2325 2326
		return this.getOrCreateExtensionsFile(extensionsFileResource)
			.then(({ content }) => {
				const extensionIdLowerCase = extensionId.toLowerCase();
2327 2328 2329
				const extensionsConfigContent: IExtensionsConfigContent = json.parse(content) || {};
				let insertInto = shouldRecommend ? extensionsConfigContent.recommendations || [] : extensionsConfigContent.unwantedRecommendations || [];
				let removeFrom = shouldRecommend ? extensionsConfigContent.unwantedRecommendations || [] : extensionsConfigContent.recommendations || [];
2330

2331
				if (insertInto.some(e => e.toLowerCase() === extensionIdLowerCase)) {
S
Sandeep Somavarapu 已提交
2332
					return Promise.resolve(null);
2333 2334
				}

2335 2336
				insertInto.push(extensionId);

2337
				let removeFromPromise: Promise<void> = Promise.resolve();
2338 2339 2340
				if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) {
					removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase);
					removeFromPromise = this.jsonEditingService.write(extensionsFileResource,
2341
						[{
S
Sandeep Somavarapu 已提交
2342
							path: shouldRecommend ? ['unwantedRecommendations'] : ['recommendations'],
2343
							value: removeFrom
2344
						}],
2345 2346 2347
						true);
				}

2348
				return removeFromPromise.then(() =>
2349
					this.jsonEditingService.write(extensionsFileResource,
2350
						[{
S
Sandeep Somavarapu 已提交
2351
							path: shouldRecommend ? ['recommendations'] : ['unwantedRecommendations'],
2352
							value: insertInto
2353
						}],
2354 2355 2356 2357 2358
						true)
				);
			});
	}

S
Sandeep Somavarapu 已提交
2359
	protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
B
Benjamin Pasero 已提交
2360
		return Promise.resolve(this.fileService.readFile(extensionsFileResource))
2361
			.then(content => {
B
Benjamin Pasero 已提交
2362
				return (json.parse(content.value.toString()) || {})['extensions'] || {};
2363 2364 2365
			}, err => ({ recommendations: [], unwantedRecommendations: [] }));
	}

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

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

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

S
Sandeep Somavarapu 已提交
2407
	private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> {
B
Benjamin Pasero 已提交
2408 2409
		return Promise.resolve(this.fileService.readFile(extensionsFileResource)).then(content => {
			return { created: false, extensionsFileResource, content: content.value.toString() };
2410
		}, err => {
B
Benjamin Pasero 已提交
2411
			return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
S
Sandeep Somavarapu 已提交
2412
				return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
2413 2414 2415 2416 2417 2418
			});
		});
	}
}

export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {
2419

2420
	static readonly ID = 'workbench.extensions.action.configureWorkspaceRecommendedExtensions';
2421
	static readonly LABEL = localize('configureWorkspaceRecommendedExtensions', "Configure Recommended Extensions (Workspace)");
2422 2423 2424 2425

	constructor(
		id: string,
		label: string,
2426
		@IFileService fileService: IFileService,
2427
		@ITextFileService textFileService: ITextFileService,
2428
		@IWorkspaceContextService contextService: IWorkspaceContextService,
2429
		@IEditorService editorService: IEditorService,
2430 2431
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService
2432
	) {
2433
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
M
Matt Bierner 已提交
2434
		this._register(this.contextService.onDidChangeWorkbenchState(() => this.update(), this));
2435 2436 2437 2438 2439
		this.update();
	}

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

2442
	public run(): Promise<void> {
2443 2444
		switch (this.contextService.getWorkbenchState()) {
			case WorkbenchState.FOLDER:
2445
				return this.openExtensionsFile(this.contextService.getWorkspace().folders[0].toResource(EXTENSIONS_CONFIG));
2446
			case WorkbenchState.WORKSPACE:
2447
				return this.openWorkspaceConfigurationFile(this.contextService.getWorkspace().configuration!);
2448
		}
2449
		return Promise.resolve();
2450
	}
2451
}
2452

2453 2454
export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends AbstractConfigureRecommendedExtensionsAction {

2455
	static readonly ID = 'workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions';
2456
	static readonly LABEL = localize('configureWorkspaceFolderRecommendedExtensions', "Configure Recommended Extensions (Workspace Folder)");
2457 2458 2459 2460 2461

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
2462
		@ITextFileService textFileService: ITextFileService,
2463
		@IWorkspaceContextService contextService: IWorkspaceContextService,
2464
		@IEditorService editorService: IEditorService,
2465
		@IJSONEditingService jsonEditingService: IJSONEditingService,
S
Sandeep Somavarapu 已提交
2466
		@ITextModelService textModelResolverService: ITextModelService,
2467
		@ICommandService private readonly commandService: ICommandService
2468
	) {
2469
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
M
Matt Bierner 已提交
2470
		this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update(), this));
2471
		this.update();
2472 2473
	}

2474 2475 2476
	private update(): void {
		this.enabled = this.contextService.getWorkspace().folders.length > 0;
	}
2477

S
Sandeep Somavarapu 已提交
2478
	public run(): Promise<any> {
2479
		const folderCount = this.contextService.getWorkspace().folders.length;
S
Sandeep Somavarapu 已提交
2480 2481
		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 已提交
2482 2483
			.then(workspaceFolder => {
				if (workspaceFolder) {
2484
					return this.openExtensionsFile(workspaceFolder.toResource(EXTENSIONS_CONFIG));
2485
				}
S
Sandeep Somavarapu 已提交
2486
				return null;
2487
			});
2488 2489 2490
	}
}

2491 2492 2493 2494 2495 2496 2497
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)");
2498 2499 2500 2501 2502

	constructor(
		id: string,
		label: string,
		@IFileService fileService: IFileService,
2503
		@ITextFileService textFileService: ITextFileService,
2504 2505 2506 2507
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService,
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService,
2508 2509
		@ICommandService private readonly commandService: ICommandService,
		@INotificationService private readonly notificationService: INotificationService
2510
	) {
2511
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
2512 2513
	}

S
Sandeep Somavarapu 已提交
2514
	run(shouldRecommend: boolean): Promise<void> {
2515
		if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension) {
2516
			return Promise.resolve();
2517 2518 2519
		}
		const folders = this.contextService.getWorkspace().folders;
		if (!folders || !folders.length) {
2520
			this.notificationService.info(localize('AddToWorkspaceFolderRecommendations.noWorkspace', 'There are no workspace folders open to add recommendations.'));
2521
			return Promise.resolve();
2522 2523
		}

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

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

S
Sandeep Somavarapu 已提交
2559
						return this.addExtensionToWorkspaceFolderConfig(configurationFile, extensionId.id, shouldRecommend).then(() => {
2560 2561 2562 2563 2564 2565
							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)
								}]);
2566 2567 2568 2569
						}, err => {
							this.notificationService.error(localize('AddToWorkspaceFolderRecommendations.failure', 'Failed to write to extensions.json. {0}', err));
						});
					}
2570 2571 2572 2573 2574
				});
			});
	}
}

2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586
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,
2587
		@ITextFileService textFileService: ITextFileService,
2588 2589 2590 2591
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEditorService editorService: IEditorService,
		@IJSONEditingService jsonEditingService: IJSONEditingService,
		@ITextModelService textModelResolverService: ITextModelService,
2592
		@INotificationService private readonly notificationService: INotificationService
2593
	) {
2594
		super(id, label, contextService, fileService, textFileService, editorService, jsonEditingService, textModelResolverService);
2595 2596
	}

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

2600 2601 2602 2603
		if (!(this.editorService.activeEditor instanceof ExtensionsInput) || !this.editorService.activeEditor.extension || !workspaceConfig) {
			return Promise.resolve();
		}

S
Sandeep Somavarapu 已提交
2604
		const extensionId = this.editorService.activeEditor.extension.identifier;
2605 2606

		return this.getWorkspaceExtensionsConfigContent(workspaceConfig).then(content => {
S
Sandeep Somavarapu 已提交
2607
			const extensionIdLowerCase = extensionId.id.toLowerCase();
2608 2609 2610
			if (shouldRecommend) {
				if ((content.recommendations || []).some(e => e.toLowerCase() === extensionIdLowerCase)) {
					this.notificationService.info(localize('AddToWorkspaceRecommendations.alreadyExists', 'This extension is already present in workspace recommendations.'));
2611
					return Promise.resolve();
2612 2613
				}

S
Sandeep Somavarapu 已提交
2614
				return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => {
2615 2616 2617 2618 2619 2620 2621
					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)
						}]);

2622 2623 2624 2625 2626 2627
				}, 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.'));
2628
					return Promise.resolve();
2629 2630
				}

S
Sandeep Somavarapu 已提交
2631
				return this.addExtensionToWorkspaceConfig(workspaceConfig, extensionId.id, shouldRecommend).then(() => {
2632 2633 2634 2635 2636 2637
					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)
						}]);
2638 2639 2640 2641 2642 2643 2644 2645
				}, err => {
					this.notificationService.error(localize('AddToWorkspaceRecommendations.failure', 'Failed to write. {0}', err));
				});
			}
		});
	}
}

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

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

S
Sandeep Somavarapu 已提交
2651
	private initialStatus: ExtensionState | null = null;
S
#66931  
Sandeep Somavarapu 已提交
2652 2653 2654
	private status: ExtensionState | null = null;
	private enablementState: EnablementState | null = null;

S
Sandeep Somavarapu 已提交
2655 2656 2657
	private _extension: IExtension | null = null;
	get extension(): IExtension | null { return this._extension; }
	set extension(extension: IExtension | null) {
S
Sandeep Somavarapu 已提交
2658
		if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) {
S
#66931  
Sandeep Somavarapu 已提交
2659
			// Different extension. Reset
S
Sandeep Somavarapu 已提交
2660
			this.initialStatus = null;
S
#66931  
Sandeep Somavarapu 已提交
2661 2662 2663 2664 2665 2666 2667 2668
			this.status = null;
			this.enablementState = null;
		}
		this._extension = extension;
		this.update();
	}

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

		const runningExtensions = await this.extensionService.getExtensions();
S
Sandeep Somavarapu 已提交
2697
		const canAddExtension = () => {
S
Sandeep Somavarapu 已提交
2698 2699 2700
			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 已提交
2701 2702
					return true;
				}
S
Sandeep Somavarapu 已提交
2703
				return this.extensionService.canAddExtension(toExtensionDescription(this.extension!.local));
S
Sandeep Somavarapu 已提交
2704 2705 2706 2707
			}
			return false;
		};
		const canRemoveExtension = () => {
S
Sandeep Somavarapu 已提交
2708
			if (this.extension!.local) {
2709
				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 已提交
2710 2711
					return true;
				}
S
Sandeep Somavarapu 已提交
2712
				return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension!.local));
S
Sandeep Somavarapu 已提交
2713 2714 2715
			}
			return false;
		};
S
#66931  
Sandeep Somavarapu 已提交
2716 2717 2718

		if (currentStatus !== null) {
			if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) {
S
Sandeep Somavarapu 已提交
2719
				return canAddExtension() ? this.initialStatus === ExtensionState.Installed ? localize('updated', "Updated") : localize('installed', "Installed") : null;
S
#66931  
Sandeep Somavarapu 已提交
2720 2721
			}
			if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) {
S
Sandeep Somavarapu 已提交
2722
				this.initialStatus = this.status;
S
#66931  
Sandeep Somavarapu 已提交
2723 2724 2725 2726 2727
				return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null;
			}
		}

		if (currentEnablementState !== null) {
2728 2729
			const currentlyEnabled = currentEnablementState === EnablementState.EnabledGlobally || currentEnablementState === EnablementState.EnabledWorkspace;
			const enabled = this.enablementState === EnablementState.EnabledGlobally || this.enablementState === EnablementState.EnabledWorkspace;
S
#66931  
Sandeep Somavarapu 已提交
2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742
			if (!currentlyEnabled && enabled) {
				return canAddExtension() ? localize('enabled', "Enabled") : null;
			}
			if (currentlyEnabled && !enabled) {
				return canRemoveExtension() ? localize('disabled', "Disabled") : null;
			}

		}

		return null;
	}

	run(): Promise<any> {
2743
		return Promise.resolve();
S
#66931  
Sandeep Somavarapu 已提交
2744 2745 2746 2747
	}

}

S
Sandeep Somavarapu 已提交
2748
export class MaliciousStatusLabelAction extends ExtensionAction {
J
Joao Moreno 已提交
2749

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

	constructor(long: boolean) {
J
Joao Moreno 已提交
2753
		const tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
2754
		const label = long ? tooltip : localize({ key: 'malicious', comment: ['Refers to a malicious extension'] }, "Malicious");
J
Joao Moreno 已提交
2755
		super('extensions.install', label, '', false);
J
Joao Moreno 已提交
2756
		this.tooltip = localize('malicious tooltip', "This extension was reported to be problematic.");
J
Joao Moreno 已提交
2757 2758
	}

S
Sandeep Somavarapu 已提交
2759
	update(): void {
J
Joao Moreno 已提交
2760 2761 2762 2763 2764 2765 2766
		if (this.extension && this.extension.isMalicious) {
			this.class = `${MaliciousStatusLabelAction.Class} malicious`;
		} else {
			this.class = `${MaliciousStatusLabelAction.Class} not-malicious`;
		}
	}

S
Sandeep Somavarapu 已提交
2767
	run(): Promise<any> {
2768
		return Promise.resolve();
J
Joao Moreno 已提交
2769 2770 2771
	}
}

S
Sandeep Somavarapu 已提交
2772
export class ToggleSyncExtensionAction extends ExtensionDropDownAction {
S
Sandeep Somavarapu 已提交
2773

S
Sandeep Somavarapu 已提交
2774 2775
	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 已提交
2776 2777

	constructor(
2778 2779
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
Sandeep Somavarapu 已提交
2780 2781
		@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
		@IInstantiationService instantiationService: IInstantiationService,
S
Sandeep Somavarapu 已提交
2782
	) {
S
Sandeep Somavarapu 已提交
2783
		super('extensions.sync', '', ToggleSyncExtensionAction.SYNC_CLASS, false, true, instantiationService);
S
Sandeep Somavarapu 已提交
2784
		this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectedKeys.includes('settingsSync.ignoredExtensions'))(() => this.update()));
S
Sandeep Somavarapu 已提交
2785
		this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.update()));
S
Sandeep Somavarapu 已提交
2786 2787 2788 2789
		this.update();
	}

	update(): void {
2790
		this.enabled = !!this.extension && this.userDataAutoSyncEnablementService.isEnabled() && this.extension.state === ExtensionState.Installed;
S
Sandeep Somavarapu 已提交
2791 2792 2793 2794
		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");
2795
		}
S
Sandeep Somavarapu 已提交
2796 2797
	}

S
Sandeep Somavarapu 已提交
2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808
	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 已提交
2809 2810 2811
	}
}

S
Sandeep Somavarapu 已提交
2812
export class ExtensionToolTipAction extends ExtensionAction {
2813

S
Sandeep Somavarapu 已提交
2814
	private static readonly Class = `${ExtensionAction.TEXT_ACTION_CLASS} disable-status`;
2815 2816

	updateWhenCounterExtensionChanges: boolean = true;
2817
	private _runningExtensions: IExtensionDescription[] | null = null;
2818

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

	private updateRunningExtensions(): void {
		this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
2834 2835 2836
	}

	update(): void {
S
Sandeep Somavarapu 已提交
2837 2838 2839 2840
		this.label = this.getTooltip();
		this.class = ExtensionToolTipAction.Class;
		if (!this.label) {
			this.class = `${ExtensionToolTipAction.Class} hide`;
2841
		}
S
Sandeep Somavarapu 已提交
2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852
	}

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

			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.");
				}
2879
			}
2880
		}
S
Sandeep Somavarapu 已提交
2881
		return '';
2882 2883 2884 2885 2886 2887 2888 2889 2890
	}

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

export class SystemDisabledWarningAction extends ExtensionAction {

S
Sandeep Somavarapu 已提交
2891
	private static readonly CLASS = `${ExtensionAction.ICON_ACTION_CLASS} system-disable`;
2892 2893
	private static readonly WARNING_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.warning.classNames}`;
	private static readonly INFO_CLASS = `${SystemDisabledWarningAction.CLASS} ${Codicon.info.classNames}`;
2894 2895

	updateWhenCounterExtensionChanges: boolean = true;
2896
	private _runningExtensions: IExtensionDescription[] | null = null;
2897

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

2913 2914 2915 2916
	private updateRunningExtensions(): void {
		this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
	}

2917
	update(): void {
2918
		this.class = `${SystemDisabledWarningAction.CLASS} hide`;
2919
		this.tooltip = '';
2920 2921 2922 2923 2924
		if (
			!this.extension ||
			!this.extension.local ||
			!this.extension.server ||
			!this._runningExtensions ||
2925
			this.extension.state !== ExtensionState.Installed
2926
		) {
2927 2928
			return;
		}
2929 2930 2931 2932 2933
		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 已提交
2934 2935
						? 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.");
2936 2937
				}
				return;
2938 2939
			}
		}
S
Sandeep Somavarapu 已提交
2940
		if (this.extension.enablementState === EnablementState.DisabledByExtensionKind) {
S
Sandeep Somavarapu 已提交
2941
			if (!this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)) {
S
Sandeep Somavarapu 已提交
2942
				const server = this.extensionManagementServerService.localExtensionManagementServer === this.extension.server ? this.extensionManagementServerService.remoteExtensionManagementServer : this.extensionManagementServerService.localExtensionManagementServer;
2943
				this.class = `${SystemDisabledWarningAction.WARNING_CLASS}`;
2944 2945 2946
				if (server) {
					this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label);
				} else {
S
Sandeep Somavarapu 已提交
2947
					this.tooltip = localize('disabled because of extension kind', "This extension has defined that it cannot run on the remote server");
2948
				}
S
Sandeep Somavarapu 已提交
2949 2950
				return;
			}
2951
		}
2952 2953
		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];
2954
			const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)) : null;
2955 2956 2957 2958 2959 2960
			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;
2961
			}
2962 2963 2964 2965 2966 2967
			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;
2968
			}
2969 2970 2971 2972 2973 2974 2975 2976
		}
	}

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

2977 2978
export class DisableAllAction extends Action {

2979
	static readonly ID = 'workbench.extensions.action.disableAll';
2980
	static readonly LABEL = localize('disableAll', "Disable All Installed Extensions");
2981 2982

	constructor(
S
Sandeep Somavarapu 已提交
2983
		id: string, label: string, isPrimary: boolean,
2984
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
2985
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
2986 2987
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
2988 2989 2990
		if (isPrimary) {
			this._register(this.extensionsWorkbenchService.onChange(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
2991 2992
	}

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

S
Sandeep Somavarapu 已提交
2997 2998
	get enabled(): boolean {
		return this.getExtensionsToDisable().length > 0;
2999 3000
	}

S
Sandeep Somavarapu 已提交
3001
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3002
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToDisable(), EnablementState.DisabledGlobally);
3003 3004 3005
	}
}

3006
export class DisableAllWorkspaceAction extends Action {
3007

3008
	static readonly ID = 'workbench.extensions.action.disableAllWorkspace';
3009
	static readonly LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace");
3010 3011

	constructor(
S
Sandeep Somavarapu 已提交
3012
		id: string, label: string, isPrimary: boolean,
3013
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
3014
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
3015
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
3016 3017
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
3018 3019 3020
		if (isPrimary) {
			this._register(Event.any(this.workspaceContextService.onDidChangeWorkbenchState, this.extensionsWorkbenchService.onChange)(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
3021 3022
	}

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

S
Sandeep Somavarapu 已提交
3027 3028
	get enabled(): boolean {
		return this.getExtensionsToDisable().length > 0;
3029 3030
	}

S
Sandeep Somavarapu 已提交
3031
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3032
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToDisable(), EnablementState.DisabledWorkspace);
3033 3034 3035 3036 3037
	}
}

export class EnableAllAction extends Action {

3038
	static readonly ID = 'workbench.extensions.action.enableAll';
3039
	static readonly LABEL = localize('enableAll', "Enable All Extensions");
3040 3041

	constructor(
S
Sandeep Somavarapu 已提交
3042
		id: string, label: string, isPrimary: boolean,
3043
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
3044
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
3045 3046
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
3047 3048 3049
		if (isPrimary) {
			this._register(this.extensionsWorkbenchService.onChange(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
3050 3051
	}

S
Sandeep Somavarapu 已提交
3052 3053 3054 3055
	private getExtensionsToEnable(): IExtension[] {
		return this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local));
	}

S
Sandeep Somavarapu 已提交
3056 3057
	get enabled(): boolean {
		return this.getExtensionsToEnable().length > 0;
3058 3059
	}

S
Sandeep Somavarapu 已提交
3060
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3061
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToEnable(), EnablementState.EnabledGlobally);
3062 3063 3064
	}
}

3065
export class EnableAllWorkspaceAction extends Action {
3066

3067
	static readonly ID = 'workbench.extensions.action.enableAllWorkspace';
3068
	static readonly LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace");
3069 3070

	constructor(
S
Sandeep Somavarapu 已提交
3071
		id: string, label: string, isPrimary: boolean,
3072 3073
		@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
S
rename  
Sandeep Somavarapu 已提交
3074
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService
3075 3076
	) {
		super(id, label);
S
Sandeep Somavarapu 已提交
3077 3078 3079
		if (isPrimary) {
			this._register(Event.any(this.workspaceContextService.onDidChangeWorkbenchState, this.extensionsWorkbenchService.onChange)(() => this._onDidChange.fire({ enabled: this.enabled })));
		}
3080 3081
	}

S
Sandeep Somavarapu 已提交
3082 3083 3084 3085
	private getExtensionsToEnable(): IExtension[] {
		return this.extensionsWorkbenchService.local.filter(e => !!e.local && this.extensionEnablementService.canChangeEnablement(e.local) && !this.extensionEnablementService.isEnabled(e.local));
	}

S
Sandeep Somavarapu 已提交
3086 3087
	get enabled(): boolean {
		return this.getExtensionsToEnable().length > 0;
3088 3089
	}

S
Sandeep Somavarapu 已提交
3090
	run(): Promise<any> {
S
Sandeep Somavarapu 已提交
3091
		return this.extensionsWorkbenchService.setEnablement(this.getExtensionsToEnable(), EnablementState.EnabledWorkspace);
3092
	}
3093 3094
}

J
Joao Moreno 已提交
3095 3096 3097
export class InstallVSIXAction extends Action {

	static readonly ID = 'workbench.extensions.action.installVSIX';
3098
	static readonly LABEL = localize('installVSIX', "Install from VSIX...");
J
Joao Moreno 已提交
3099 3100 3101 3102

	constructor(
		id = InstallVSIXAction.ID,
		label = InstallVSIXAction.LABEL,
3103
		@IFileDialogService private readonly fileDialogService: IFileDialogService,
3104
		@ICommandService private readonly commandService: ICommandService
J
Joao Moreno 已提交
3105
	) {
S
Sandeep Somavarapu 已提交
3106
		super(id, label, 'extension-action install-vsix', true);
J
Joao Moreno 已提交
3107 3108
	}

3109 3110 3111 3112 3113 3114 3115
	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"))
		});
3116

3117 3118
		if (!vsixPaths) {
			return;
3119
		}
J
Joao Moreno 已提交
3120

3121
		// Install extension(s), display notification(s), display @installed extensions
3122
		await this.commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths);
J
Joao Moreno 已提交
3123 3124 3125 3126 3127 3128
	}
}

export class ReinstallAction extends Action {

	static readonly ID = 'workbench.extensions.action.reinstall';
3129
	static readonly LABEL = localize('reinstall', "Reinstall Extension...");
J
Joao Moreno 已提交
3130 3131 3132

	constructor(
		id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL,
3133 3134 3135
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
3136
		@IHostService private readonly hostService: IHostService,
S
Sandeep Somavarapu 已提交
3137 3138
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IExtensionService private readonly extensionService: IExtensionService
J
Joao Moreno 已提交
3139 3140 3141 3142 3143
	) {
		super(id, label);
	}

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

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

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

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

S
Sandeep Somavarapu 已提交
3192
export class InstallSpecificVersionOfExtensionAction extends Action {
3193

S
Sandeep Somavarapu 已提交
3194
	static readonly ID = 'workbench.extensions.action.install.specificVersion';
3195
	static readonly LABEL = localize('install previous version', "Install Specific Version of Extension...");
3196 3197

	constructor(
S
Sandeep Somavarapu 已提交
3198
		id: string = InstallSpecificVersionOfExtensionAction.ID, label: string = InstallSpecificVersionOfExtensionAction.LABEL,
3199 3200 3201 3202
		@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@INotificationService private readonly notificationService: INotificationService,
3203
		@IHostService private readonly hostService: IHostService,
S
Sandeep Somavarapu 已提交
3204
		@IInstantiationService private readonly instantiationService: IInstantiationService,
3205
		@IExtensionService private readonly extensionService: IExtensionService,
S
rename  
Sandeep Somavarapu 已提交
3206
		@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
3207 3208 3209 3210 3211
	) {
		super(id, label);
	}

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

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

	private isEnabled(extension: IExtension): boolean {
3228
		return !!extension.gallery && !!extension.local && this.extensionEnablementService.isEnabled(extension.local);
3229 3230
	}

S
Sandeep Somavarapu 已提交
3231
	private async getExtensionEntries(): Promise<(IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] })[]> {
3232
		const installed = await this.extensionsWorkbenchService.queryLocal();
3233
		const versionsPromises: Promise<{ extension: IExtension, versions: IGalleryExtensionVersion[] } | null>[] = [];
3234
		for (const extension of installed) {
S
Sandeep Somavarapu 已提交
3235
			if (this.isEnabled(extension)) {
3236
				versionsPromises.push(this.extensionGalleryService.getAllVersions(extension.gallery!, true)
S
Sandeep Somavarapu 已提交
3237
					.then(versions => (versions.length ? { extension, versions } : null)));
3238 3239 3240
			}
		}

S
Sandeep Somavarapu 已提交
3241
		const extensions = await Promise.all(versionsPromises);
M
Matt Bierner 已提交
3242
		return coalesce(extensions)
3243
			.sort((e1, e2) => e1.extension.displayName.localeCompare(e2.extension.displayName))
S
Sandeep Somavarapu 已提交
3244
			.map(({ extension, versions }) => {
3245
				return {
S
Sandeep Somavarapu 已提交
3246 3247 3248
					id: extension.identifier.id,
					label: extension.displayName || extension.identifier.id,
					description: extension.identifier.id,
3249
					extension,
S
Sandeep Somavarapu 已提交
3250 3251
					versions
				} as (IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] });
3252 3253 3254
			});
	}

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

3278 3279 3280 3281
interface IExtensionPickItem extends IQuickPickItem {
	extension?: IExtension;
}

3282
export class InstallLocalExtensionsInRemoteAction extends Action {
3283

3284 3285
	private extensions: IExtension[] | undefined = undefined;

3286 3287 3288 3289 3290 3291
	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,
3292
		@IHostService private readonly hostService: IHostService,
3293 3294
		@IProgressService private readonly progressService: IProgressService,
		@IInstantiationService private readonly instantiationService: IInstantiationService
3295
	) {
3296
		super('workbench.extensions.actions.installLocalExtensionsInRemote');
3297
		this.update();
3298 3299 3300 3301 3302 3303
		this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions());
		this._register(this.extensionsWorkbenchService.onChange(() => {
			if (this.extensions) {
				this.updateExtensions();
			}
		}));
3304 3305 3306
	}

	get label(): string {
S
Sandeep Somavarapu 已提交
3307
		if (this.extensionManagementServerService.remoteExtensionManagementServer) {
3308
			return localize('select and install local extensions', "Install Local Extensions in '{0}'...", this.extensionManagementServerService.remoteExtensionManagementServer.label);
S
Sandeep Somavarapu 已提交
3309 3310
		}
		return '';
3311 3312
	}

3313 3314 3315 3316 3317
	private updateExtensions(): void {
		this.extensions = this.extensionsWorkbenchService.local;
		this.update();
	}

3318
	private update(): void {
3319
		this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0;
S
Sandeep Somavarapu 已提交
3320
		this.tooltip = this.label;
3321 3322
	}

3323
	async run(): Promise<void> {
3324
		return this.selectAndInstallLocalExtensions();
3325 3326 3327 3328 3329 3330 3331 3332 3333
	}

	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 已提交
3334
			const action = this.instantiationService.createInstance(RemoteInstallAction, true);
3335 3336 3337
			action.extension = extension;
			return action.enabled;
		});
3338 3339
	}

3340
	private async selectAndInstallLocalExtensions(): Promise<void> {
3341 3342 3343 3344 3345 3346 3347 3348 3349
		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();
3350
		const localExtensionsToInstall = await this.queryExtensionsToInstall();
3351 3352
		quickPick.busy = false;
		if (localExtensionsToInstall.length) {
3353
			quickPick.title = localize('install local extensions title', "Install Local Extensions in '{0}'", this.extensionManagementServerService.remoteExtensionManagementServer!.label);
3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364
			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.")
			});
3365 3366 3367
		}
	}

3368 3369 3370
	private onDidAccept(selectedItems: ReadonlyArray<IExtensionPickItem>): void {
		if (selectedItems.length) {
			const localExtensionsToInstall = selectedItems.filter(r => !!r.extension).map(r => r.extension!);
3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401
			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,
3402
			message: localize('finished installing', "Successfully installed extensions in {0}. Please reload the window to enable them.", this.extensionManagementServerService.remoteExtensionManagementServer!.label),
3403
			actions: {
S
Sandeep Somavarapu 已提交
3404
				primary: [new Action('realod', localize('reload', "Reload Window"), '', true,
3405
					() => this.hostService.reload())]
3406 3407 3408 3409 3410
			}
		});
	}
}

S
Sandeep Somavarapu 已提交
3411
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForLanguage', function (accessor: ServicesAccessor, fileExtension: string) {
3412 3413 3414
	const viewletService = accessor.get(IViewletService);

	return viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
3415
		.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
3416 3417 3418 3419 3420
		.then(viewlet => {
			viewlet.search(`ext:${fileExtension.replace(/^\./, '')}`);
			viewlet.focus();
		});
});
B
Benjamin Pasero 已提交
3421

3422
CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithIds', function (accessor: ServicesAccessor, extensionIds: string[]) {
3423 3424 3425
	const viewletService = accessor.get(IViewletService);

	return viewletService.openViewlet(VIEWLET_ID, true)
S
SteVen Batten 已提交
3426
		.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
3427
		.then(viewlet => {
3428 3429 3430 3431
			const query = extensionIds
				.map(id => `@id:${id}`)
				.join(' ');
			viewlet.search(query);
3432 3433 3434 3435
			viewlet.focus();
		});
});

B
Benjamin Pasero 已提交
3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453
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 已提交
3454
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
B
Benjamin Pasero 已提交
3455 3456
	const foregroundColor = theme.getColor(foreground);
	if (foregroundColor) {
3457
		collector.addRule(`.extension-list-item .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`);
3458
		collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`);
B
Benjamin Pasero 已提交
3459 3460
	}

B
Benjamin Pasero 已提交
3461 3462
	const buttonBackgroundColor = theme.getColor(buttonBackground);
	if (buttonBackgroundColor) {
S
Sandeep Somavarapu 已提交
3463 3464
		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 已提交
3465 3466 3467 3468
	}

	const buttonForegroundColor = theme.getColor(buttonForeground);
	if (buttonForegroundColor) {
S
Sandeep Somavarapu 已提交
3469 3470
		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 已提交
3471 3472 3473 3474
	}

	const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground);
	if (buttonHoverBackgroundColor) {
S
Sandeep Somavarapu 已提交
3475 3476
		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 已提交
3477 3478 3479 3480
	}

	const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground);
	if (extensionButtonProminentBackground) {
S
Sandeep Somavarapu 已提交
3481 3482
		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 已提交
3483 3484 3485 3486
	}

	const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground);
	if (extensionButtonProminentForeground) {
S
Sandeep Somavarapu 已提交
3487 3488
		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 已提交
3489 3490 3491 3492
	}

	const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground);
	if (extensionButtonProminentHoverBackground) {
S
Sandeep Somavarapu 已提交
3493 3494 3495 3496 3497 3498
		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 已提交
3499 3500
		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 已提交
3501
	}
A
Alex Dima 已提交
3502
});