extensionsActions.ts 16.0 KB
Newer Older
E
Erich Gamma 已提交
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.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import 'vs/css!./media/extensionActions';
J
Joao Moreno 已提交
7
import { localize } from 'vs/nls';
J
Joao Moreno 已提交
8
import { TPromise } from 'vs/base/common/winjs.base';
E
Erich Gamma 已提交
9
import { Action } from 'vs/base/common/actions';
10
import severity from 'vs/base/common/severity';
11
import paths = require('vs/base/common/paths');
J
Joao Moreno 已提交
12
import Event from 'vs/base/common/event';
J
Joao Moreno 已提交
13
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
J
Joao Moreno 已提交
14
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
15
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet } from './extensions';
16
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
17
import { IMessageService, LaterAction } from 'vs/platform/message/common/message';
18
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
19 20 21
import { ToggleViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
J
Joao Moreno 已提交
22
import { Query } from '../common/extensionQuery';
23
import { shell } from 'electron';
J
Joao Moreno 已提交
24

25 26
export class InstallAction extends Action {

J
Joao Moreno 已提交
27 28
	private static InstallLabel = localize('installAction', "Install");
	private static InstallingLabel = localize('installing', "Installing");
J
Joao Moreno 已提交
29
	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
30 31 32
	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) { this._extension = extension; this.update(); }
33

34 35 36
	constructor(
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
J
Joao Moreno 已提交
37
		super('extensions.install', InstallAction.InstallLabel, 'extension-action install', false);
38

39
		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
J
Joao Moreno 已提交
40
		this.update();
41 42
	}

J
Joao Moreno 已提交
43
	private update(): void {
J
Joao Moreno 已提交
44 45 46 47 48 49
		if (!this.extension) {
			this.enabled = false;
			this.label = InstallAction.InstallLabel;
			return;
		}

50
		this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled;
J
Joao Moreno 已提交
51
		this.label = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingLabel : InstallAction.InstallLabel;
52 53
	}

J
Joao Moreno 已提交
54
	run(): TPromise<any> {
55
		return this.extensionsWorkbenchService.install(this.extension);
56 57
	}

J
Joao Moreno 已提交
58 59 60
	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
61 62
	}
}
J
Joao Moreno 已提交
63

J
Joao Moreno 已提交
64
export class UninstallAction extends Action {
J
Joao Moreno 已提交
65

J
Joao Moreno 已提交
66
	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
67 68 69
	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) { this._extension = extension; this.update(); }
J
Joao Moreno 已提交
70

71
	constructor(
72 73 74
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IMessageService private messageService: IMessageService,
		@IInstantiationService private instantiationService: IInstantiationService
75
	) {
J
Joao Moreno 已提交
76
		super('extensions.uninstall', localize('uninstall', "Uninstall"), 'extension-action uninstall', false);
J
Joao Moreno 已提交
77

J
Joao Moreno 已提交
78 79
		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
		this.update();
J
Joao Moreno 已提交
80
	}
J
Joao Moreno 已提交
81

J
Joao Moreno 已提交
82 83 84 85 86 87
	private update(): void {
		if (!this.extension) {
			this.enabled = false;
			return;
		}

J
Joao Moreno 已提交
88 89
		this.enabled = this.extension.state === ExtensionState.Installed
			|| this.extension.state === ExtensionState.NeedsRestart;
J
Joao Moreno 已提交
90
	}
J
Joao Moreno 已提交
91

J
Joao Moreno 已提交
92
	run(): TPromise<any> {
J
Joao Moreno 已提交
93
		if (!window.confirm(localize('deleteSure', "Are you sure you want to uninstall '{0}'?", this.extension.displayName))) {
J
Joao Moreno 已提交
94 95
			return TPromise.as(null);
		}
J
Joao Moreno 已提交
96

97 98 99
		return this.extensionsWorkbenchService.uninstall(this.extension).then(() => {
			this.messageService.show(severity.Info, {
				message: localize('postUninstallMessage', "{0} was successfully uninstalled. Restart to deactivate it.", this.extension.displayName),
100
				actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('restartNow', "Restart Now")), LaterAction]
101 102
			});
		});
J
Joao Moreno 已提交
103
	}
J
Joao Moreno 已提交
104

J
Joao Moreno 已提交
105 106 107 108 109
	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}
J
Joao Moreno 已提交
110 111 112

export class CombinedInstallAction extends Action {

J
Joao Moreno 已提交
113
	private static NoExtensionClass = 'extension-action install no-extension';
J
Joao Moreno 已提交
114 115 116
	private installAction: InstallAction;
	private uninstallAction: UninstallAction;
	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
117 118 119 120 121 122 123
	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) {
		this._extension = extension;
		this.installAction.extension = extension;
		this.uninstallAction.extension = extension;
	}
J
Joao Moreno 已提交
124

125 126 127
	constructor(
		@IInstantiationService instantiationService: IInstantiationService
	) {
J
Joao Moreno 已提交
128 129
		super('extensions.combinedInstall', '', '', false);

J
Joao Moreno 已提交
130 131
		this.installAction = instantiationService.createInstance(InstallAction);
		this.uninstallAction = instantiationService.createInstance(UninstallAction);
J
Joao Moreno 已提交
132 133
		this.disposables.push(this.installAction, this.uninstallAction);

134
		this.installAction.onDidChange(this.update, this, this.disposables);
135
		this.uninstallAction.onDidChange(this.update, this, this.disposables);
J
Joao Moreno 已提交
136 137 138 139
		this.update();
	}

	private update(): void {
J
Joao Moreno 已提交
140 141 142 143
		if (!this.extension) {
			this.enabled = false;
			this.class = CombinedInstallAction.NoExtensionClass;
		} else if (this.installAction.enabled) {
J
Joao Moreno 已提交
144 145 146 147 148 149 150
			this.enabled = true;
			this.label = this.installAction.label;
			this.class = this.installAction.class;
		} else if (this.uninstallAction.enabled) {
			this.enabled = true;
			this.label = this.uninstallAction.label;
			this.class = this.uninstallAction.class;
J
Joao Moreno 已提交
151 152 153 154
		} else if (this.extension.state === ExtensionState.Installing) {
			this.enabled = false;
			this.label = this.installAction.label;
			this.class = this.installAction.class;
J
Joao Moreno 已提交
155 156
		} else {
			this.enabled = false;
J
Joao Moreno 已提交
157 158
			this.label = this.installAction.label;
			this.class = this.installAction.class;
J
Joao Moreno 已提交
159 160 161 162 163 164 165 166 167 168 169 170 171
		}
	}

	run(): TPromise<any> {
		if (this.installAction.enabled) {
			return this.installAction.run();
		} else if (this.uninstallAction.enabled) {
			return this.uninstallAction.run();
		}

		return TPromise.as(null);
	}

J
Joao Moreno 已提交
172 173 174 175 176 177 178 179
	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

export class UpdateAction extends Action {

J
Joao Moreno 已提交
180
	private static EnabledClass = 'extension-action update';
J
Joao Moreno 已提交
181 182 183
	private static DisabledClass = `${ UpdateAction.EnabledClass } disabled`;

	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
184 185 186
	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) { this._extension = extension; this.update(); }
J
Joao Moreno 已提交
187

188 189 190
	constructor(
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
J
Joao Moreno 已提交
191
		super('extensions.update', localize('updateAction', "Update"), UpdateAction.DisabledClass, false);
J
Joao Moreno 已提交
192

J
Joao Moreno 已提交
193 194
		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
		this.update();
J
Joao Moreno 已提交
195 196
	}

J
Joao Moreno 已提交
197 198 199 200 201 202 203
	private update(): void {
		if (!this.extension) {
			this.enabled = false;
			this.class = UpdateAction.DisabledClass;
			return;
		}

204
		const canInstall = this.extensionsWorkbenchService.canInstall(this.extension);
J
Joao Moreno 已提交
205 206
		const isInstalled = this.extension.state === ExtensionState.Installed
			|| this.extension.state === ExtensionState.NeedsRestart;
J
Joao Moreno 已提交
207

J
Joao Moreno 已提交
208
		this.enabled = canInstall && isInstalled && this.extension.outdated;
J
Joao Moreno 已提交
209 210 211 212
		this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass;
	}

	run(): TPromise<any> {
213
		return this.extensionsWorkbenchService.install(this.extension);
J
Joao Moreno 已提交
214 215
	}

J
Joao Moreno 已提交
216 217 218 219 220 221 222 223 224 225 226 227
	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
}

export class EnableAction extends Action {

	private static EnabledClass = 'extension-action enable';
	private static DisabledClass = `${ EnableAction.EnabledClass } disabled`;

	private disposables: IDisposable[] = [];
J
Joao Moreno 已提交
228 229 230
	private _extension: IExtension;
	get extension(): IExtension { return this._extension; }
	set extension(extension: IExtension) { this._extension = extension; this.update(); }
J
Joao Moreno 已提交
231 232 233 234 235 236 237

	constructor(
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
		@IInstantiationService private instantiationService: IInstantiationService
	) {
		super('extensions.enable', localize('enableAction', "Enable"), EnableAction.DisabledClass, false);

J
Joao Moreno 已提交
238 239
		this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
		this.update();
J
Joao Moreno 已提交
240 241
	}

J
Joao Moreno 已提交
242 243 244 245 246 247 248
	private update(): void {
		if (!this.extension) {
			this.enabled = false;
			this.class = EnableAction.DisabledClass;
			return;
		}

J
Joao Moreno 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261
		this.enabled = this.extension.state === ExtensionState.NeedsRestart;
		this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass;
	}

	run(): TPromise<any> {
		if (!window.confirm(localize('restart', "In order to enable this extension, this window of VS Code needs to be restarted.\n\nDo you want to continue?"))) {
			return TPromise.as(null);
		}

		const action = this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('restartNow', "Restart Now"));
		return action.run();
	}

J
Joao Moreno 已提交
262 263 264 265
	dispose(): void {
		super.dispose();
		this.disposables = dispose(this.disposables);
	}
266
}
J
Joao Moreno 已提交
267 268 269

export class UpdateAllAction extends Action {

270 271 272
	static ID = 'extensions.update-all';
	static LABEL = localize('updateAll', "Update All Extensions");

J
Joao Moreno 已提交
273 274 275
	private disposables: IDisposable[] = [];

	constructor(
276 277
		id = UpdateAllAction.ID,
		label = UpdateAllAction.LABEL,
J
Joao Moreno 已提交
278 279
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
280
		super(id, label, '', false);
J
Joao Moreno 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

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

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

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

	run(): TPromise<any> {
		return TPromise.join(this.outdated.map(e => this.extensionsWorkbenchService.install(e)));
	}

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

export class OpenExtensionsViewletAction extends ToggleViewletAction {

J
Joao Moreno 已提交
309
	static ID = VIEWLET_ID;
310 311 312 313 314 315 316 317 318 319 320 321
	static LABEL = localize('toggleExtensionsViewlet', "Show Extensions");

	constructor(
		id: string,
		label: string,
		@IViewletService viewletService: IViewletService,
		@IWorkbenchEditorService editorService: IWorkbenchEditorService
	) {
		super(id, label, VIEWLET_ID, viewletService, editorService);
	}
}

I
isidor 已提交
322 323 324 325 326
export class InstallExtensionsAction extends OpenExtensionsViewletAction {
	static ID = 'workbench.extensions.action.installExtensions';
	static LABEL = localize('installExtensions', "Install Extensions");
}

J
Joao Moreno 已提交
327
export class ShowInstalledExtensionsAction extends Action {
328

J
Joao Moreno 已提交
329 330
	static ID = 'workbench.extensions.action.showInstalledExtensions';
	static LABEL = localize('showInstalledExtensions', "Show Installed Extensions");
331 332 333 334

	constructor(
		id: string,
		label: string,
335 336
		@IViewletService private viewletService: IViewletService,
		@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
337
	) {
338
		super(id, label, 'clear-extensions', true);
339 340 341 342 343 344
	}

	run(): TPromise<void> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
345
				viewlet.search('');
346 347 348 349 350
				viewlet.focus();
			});
	}
}

J
Joao Moreno 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
export class ClearExtensionsInputAction extends ShowInstalledExtensionsAction {

	static ID = 'workbench.extensions.action.clearExtensionsInput';
	static LABEL = localize('clearExtensionsInput', "Clear Extensions Input");

	private disposables: IDisposable[] = [];

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
		@IViewletService viewletService: IViewletService,
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
		super(id, label, viewletService, extensionsWorkbenchService);
366
		this.enabled = false;
J
Joao Moreno 已提交
367 368 369 370 371 372 373 374 375 376 377 378
		onSearchChange(this.onSearchChange, this, this.disposables);
	}

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

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

J
Joao Moreno 已提交
379
export class ShowOutdatedExtensionsAction extends Action {
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396

	static ID = 'workbench.extensions.action.listOutdatedExtensions';
	static LABEL = localize('showOutdatedExtensions', "Show Outdated Extensions");

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

	run(): TPromise<void> {

		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
397
				viewlet.search('@outdated');
398 399 400 401 402 403 404 405
				viewlet.focus();
			});
	}

	protected isEnabled(): boolean {
		return true;
	}
}
J
Joao Moreno 已提交
406 407 408 409 410 411 412 413 414 415 416

export class ShowPopularExtensionsAction extends Action {

	static ID = 'workbench.extensions.action.showPopularExtensions';
	static LABEL = localize('showPopularExtensions', "Show Popular Extensions");

	constructor(
		id: string,
		label: string,
		@IViewletService private viewletService: IViewletService
	) {
417
		super(id, label, null, true);
J
Joao Moreno 已提交
418 419 420 421 422 423
	}

	run(): TPromise<void> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
424
				viewlet.search('@sort:installs');
J
Joao Moreno 已提交
425 426 427 428 429 430 431 432 433
				viewlet.focus();
			});
	}

	protected isEnabled(): boolean {
		return true;
	}
}

J
Joao Moreno 已提交
434
export class ShowRecommendedExtensionsAction extends Action {
J
Joao Moreno 已提交
435

J
Joao Moreno 已提交
436
	static ID = 'workbench.extensions.action.showRecommendedExtensions';
J
Joao Moreno 已提交
437
	static LABEL = localize('showRecommendedExtensions', "Show Recommended Extensions");
J
Joao Moreno 已提交
438 439 440 441 442 443

	constructor(
		id: string,
		label: string,
		@IViewletService private viewletService: IViewletService
	) {
444
		super(id, label, null, true);
J
Joao Moreno 已提交
445 446 447 448 449 450
	}

	run(): TPromise<void> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
451
				viewlet.search('@recommended');
J
Joao Moreno 已提交
452 453 454 455
				viewlet.focus();
			});
	}

J
Joao Moreno 已提交
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
	protected isEnabled(): boolean {
		return true;
	}
}

export class ChangeSortAction extends Action {

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

	constructor(
		id: string,
		label: string,
		onSearchChange: Event<string>,
		private sortBy: string,
		private sortOrder: string,
		@IViewletService private viewletService: IViewletService
	) {
		super(id, label, null, true);

J
Joao Moreno 已提交
476
		if (sortBy === undefined && sortOrder === undefined) {
J
Joao Moreno 已提交
477 478 479 480
			throw new Error('bad arguments');
		}

		this.query = Query.parse('');
481
		this.enabled = false;
J
Joao Moreno 已提交
482 483 484 485 486 487
		onSearchChange(this.onSearchChange, this, this.disposables);
	}

	private onSearchChange(value: string): void {
		const query = Query.parse(value);
		this.query = new Query(query.value, this.sortBy || query.sortBy, this.sortOrder || query.sortOrder);
488
		this.enabled = value && this.query.isValid() && !this.query.equals(query);
J
Joao Moreno 已提交
489 490 491 492 493 494 495 496 497 498 499
	}

	run(): TPromise<void> {
		return this.viewletService.openViewlet(VIEWLET_ID, true)
			.then(viewlet => viewlet as IExtensionsViewlet)
			.then(viewlet => {
				viewlet.search(this.query.toString());
				viewlet.focus();
			});
	}

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
	protected isEnabled(): boolean {
		return true;
	}
}

export class OpenExtensionsFolderAction extends Action {

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

	constructor(
		id: string,
		label: string,
		@IEnvironmentService private environmentService: IEnvironmentService
	) {
		super(id, label, null, true);
	}

	run(): TPromise<any> {
		const extensionsHome = this.environmentService.extensionsPath;
		shell.showItemInFolder(paths.normalize(extensionsHome, true));

		return TPromise.as(true);
	}

J
Joao Moreno 已提交
525 526 527
	protected isEnabled(): boolean {
		return true;
	}
528
}