actions.ts 13.7 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';
9
import {TPromise} from 'vs/base/common/winjs.base';
E
Erich Gamma 已提交
10 11 12 13 14
import timer = require('vs/base/common/timer');
import paths = require('vs/base/common/paths');
import {Action} from 'vs/base/common/actions';
import {IWindowService} from 'vs/workbench/services/window/electron-browser/windowService';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
15 16
import {EditorInput} from 'vs/workbench/common/editor';
import {DiffEditorInput} from 'vs/workbench/common/editor/diffEditorInput';
E
Erich Gamma 已提交
17 18
import nls = require('vs/nls');
import {IMessageService, Severity} from 'vs/platform/message/common/message';
19
import {IWindowConfiguration} from 'vs/workbench/electron-browser/window';
E
Erich Gamma 已提交
20
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
21
import {IQuickOpenService} from 'vs/workbench/services/quickopen/common/quickOpenService';
22
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
23 24
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
import {ServicesAccessor} from 'vs/platform/instantiation/common/instantiation';
25
import * as browser from 'vs/base/browser/browser';
E
Erich Gamma 已提交
26

B
Benjamin Pasero 已提交
27
import {ipcRenderer as ipc, webFrame, remote} from 'electron';
E
Erich Gamma 已提交
28

29 30
// --- actions

E
Erich Gamma 已提交
31 32 33 34 35 36 37 38 39 40 41 42 43 44
export class CloseEditorAction extends Action {

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

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

45
	public run(): TPromise<any> {
E
Erich Gamma 已提交
46 47
		let activeEditor = this.editorService.getActiveEditor();
		if (activeEditor) {
48
			return this.editorService.closeEditor(activeEditor.position, activeEditor.input);
E
Erich Gamma 已提交
49 50 51 52
		}

		this.windowService.getWindow().close();

A
Alex Dima 已提交
53
		return TPromise.as(false);
E
Erich Gamma 已提交
54 55 56 57 58 59 60 61 62 63 64 65
	}
}

export class CloseWindowAction extends Action {

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

	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
		super(id, label);
	}

66
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
67 68
		this.windowService.getWindow().close();

A
Alex Dima 已提交
69
		return TPromise.as(true);
E
Erich Gamma 已提交
70 71 72 73 74 75 76 77 78 79 80 81
	}
}

export class CloseFolderAction extends Action {

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

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
B
Benjamin Pasero 已提交
82 83
		@IMessageService private messageService: IMessageService,
		@IWindowService private windowService: IWindowService
E
Erich Gamma 已提交
84 85 86 87
	) {
		super(id, label);
	}

88
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
89
		if (this.contextService.getWorkspace()) {
B
Benjamin Pasero 已提交
90
			ipc.send('vscode:closeFolder', this.windowService.getWindowId()); // handled from browser process
E
Erich Gamma 已提交
91 92 93 94
		} else {
			this.messageService.show(Severity.Info, nls.localize('noFolderOpened', "There is currently no folder opened in this instance to close."));
		}

A
Alex Dima 已提交
95
		return TPromise.as(true);
E
Erich Gamma 已提交
96 97 98 99 100 101 102 103
	}
}

export class NewWindowAction extends Action {

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

B
Benjamin Pasero 已提交
104 105 106 107 108
	constructor(
		id: string,
		label: string,
		@IWindowService private windowService: IWindowService
	) {
E
Erich Gamma 已提交
109 110 111
		super(id, label);
	}

112
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
113 114
		this.windowService.getWindow().openNew();

A
Alex Dima 已提交
115
		return TPromise.as(true);
E
Erich Gamma 已提交
116 117 118 119 120 121 122 123 124 125 126 127
	}
}

export class ToggleFullScreenAction extends Action {

	public static ID = 'workbench.action.toggleFullScreen';
	public static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen");

	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
		super(id, label);
	}

128
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
129 130
		ipc.send('vscode:toggleFullScreen', this.windowService.getWindowId());

A
Alex Dima 已提交
131
		return TPromise.as(true);
E
Erich Gamma 已提交
132 133 134
	}
}

135 136 137 138 139 140 141 142 143
export class ToggleMenuBarAction extends Action {

	public static ID = 'workbench.action.toggleMenuBar';
	public static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");

	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
		super(id, label);
	}

144
	public run(): TPromise<boolean> {
145 146
		ipc.send('vscode:toggleMenuBar', this.windowService.getWindowId());

A
Alex Dima 已提交
147
		return TPromise.as(true);
148 149 150
	}
}

E
Erich Gamma 已提交
151 152 153 154 155
export class ToggleDevToolsAction extends Action {

	public static ID = 'workbench.action.toggleDevTools';
	public static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools");

156
	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
E
Erich Gamma 已提交
157 158 159
		super(id, label);
	}

160
	public run(): TPromise<boolean> {
161
		ipc.send('vscode:toggleDevTools', this.windowService.getWindowId());
E
Erich Gamma 已提交
162

A
Alex Dima 已提交
163
		return TPromise.as(true);
E
Erich Gamma 已提交
164 165 166 167 168 169 170 171
	}
}

export class ZoomInAction extends Action {

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

172
	constructor(id: string, label: string) {
E
Erich Gamma 已提交
173 174 175
		super(id, label);
	}

176
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
177
		webFrame.setZoomLevel(webFrame.getZoomLevel() + 1);
B
Benjamin Pasero 已提交
178
		browser.setZoomLevel(webFrame.getZoomLevel()); // Ensure others can listen to zoom level changes
E
Erich Gamma 已提交
179

A
Alex Dima 已提交
180
		return TPromise.as(true);
E
Erich Gamma 已提交
181 182 183
	}
}

B
Benjamin Pasero 已提交
184
export class ZoomOutAction extends Action {
E
Erich Gamma 已提交
185 186 187 188

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

189 190
	constructor(
		id: string,
B
Benjamin Pasero 已提交
191
		label: string
192
	) {
B
Benjamin Pasero 已提交
193
		super(id, label);
E
Erich Gamma 已提交
194 195
	}

196
	public run(): TPromise<boolean> {
B
Benjamin Pasero 已提交
197 198
		webFrame.setZoomLevel(webFrame.getZoomLevel() - 1);
		browser.setZoomLevel(webFrame.getZoomLevel()); // Ensure others can listen to zoom level changes
199

200
		return TPromise.as(true);
E
Erich Gamma 已提交
201 202 203
	}
}

B
Benjamin Pasero 已提交
204
export class ZoomResetAction extends Action {
E
Erich Gamma 已提交
205 206 207 208

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

209 210 211
	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
212
		@IConfigurationService private configurationService: IConfigurationService
213
	) {
B
Benjamin Pasero 已提交
214
		super(id, label);
E
Erich Gamma 已提交
215 216
	}

217
	public run(): TPromise<boolean> {
218 219
		const level = this.getConfiguredZoomLevel();
		webFrame.setZoomLevel(level);
B
Benjamin Pasero 已提交
220
		browser.setZoomLevel(webFrame.getZoomLevel()); // Ensure others can listen to zoom level changes
221

222
		return TPromise.as(true);
E
Erich Gamma 已提交
223
	}
B
Benjamin Pasero 已提交
224 225 226 227 228 229 230 231 232

	private getConfiguredZoomLevel(): number {
		const windowConfig = this.configurationService.getConfiguration<IWindowConfiguration>();
		if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') {
			return windowConfig.window.zoomLevel;
		}

		return 0; // default
	}
E
Erich Gamma 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
}

/* 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
}
interface ILoaderEvent {
	type: LoaderEventType;
	timestamp: number;
	detail: string;
}
export class ShowStartupPerformance extends Action {

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

262 263 264 265 266 267
	constructor(
		id: string,
		label: string,
		@IWindowService private windowService: IWindowService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService
	) {
E
Erich Gamma 已提交
268
		super(id, label);
269 270

		this.enabled = contextService.getConfiguration().env.enablePerformance;
E
Erich Gamma 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
	}

	private _analyzeLoaderTimes(): any[] {
		let stats = <ILoaderEvent[]>(<any>require).getStats();
		let 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) {
					let entry: any = {};
					entry['Event'] = 'nodeRequire ' + stats[i].detail;
					entry['Took (ms)'] = (stats[i].timestamp - stats[i - 1].timestamp);
					total += (stats[i].timestamp - stats[i - 1].timestamp);
					entry['Start (ms)'] = '**' + stats[i - 1].timestamp;
					entry['End (ms)'] = '**' + stats[i - 1].timestamp;
					result.push(entry);
				}
			}
		}

		if (total > 0) {
			let entry: any = {};
			entry['Event'] = '===nodeRequire TOTAL';
			entry['Took (ms)'] = total;
			entry['Start (ms)'] = '**';
			entry['End (ms)'] = '**';
			result.push(entry);
		}

		return result;
	}

305
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
		let table: any[] = [];
		table.push(...this._analyzeLoaderTimes());

		let start = Math.round(remote.getGlobal('programStart') || remote.getGlobal('vscodeStart'));
		let windowShowTime = Math.round(remote.getGlobal('windowShow'));

		let lastEvent: timer.ITimerEvent;
		let events = timer.getTimeKeeper().getCollectedEvents();
		events.forEach((e) => {
			if (e.topic === 'Startup') {
				lastEvent = e;
				let entry: any = {};

				entry['Event'] = e.name;
				entry['Took (ms)'] = e.stopTime.getTime() - e.startTime.getTime();
				entry['Start (ms)'] = Math.max(e.startTime.getTime() - start, 0);
				entry['End (ms)'] = e.stopTime.getTime() - start;

				table.push(entry);
			}
		});

		table.push({ Event: '---------------------------' });

		let windowShowEvent: any = {};
		windowShowEvent['Event'] = 'Show Window at';
		windowShowEvent['Start (ms)'] = windowShowTime - start;
		table.push(windowShowEvent);

		let sum: any = {};
		sum['Event'] = 'Total';
		sum['Took (ms)'] = lastEvent.stopTime.getTime() - start;
		table.push(sum);


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

		// Print to console
		setTimeout(() => {
			console.warn('Run the action again if you do not see the numbers!');
			(<any>console).table(table);
		}, 1000);

A
Alex Dima 已提交
350
		return TPromise.as(true);
E
Erich Gamma 已提交
351 352 353 354 355 356 357 358 359 360 361 362
	}
}

export class ReloadWindowAction extends Action {

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

	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
		super(id, label);
	}

363
	public run(): TPromise<boolean> {
364
		this.windowService.getWindow().reload();
E
Erich Gamma 已提交
365

366
		return TPromise.as(true);
E
Erich Gamma 已提交
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
	}
}

export class OpenRecentAction extends Action {

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

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IQuickOpenService private quickOpenService: IQuickOpenService
	) {
		super(id, label);
	}

384
	public run(): TPromise<boolean> {
385 386 387 388 389 390 391 392 393 394 395 396 397
		const recentFolders = this.contextService.getConfiguration().env.recentFolders;
		const recentFiles = this.contextService.getConfiguration().env.recentFiles;

		let folderPicks = recentFolders.map((p, index) => {
			return {
				label: paths.basename(p),
				description: paths.dirname(p),
				path: p,
				separator: index === 0 ? { label: nls.localize('folders', "folders") } : void 0
			};
		});

		let filePicks = recentFiles.map((p, index) => {
E
Erich Gamma 已提交
398 399 400
			return {
				label: paths.basename(p),
				description: paths.dirname(p),
401 402
				path: p,
				separator: index === 0 ? { label: nls.localize('files', "files"), border: true } : void 0
B
Benjamin Pasero 已提交
403
			};
E
Erich Gamma 已提交
404 405
		});

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

408
		return this.quickOpenService.pick(folderPicks.concat(...filePicks), {
409
			autoFocus: { autoFocusFirstEntry: !hasWorkspace, autoFocusSecondEntry: hasWorkspace },
E
Erich Gamma 已提交
410 411 412 413 414 415
			placeHolder: nls.localize('openRecentPlaceHolder', "Select a path to open"),
			matchOnDescription: true
		}).then(p => {
			if (p) {
				ipc.send('vscode:windowOpen', [p.path]);
			}
416 417

			return true;
E
Erich Gamma 已提交
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
		});
	}
}

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

436
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
437 438 439 440 441 442 443 444 445 446

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

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

447
		return TPromise.as(true);
E
Erich Gamma 已提交
448
	}
449 450 451 452 453 454 455
}

// --- commands

KeybindingsRegistry.registerCommandDesc({
	id: '_workbench.ipc',
	weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
B
Benjamin Pasero 已提交
456
	handler(accessor: ServicesAccessor, ipcMessage: string, ipcArgs: any[]) {
457 458 459 460 461 462
		if (ipcMessage && Array.isArray(ipcArgs)) {
			ipc.send(ipcMessage, ...ipcArgs);
		} else {
			ipc.send(ipcMessage);
		}
	},
463
	when: undefined,
464
	primary: undefined
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
});

KeybindingsRegistry.registerCommandDesc({
	id: '_workbench.diff',
	weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
	handler(accessor: ServicesAccessor, args: [URI, URI, string]) {

		const editorService = accessor.get(IWorkbenchEditorService);
		let [left, right, label] = args;

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

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

			const diff = new DiffEditorInput(label, undefined, <EditorInput>left, <EditorInput>right);
			return editorService.openEditor(diff);
		}).then(() => {
			return void 0;
		});
	},
	when: undefined,
	primary: undefined
490
});