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

'use strict';

8
import URI from 'vs/base/common/uri';
J
Johannes Rieken 已提交
9 10
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
J
Joao Moreno 已提交
11
import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser/windowService';
12
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
13 14 15
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorInput } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
E
Erich Gamma 已提交
16
import nls = require('vs/nls');
17 18
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
B
Benjamin Pasero 已提交
19
import errors = require('vs/base/common/errors');
J
Johannes Rieken 已提交
20 21 22 23 24 25 26
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IExtensionManagementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
B
Benjamin Pasero 已提交
27
import paths = require('vs/base/common/paths');
28
import { isMacintosh, isLinux } from 'vs/base/common/platform';
J
Johannes Rieken 已提交
29
import { IQuickOpenService, IFilePickOpenEntry, ISeparator } from 'vs/platform/quickOpen/common/quickOpen';
J
Johannes Rieken 已提交
30 31
import { KeyMod } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
32
import * as browser from 'vs/base/browser/browser';
J
Johannes Rieken 已提交
33
import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
B
Benjamin Pasero 已提交
34
import { IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
B
Benjamin Pasero 已提交
35
import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/common/timerService';
E
Erich Gamma 已提交
36

B
Benjamin Pasero 已提交
37
import * as os from 'os';
38
import { webFrame } from 'electron';
E
Erich Gamma 已提交
39

40 41
// --- actions

E
Erich Gamma 已提交
42 43 44 45 46 47 48 49
export class CloseEditorAction extends Action {

	public static ID = 'workbench.action.closeActiveEditor';
	public static LABEL = nls.localize('closeActiveEditor', "Close Editor");

	constructor(
		id: string,
		label: string,
50
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService
E
Erich Gamma 已提交
51 52 53 54
	) {
		super(id, label);
	}

55
	public run(): TPromise<any> {
B
Benjamin Pasero 已提交
56
		const activeEditor = this.editorService.getActiveEditor();
E
Erich Gamma 已提交
57
		if (activeEditor) {
58
			return this.editorService.closeEditor(activeEditor.position, activeEditor.input);
E
Erich Gamma 已提交
59 60
		}

A
Alex Dima 已提交
61
		return TPromise.as(false);
E
Erich Gamma 已提交
62 63 64 65 66 67 68 69
	}
}

export class CloseWindowAction extends Action {

	public static ID = 'workbench.action.closeWindow';
	public static LABEL = nls.localize('closeWindow', "Close Window");

J
Joao Moreno 已提交
70
	constructor(id: string, label: string, @IWindowIPCService private windowService: IWindowIPCService) {
E
Erich Gamma 已提交
71 72 73
		super(id, label);
	}

74
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
75 76
		this.windowService.getWindow().close();

A
Alex Dima 已提交
77
		return TPromise.as(true);
E
Erich Gamma 已提交
78 79 80
	}
}

81
export class SwitchWindow extends Action {
82

J
Joao Moreno 已提交
83 84
	static ID = 'workbench.action.switchWindow';
	static LABEL = nls.localize('switchWindow', "Switch Window");
85 86 87 88

	constructor(
		id: string,
		label: string,
J
Joao Moreno 已提交
89
		@IWindowsService private windowsService: IWindowsService,
J
Joao Moreno 已提交
90
		@IWindowService private windowService: IWindowService,
91 92 93 94 95
		@IQuickOpenService private quickOpenService: IQuickOpenService
	) {
		super(id, label);
	}

J
Joao Moreno 已提交
96 97
	run(): TPromise<void> {
		const currentWindowId = this.windowService.getCurrentWindowId();
98

J
Joao Moreno 已提交
99 100 101 102 103 104 105 106 107 108
		return this.windowsService.getWindows().then(workspaces => {
			const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window");
			const picks = workspaces.map(w => ({
				label: w.title,
				description: (currentWindowId === w.id) ? nls.localize('current', "Current Window") : void 0,
				run: () => this.windowsService.showWindow(w.id)
			}));

			this.quickOpenService.pick(picks, { placeHolder });
		});
109 110 111
	}
}

E
Erich Gamma 已提交
112 113
export class CloseFolderAction extends Action {

J
Joao Moreno 已提交
114 115
	static ID = 'workbench.action.closeFolder';
	static LABEL = nls.localize('closeFolder', "Close Folder");
E
Erich Gamma 已提交
116 117 118 119 120

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
B
Benjamin Pasero 已提交
121
		@IMessageService private messageService: IMessageService,
J
Joao Moreno 已提交
122
		@IWindowService private windowService: IWindowService
E
Erich Gamma 已提交
123 124 125 126
	) {
		super(id, label);
	}

J
Joao Moreno 已提交
127 128
	run(): TPromise<void> {
		if (!this.contextService.getWorkspace()) {
E
Erich Gamma 已提交
129
			this.messageService.show(Severity.Info, nls.localize('noFolderOpened', "There is currently no folder opened in this instance to close."));
J
Joao Moreno 已提交
130
			return TPromise.as(null);
E
Erich Gamma 已提交
131 132
		}

J
Joao Moreno 已提交
133
		return this.windowService.closeFolder();
E
Erich Gamma 已提交
134 135 136 137 138
	}
}

export class NewWindowAction extends Action {

J
Joao Moreno 已提交
139 140
	static ID = 'workbench.action.newWindow';
	static LABEL = nls.localize('newWindow', "New Window");
E
Erich Gamma 已提交
141

B
Benjamin Pasero 已提交
142 143 144
	constructor(
		id: string,
		label: string,
J
Joao Moreno 已提交
145
		@IWindowsService private windowsService: IWindowsService
B
Benjamin Pasero 已提交
146
	) {
E
Erich Gamma 已提交
147 148 149
		super(id, label);
	}

J
Joao Moreno 已提交
150 151
	run(): TPromise<void> {
		return this.windowsService.openNewWindow();
E
Erich Gamma 已提交
152 153 154 155 156
	}
}

export class ToggleFullScreenAction extends Action {

J
Joao Moreno 已提交
157 158
	static ID = 'workbench.action.toggleFullScreen';
	static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen");
E
Erich Gamma 已提交
159

J
Joao Moreno 已提交
160
	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
E
Erich Gamma 已提交
161 162 163
		super(id, label);
	}

J
Joao Moreno 已提交
164 165
	run(): TPromise<void> {
		return this.windowService.toggleFullScreen();
E
Erich Gamma 已提交
166 167 168
	}
}

169 170
export class ToggleMenuBarAction extends Action {

J
Joao Moreno 已提交
171 172
	static ID = 'workbench.action.toggleMenuBar';
	static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");
173

J
Joao Moreno 已提交
174
	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
175 176 177
		super(id, label);
	}

J
Joao Moreno 已提交
178 179
	run(): TPromise<void> {
		return this.windowService.toggleMenuBar();
180 181 182
	}
}

E
Erich Gamma 已提交
183 184
export class ToggleDevToolsAction extends Action {

J
Joao Moreno 已提交
185 186
	static ID = 'workbench.action.toggleDevTools';
	static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools");
E
Erich Gamma 已提交
187

J
Joao Moreno 已提交
188
	constructor(id: string, label: string, @IWindowService private windowsService: IWindowService) {
E
Erich Gamma 已提交
189 190 191
		super(id, label);
	}

B
Benjamin Pasero 已提交
192
	public run(): TPromise<void> {
J
Joao Moreno 已提交
193
		return this.windowsService.toggleDevTools();
E
Erich Gamma 已提交
194 195 196
	}
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
export abstract class BaseZoomAction extends Action {
	private static SETTING_KEY = 'window.zoomLevel';

	constructor(
		id: string,
		label: string,
		@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
		@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService
	) {
		super(id, label);
	}

	protected setConfiguredZoomLevel(level: number): void {
		let target = ConfigurationTarget.USER;
		if (typeof this.configurationService.lookup(BaseZoomAction.SETTING_KEY).workspace === 'number') {
			target = ConfigurationTarget.WORKSPACE;
		}

215 216
		level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels

B
Benjamin Pasero 已提交
217
		const applyZoom = () => {
218
			webFrame.setZoomLevel(level);
B
Benjamin Pasero 已提交
219
			browser.setZoomFactor(webFrame.getZoomFactor());
220
			browser.setZoomLevel(level); // Ensure others can listen to zoom level changes
221 222
		};

B
Benjamin Pasero 已提交
223
		this.configurationEditingService.writeConfiguration(target, { key: BaseZoomAction.SETTING_KEY, value: level }).done(() => applyZoom(), error => applyZoom());
224 225 226 227
	}
}

export class ZoomInAction extends BaseZoomAction {
E
Erich Gamma 已提交
228 229

	public static ID = 'workbench.action.zoomIn';
B
Benjamin Pasero 已提交
230
	public static LABEL = nls.localize('zoomIn', "Zoom In");
E
Erich Gamma 已提交
231

232 233 234 235 236 237
	constructor(
		id: string,
		label: string,
		@IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService,
		@IConfigurationEditingService configurationEditingService: IConfigurationEditingService
	) {
B
Benjamin Pasero 已提交
238
		super(id, label, configurationService, configurationEditingService);
E
Erich Gamma 已提交
239 240
	}

241
	public run(): TPromise<boolean> {
242
		this.setConfiguredZoomLevel(webFrame.getZoomLevel() + 1);
E
Erich Gamma 已提交
243

A
Alex Dima 已提交
244
		return TPromise.as(true);
E
Erich Gamma 已提交
245 246 247
	}
}

248
export class ZoomOutAction extends BaseZoomAction {
E
Erich Gamma 已提交
249 250

	public static ID = 'workbench.action.zoomOut';
B
Benjamin Pasero 已提交
251
	public static LABEL = nls.localize('zoomOut', "Zoom Out");
E
Erich Gamma 已提交
252

253 254
	constructor(
		id: string,
255 256 257
		label: string,
		@IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService,
		@IConfigurationEditingService configurationEditingService: IConfigurationEditingService
258
	) {
B
Benjamin Pasero 已提交
259
		super(id, label, configurationService, configurationEditingService);
E
Erich Gamma 已提交
260 261
	}

262
	public run(): TPromise<boolean> {
263
		this.setConfiguredZoomLevel(webFrame.getZoomLevel() - 1);
264

265
		return TPromise.as(true);
E
Erich Gamma 已提交
266 267 268
	}
}

269
export class ZoomResetAction extends BaseZoomAction {
E
Erich Gamma 已提交
270 271 272 273

	public static ID = 'workbench.action.zoomReset';
	public static LABEL = nls.localize('zoomReset', "Reset Zoom");

274 275 276
	constructor(
		id: string,
		label: string,
277 278
		@IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService,
		@IConfigurationEditingService configurationEditingService: IConfigurationEditingService
279
	) {
B
Benjamin Pasero 已提交
280
		super(id, label, configurationService, configurationEditingService);
E
Erich Gamma 已提交
281 282
	}

283
	public run(): TPromise<boolean> {
284
		this.setConfiguredZoomLevel(0);
285

286
		return TPromise.as(true);
E
Erich Gamma 已提交
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
	}
}

/* Copied from loader.ts */
enum LoaderEventType {
	LoaderAvailable = 1,

	BeginLoadingScript = 10,
	EndLoadingScriptOK = 11,
	EndLoadingScriptError = 12,

	BeginInvokeFactory = 21,
	EndInvokeFactory = 22,

	NodeBeginEvaluatingScript = 31,
	NodeEndEvaluatingScript = 32,

	NodeBeginNativeRequire = 33,
	NodeEndNativeRequire = 34
}
307

E
Erich Gamma 已提交
308 309 310 311 312
interface ILoaderEvent {
	type: LoaderEventType;
	timestamp: number;
	detail: string;
}
313

E
Erich Gamma 已提交
314 315 316 317 318
export class ShowStartupPerformance extends Action {

	public static ID = 'workbench.action.appPerf';
	public static LABEL = nls.localize('appPerf', "Startup Performance");

319 320 321
	constructor(
		id: string,
		label: string,
J
Joao Moreno 已提交
322
		@IWindowService private windowService: IWindowService,
B
Benjamin Pasero 已提交
323
		@ITimerService private timerService: ITimerService,
324
		@IEnvironmentService private environmentService: IEnvironmentService
325
	) {
E
Erich Gamma 已提交
326
		super(id, label);
327 328 329 330 331 332 333 334 335
	}

	public run(): TPromise<boolean> {

		// Show dev tools
		this.windowService.openDevTools();

		// Print to console
		setTimeout(() => {
336
			(<any>console).group('Startup Performance Measurement');
B
Benjamin Pasero 已提交
337 338 339 340 341 342 343 344
			const metrics: IStartupMetrics = this.timerService.startupMetrics;
			console.log(`OS: ${metrics.platform} (${metrics.release})`);
			console.log(`CPUs: ${metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed})`);
			console.log(`Memory (System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)}GB (${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`);
			console.log(`Memory (Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)}MB working set (${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`);
			console.log(`Initial Startup: ${metrics.initialStartup}`);
			console.log(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`);
			console.log(`Empty Workspace: ${metrics.emptyWorkbench}`);
345 346 347 348 349 350 351 352 353

			let nodeModuleLoadTime: number;
			let nodeModuleLoadDetails: any[];
			if (this.environmentService.performance) {
				const nodeModuleTimes = this.analyzeNodeModulesLoadTimes();
				nodeModuleLoadTime = nodeModuleTimes.duration;
				nodeModuleLoadDetails = nodeModuleTimes.table;
			}

B
Benjamin Pasero 已提交
354
			(<any>console).table(this.getStartupMetricsTable(nodeModuleLoadTime));
355 356 357 358 359 360

			if (nodeModuleLoadDetails) {
				(<any>console).groupCollapsed('node_modules Load Details');
				(<any>console).table(nodeModuleLoadDetails);
				(<any>console).groupEnd();
			}
361 362
			(<any>console).groupEnd();
		}, 1000);
363

364
		return TPromise.as(true);
E
Erich Gamma 已提交
365 366
	}

B
Benjamin Pasero 已提交
367
	private getStartupMetricsTable(nodeModuleLoadTime?: number): any[] {
368
		const table: any[] = [];
B
Benjamin Pasero 已提交
369
		const metrics: IStartupMetrics = this.timerService.startupMetrics;
370

B
Benjamin Pasero 已提交
371 372
		if (metrics.initialStartup) {
			table.push({ Topic: '[main] start => window.loadUrl()', 'Took (ms)': metrics.timers.ellapsedWindowLoad });
E
Erich Gamma 已提交
373
		}
374

B
Benjamin Pasero 已提交
375 376
		table.push({ Topic: '[renderer] window.loadUrl() => begin to require(workbench.main.js)', 'Took (ms)': metrics.timers.ellapsedWindowLoadToRequire });
		table.push({ Topic: '[renderer] require(workbench.main.js)', 'Took (ms)': metrics.timers.ellapsedRequire });
377 378 379 380 381

		if (nodeModuleLoadTime) {
			table.push({ Topic: '[renderer] -> of which require() node_modules', 'Took (ms)': nodeModuleLoadTime });
		}

B
Benjamin Pasero 已提交
382 383 384 385
		table.push({ Topic: '[renderer] create extension host => extensions onReady()', 'Took (ms)': metrics.timers.ellapsedExtensions });
		table.push({ Topic: '[renderer] restore viewlet', 'Took (ms)': metrics.timers.ellapsedViewletRestore });
		table.push({ Topic: '[renderer] restore editor view state', 'Took (ms)': metrics.timers.ellapsedEditorRestore });
		table.push({ Topic: '[renderer] overall workbench load', 'Took (ms)': metrics.timers.ellapsedWorkbench });
386
		table.push({ Topic: '------------------------------------------------------' });
B
Benjamin Pasero 已提交
387 388
		table.push({ Topic: '[main, renderer] start => extensions ready', 'Took (ms)': metrics.timers.ellapsedExtensionsReady });
		table.push({ Topic: '[main, renderer] start => workbench ready', 'Took (ms)': metrics.ellapsed });
E
Erich Gamma 已提交
389

390
		return table;
E
Erich Gamma 已提交
391 392
	}

393
	private analyzeNodeModulesLoadTimes(): { table: any[], duration: number } {
394 395 396 397 398 399 400 401 402
		const stats = <ILoaderEvent[]>(<any>require).getStats();
		const result = [];

		let total = 0;

		for (let i = 0, len = stats.length; i < len; i++) {
			if (stats[i].type === LoaderEventType.NodeEndNativeRequire) {
				if (stats[i - 1].type === LoaderEventType.NodeBeginNativeRequire && stats[i - 1].detail === stats[i].detail) {
					const entry: any = {};
403
					const dur = (stats[i].timestamp - stats[i - 1].timestamp);
404
					entry['Event'] = 'nodeRequire ' + stats[i].detail;
405 406 407 408
					entry['Took (ms)'] = dur.toFixed(2);
					total += dur;
					entry['Start (ms)'] = '**' + stats[i - 1].timestamp.toFixed(2);
					entry['End (ms)'] = '**' + stats[i - 1].timestamp.toFixed(2);
409 410 411 412 413 414
					result.push(entry);
				}
			}
		}

		if (total > 0) {
415 416
			result.push({ Event: '------------------------------------------------------' });

417
			const entry: any = {};
418 419
			entry['Event'] = '[renderer] total require() node_modules';
			entry['Took (ms)'] = total.toFixed(2);
420 421 422 423 424
			entry['Start (ms)'] = '**';
			entry['End (ms)'] = '**';
			result.push(entry);
		}

425
		return { table: result, duration: Math.round(total) };
E
Erich Gamma 已提交
426 427 428 429 430
	}
}

export class ReloadWindowAction extends Action {

431 432
	static ID = 'workbench.action.reloadWindow';
	static LABEL = nls.localize('reloadWindow', "Reload Window");
E
Erich Gamma 已提交
433

434 435 436
	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
437
		@IWindowService private windowService: IWindowService
438
	) {
E
Erich Gamma 已提交
439 440 441
		super(id, label);
	}

442 443
	run(): TPromise<boolean> {
		return this.windowService.reloadWindow().then(() => true);
E
Erich Gamma 已提交
444 445 446 447 448 449 450 451 452 453 454
	}
}

export class OpenRecentAction extends Action {

	public static ID = 'workbench.action.openRecent';
	public static LABEL = nls.localize('openRecent', "Open Recent");

	constructor(
		id: string,
		label: string,
455
		@IWindowsService private windowsService: IWindowsService,
J
Joao Moreno 已提交
456
		@IWindowService private windowService: IWindowService,
B
Benjamin Pasero 已提交
457 458
		@IQuickOpenService private quickOpenService: IQuickOpenService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService
E
Erich Gamma 已提交
459 460 461 462
	) {
		super(id, label);
	}

J
Joao Moreno 已提交
463 464 465
	public run(): TPromise<void> {
		return this.windowService.getRecentlyOpen()
			.then(({ files, folders }) => this.openRecent(files, folders));
B
Benjamin Pasero 已提交
466 467 468
	}

	private openRecent(recentFiles: string[], recentFolders: string[]): void {
B
Benjamin Pasero 已提交
469
		function toPick(path: string, separator: ISeparator, isFolder: boolean): IFilePickOpenEntry {
B
Benjamin Pasero 已提交
470
			return {
B
Benjamin Pasero 已提交
471 472
				resource: URI.file(path),
				isFolder,
B
Benjamin Pasero 已提交
473 474 475
				label: paths.basename(path),
				description: paths.dirname(path),
				separator,
B
Benjamin Pasero 已提交
476
				run: context => runPick(path, context)
B
Benjamin Pasero 已提交
477 478 479
			};
		}

B
Benjamin Pasero 已提交
480
		const runPick = (path: string, context: IEntryRunContext) => {
B
Benjamin Pasero 已提交
481
			const newWindow = context.keymods.indexOf(KeyMod.CtrlCmd) >= 0;
482 483
			this.windowsService.windowOpen([path], newWindow);
		};
B
Benjamin Pasero 已提交
484

B
Benjamin Pasero 已提交
485 486
		const folderPicks: IFilePickOpenEntry[] = recentFolders.map((p, index) => toPick(p, index === 0 ? { label: nls.localize('folders', "folders") } : void 0, true));
		const filePicks: IFilePickOpenEntry[] = recentFiles.map((p, index) => toPick(p, index === 0 ? { label: nls.localize('files', "files"), border: true } : void 0, false));
B
Benjamin Pasero 已提交
487 488 489 490 491 492 493 494

		const hasWorkspace = !!this.contextService.getWorkspace();

		this.quickOpenService.pick(folderPicks.concat(...filePicks), {
			autoFocus: { autoFocusFirstEntry: !hasWorkspace, autoFocusSecondEntry: hasWorkspace },
			placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select a path (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select a path to open (hold Ctrl-key to open in new window)"),
			matchOnDescription: true
		}).done(null, errors.onUnexpectedError);
E
Erich Gamma 已提交
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
	}
}

export class CloseMessagesAction extends Action {

	public static ID = 'workbench.action.closeMessages';
	public static LABEL = nls.localize('closeMessages', "Close Notification Messages");

	constructor(
		id: string,
		label: string,
		@IMessageService private messageService: IMessageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService
	) {
		super(id, label);
	}

512
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
513 514 515 516 517 518 519 520 521 522

		// Close any Message if visible
		this.messageService.hideAll();

		// Restore focus if we got an editor
		const editor = this.editorService.getActiveEditor();
		if (editor) {
			editor.focus();
		}

523
		return TPromise.as(true);
E
Erich Gamma 已提交
524
	}
525 526
}

B
Benjamin Pasero 已提交
527 528 529 530 531 532 533 534 535
export class ReportIssueAction extends Action {

	public static ID = 'workbench.action.reportIssues';
	public static LABEL = nls.localize('reportIssues', "Report Issues");

	constructor(
		id: string,
		label: string,
		@IIntegrityService private integrityService: IIntegrityService,
B
Benjamin Pasero 已提交
536
		@IExtensionManagementService private extensionManagementService: IExtensionManagementService
B
Benjamin Pasero 已提交
537 538 539 540 541 542
	) {
		super(id, label);
	}

	public run(): TPromise<boolean> {
		return this.integrityService.isPure().then(res => {
B
Benjamin Pasero 已提交
543 544
			return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
				const issueUrl = this.generateNewIssueUrl(product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, res.isPure, extensions);
B
Benjamin Pasero 已提交
545

B
Benjamin Pasero 已提交
546
				window.open(issueUrl);
B
Benjamin Pasero 已提交
547

B
Benjamin Pasero 已提交
548 549
				return TPromise.as(true);
			});
B
Benjamin Pasero 已提交
550 551 552
		});
	}

J
Johannes Rieken 已提交
553
	private generateNewIssueUrl(baseUrl: string, name: string, version: string, commit: string, date: string, isPure: boolean, extensions: ILocalExtension[]): string {
C
Christof Marti 已提交
554
		// Avoid backticks, these can trigger XSS detectors. (https://github.com/Microsoft/vscode/issues/13098)
B
Benjamin Pasero 已提交
555 556 557 558 559
		const osVersion = `${os.type()} ${os.arch()} ${os.release()}`;
		const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
		const body = encodeURIComponent(
			`- VSCode Version: ${name} ${version}${isPure ? '' : ' **[Unsupported]**'} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})
- OS Version: ${osVersion}
560 561 562 563 564
- Extensions:

${this.generateExtensionTable(extensions)}

---
B
Benjamin Pasero 已提交
565 566 567 568 569 570 571 572 573

Steps to Reproduce:

1.
2.`
		);

		return `${baseUrl}${queryStringPrefix}body=${body}`;
	}
574 575

	private generateExtensionTable(extensions: ILocalExtension[]): string {
576 577
		let tableHeader = `|Extension|Author|Version|
|---|---|---|`;
578
		const table = extensions.map(e => {
579
			return `|${e.manifest.name}|${e.manifest.publisher}|${e.manifest.version}|`;
580 581
		}).join('\n');

B
Benjamin Pasero 已提交
582
		return `${tableHeader}\n${table}`;
583
	}
B
Benjamin Pasero 已提交
584 585
}

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
export class KeybindingsReferenceAction extends Action {

	public static ID = 'workbench.action.keybindingsReference';
	public static LABEL = nls.localize('keybindingsReference', "Keyboard Shortcuts Reference");

	private static URL = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin;
	public static AVAILABLE = !!KeybindingsReferenceAction.URL;

	constructor(
		id: string,
		label: string
	) {
		super(id, label);
	}

	public run(): TPromise<void> {
		window.open(KeybindingsReferenceAction.URL);
		return null;
	}
}

607 608
// --- commands

609 610 611
CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string]) {
	const editorService = accessor.get(IWorkbenchEditorService);
	let [left, right, label] = args;
612

613 614 615
	if (!label) {
		label = nls.localize('diffLeftRightLabel', "{0} ⟷ {1}", left.toString(true), right.toString(true));
	}
616

617 618
	return TPromise.join([editorService.createInput({ resource: left }), editorService.createInput({ resource: right })]).then(inputs => {
		const [left, right] = inputs;
619

B
💄  
Benjamin Pasero 已提交
620
		const diff = new DiffEditorInput(label, void 0, <EditorInput>left, <EditorInput>right);
621 622 623 624
		return editorService.openEditor(diff);
	}).then(() => {
		return void 0;
	});
625 626
});

627 628
CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, number]) {
	const editorService = accessor.get(IWorkbenchEditorService);
B
Benjamin Pasero 已提交
629
	const [resource, column] = args;
630

631 632 633
	return editorService.openEditor({ resource }, column).then(() => {
		return void 0;
	});
634
});