actions.ts 20.6 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
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
E
Erich Gamma 已提交
14
import nls = require('vs/nls');
15 16
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
B
Benjamin Pasero 已提交
17
import errors = require('vs/base/common/errors');
J
Johannes Rieken 已提交
18 19 20 21 22 23 24
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 已提交
25
import paths = require('vs/base/common/paths');
26
import { isMacintosh, isLinux } from 'vs/base/common/platform';
J
Johannes Rieken 已提交
27
import { IQuickOpenService, IFilePickOpenEntry, ISeparator } from 'vs/platform/quickOpen/common/quickOpen';
J
Johannes Rieken 已提交
28 29
import { KeyMod } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
30
import * as browser from 'vs/base/browser/browser';
J
Johannes Rieken 已提交
31
import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
B
Benjamin Pasero 已提交
32
import { IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
B
Benjamin Pasero 已提交
33
import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/common/timerService';
E
Erich Gamma 已提交
34

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

38 39
// --- actions

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

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

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

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

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

export class CloseWindowAction extends Action {

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

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

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

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

79
export class SwitchWindow extends Action {
80

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

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

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

J
Joao Moreno 已提交
97 98 99 100 101 102 103 104 105 106
		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 });
		});
107 108 109
	}
}

E
Erich Gamma 已提交
110 111
export class CloseFolderAction extends Action {

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

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

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

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

export class NewWindowAction extends Action {

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

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

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

export class ToggleFullScreenAction extends Action {

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

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

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

167 168
export class ToggleMenuBarAction extends Action {

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

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

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

E
Erich Gamma 已提交
181 182
export class ToggleDevToolsAction extends Action {

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

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

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

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
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;
		}

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

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

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

export class ZoomInAction extends BaseZoomAction {
E
Erich Gamma 已提交
226 227

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

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

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

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

246
export class ZoomOutAction extends BaseZoomAction {
E
Erich Gamma 已提交
247 248

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

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

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

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

267
export class ZoomResetAction extends BaseZoomAction {
E
Erich Gamma 已提交
268 269 270 271

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

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

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

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

/* 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
}
305

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

E
Erich Gamma 已提交
312 313 314 315 316
export class ShowStartupPerformance extends Action {

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

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

	public run(): TPromise<boolean> {

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

		// Print to console
		setTimeout(() => {
334
			(<any>console).group('Startup Performance Measurement');
B
Benjamin Pasero 已提交
335 336 337 338 339 340 341 342
			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}`);
343 344 345 346 347 348 349 350 351

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

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

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

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

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

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

B
Benjamin Pasero 已提交
373 374
		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 });
375 376 377 378 379

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

B
Benjamin Pasero 已提交
380 381 382 383
		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 });
384
		table.push({ Topic: '------------------------------------------------------' });
B
Benjamin Pasero 已提交
385 386
		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 已提交
387

388
		return table;
E
Erich Gamma 已提交
389 390
	}

391
	private analyzeNodeModulesLoadTimes(): { table: any[], duration: number } {
392 393 394 395 396 397 398 399 400
		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 = {};
401
					const dur = (stats[i].timestamp - stats[i - 1].timestamp);
402
					entry['Event'] = 'nodeRequire ' + stats[i].detail;
403 404 405 406
					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);
407 408 409 410 411 412
					result.push(entry);
				}
			}
		}

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

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

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

export class ReloadWindowAction extends Action {

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

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

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

export class OpenRecentAction extends Action {

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

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

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

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

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

B
Benjamin Pasero 已提交
483 484
		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 已提交
485 486 487 488 489 490 491 492

		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 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	}
}

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

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

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

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

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

B
Benjamin Pasero 已提交
525 526 527 528 529 530 531 532 533
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 已提交
534
		@IExtensionManagementService private extensionManagementService: IExtensionManagementService
B
Benjamin Pasero 已提交
535 536 537 538 539 540
	) {
		super(id, label);
	}

	public run(): TPromise<boolean> {
		return this.integrityService.isPure().then(res => {
B
Benjamin Pasero 已提交
541 542
			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 已提交
543

B
Benjamin Pasero 已提交
544
				window.open(issueUrl);
B
Benjamin Pasero 已提交
545

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

J
Johannes Rieken 已提交
551
	private generateNewIssueUrl(baseUrl: string, name: string, version: string, commit: string, date: string, isPure: boolean, extensions: ILocalExtension[]): string {
C
Christof Marti 已提交
552
		// Avoid backticks, these can trigger XSS detectors. (https://github.com/Microsoft/vscode/issues/13098)
B
Benjamin Pasero 已提交
553 554 555 556 557
		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}
558 559 560 561 562
- Extensions:

${this.generateExtensionTable(extensions)}

---
B
Benjamin Pasero 已提交
563 564 565 566 567 568 569 570 571

Steps to Reproduce:

1.
2.`
		);

		return `${baseUrl}${queryStringPrefix}body=${body}`;
	}
572 573

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

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

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
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;
	}
}

605 606
// --- commands

607
CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string]) {
608
	const editorService = accessor.get(IWorkbenchEditorService);
609
	let [leftResource, rightResource, label, description] = args;
610

611
	if (!label) {
612
		label = nls.localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true));
613
	}
614

615
	return editorService.openEditor({ leftResource, rightResource, label, description }).then(() => {
616 617
		return void 0;
	});
618 619
});

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

624 625 626
	return editorService.openEditor({ resource }, column).then(() => {
		return void 0;
	});
627
});