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

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

26 27
// --- actions

E
Erich Gamma 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41
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);
	}

42
	public run(): TPromise<any> {
E
Erich Gamma 已提交
43 44 45 46 47 48 49
		let activeEditor = this.editorService.getActiveEditor();
		if (activeEditor) {
			return this.editorService.closeEditor(activeEditor);
		}

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

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

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

63
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
64 65
		this.windowService.getWindow().close();

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

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 已提交
79 80
		@IMessageService private messageService: IMessageService,
		@IWindowService private windowService: IWindowService
E
Erich Gamma 已提交
81 82 83 84
	) {
		super(id, label);
	}

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

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

export class NewWindowAction extends Action {

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

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

109
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
110 111
		this.windowService.getWindow().openNew();

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

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

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

A
Alex Dima 已提交
128
		return TPromise.as(true);
E
Erich Gamma 已提交
129 130 131
	}
}

132 133 134 135 136 137 138 139 140
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);
	}

141
	public run(): TPromise<boolean> {
142 143
		ipc.send('vscode:toggleMenuBar', this.windowService.getWindowId());

A
Alex Dima 已提交
144
		return TPromise.as(true);
145 146 147
	}
}

E
Erich Gamma 已提交
148 149 150 151 152
export class ToggleDevToolsAction extends Action {

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

153
	constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
E
Erich Gamma 已提交
154 155 156
		super(id, label);
	}

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

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

export class ZoomInAction extends Action {

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

169
	constructor(id: string, label: string) {
E
Erich Gamma 已提交
170 171 172
		super(id, label);
	}

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

A
Alex Dima 已提交
177
		return TPromise.as(true);
E
Erich Gamma 已提交
178 179 180
	}
}

B
Benjamin Pasero 已提交
181
export class ZoomOutAction extends Action {
E
Erich Gamma 已提交
182 183 184 185

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

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

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

197
		return TPromise.as(true);
E
Erich Gamma 已提交
198 199 200
	}
}

B
Benjamin Pasero 已提交
201
export class ZoomResetAction extends Action {
E
Erich Gamma 已提交
202 203 204 205

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

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

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

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

	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 已提交
230 231 232 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
}

/* 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");

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

		this.enabled = contextService.getConfiguration().env.enablePerformance;
E
Erich Gamma 已提交
268 269 270 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
	}

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

302
	public run(): TPromise<boolean> {
E
Erich Gamma 已提交
303 304 305 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
		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 已提交
347
		return TPromise.as(true);
E
Erich Gamma 已提交
348 349 350 351 352 353 354 355 356 357 358 359
	}
}

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

360
	public run(): TPromise<boolean> {
361
		this.windowService.getWindow().reload();
E
Erich Gamma 已提交
362

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

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

381
	public run(): TPromise<boolean> {
382 383 384 385 386 387 388 389 390 391 392 393 394
		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 已提交
395 396 397
			return {
				label: paths.basename(p),
				description: paths.dirname(p),
398 399
				path: p,
				separator: index === 0 ? { label: nls.localize('files', "files"), border: true } : void 0
B
Benjamin Pasero 已提交
400
			};
E
Erich Gamma 已提交
401 402
		});

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

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

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

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

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

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

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

444
		return TPromise.as(true);
E
Erich Gamma 已提交
445
	}
446 447 448 449 450 451 452
}

// --- commands

KeybindingsRegistry.registerCommandDesc({
	id: '_workbench.ipc',
	weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
B
Benjamin Pasero 已提交
453
	handler(accessor: ServicesAccessor, ipcMessage: string, ipcArgs: any[]) {
454 455 456 457 458 459
		if (ipcMessage && Array.isArray(ipcArgs)) {
			ipc.send(ipcMessage, ...ipcArgs);
		} else {
			ipc.send(ipcMessage);
		}
	},
460
	when: undefined,
461 462
	primary: undefined
});