workspaceActions.ts 17.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import nls = require('vs/nls');
11
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
12
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
13
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
B
Benjamin Pasero 已提交
14 15 16
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import URI from 'vs/base/common/uri';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
B
Benjamin Pasero 已提交
17
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
18
import { WORKSPACE_FILTER, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
B
Benjamin Pasero 已提交
19 20 21
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isLinux } from 'vs/base/common/platform';
import { dirname } from 'vs/base/common/paths';
B
Benjamin Pasero 已提交
22
import * as resources from 'vs/base/common/resources';
23 24
import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels';
import { isParent, FileKind } from 'vs/platform/files/common/files';
B
Benjamin Pasero 已提交
25
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
26 27
import { IQuickOpenService, IFilePickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { CancellationToken } from 'vs/base/common/cancellation';
28
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
import { IHistoryService } from 'vs/workbench/services/history/common/history';

export class OpenFileAction extends Action {

	static ID = 'workbench.action.files.openFile';
	static LABEL = nls.localize('openFile', "Open File...");

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

	run(event?: any, data?: ITelemetryData): TPromise<any> {
		return this.windowService.pickFileAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultFilePath(this.contextService, this.historyService) } });
	}
}
50 51 52 53 54 55 56 57 58

export class OpenFolderAction extends Action {

	static ID = 'workbench.action.files.openFolder';
	static LABEL = nls.localize('openFolder', "Open Folder...");

	constructor(
		id: string,
		label: string,
59 60 61
		@IWindowService private windowService: IWindowService,
		@IHistoryService private historyService: IHistoryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService
62 63 64 65
	) {
		super(id, label);
	}

66
	run(event?: any, data?: ITelemetryData): TPromise<any> {
67
		return this.windowService.pickFolderAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultFolderPath(this.contextService, this.historyService) } });
68 69 70 71 72 73 74 75 76 77 78
	}
}

export class OpenFileFolderAction extends Action {

	static ID = 'workbench.action.files.openFileFolder';
	static LABEL = nls.localize('openFileFolder', "Open...");

	constructor(
		id: string,
		label: string,
79 80 81
		@IWindowService private windowService: IWindowService,
		@IHistoryService private historyService: IHistoryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService
82 83 84 85
	) {
		super(id, label);
	}

86
	run(event?: any, data?: ITelemetryData): TPromise<any> {
87
		return this.windowService.pickFileFolderAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultFilePath(this.contextService, this.historyService) } });
88
	}
B
Benjamin Pasero 已提交
89 90
}

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
export const openFileFolderInNewWindowCommand = (accessor: ServicesAccessor) => {
	const { windowService, historyService, contextService } = services(accessor);

	windowService.pickFileFolderAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultFilePath(contextService, historyService) } });
};

export const openFolderCommand = (accessor: ServicesAccessor, forceNewWindow: boolean) => {
	const { windowService, historyService, contextService } = services(accessor);

	windowService.pickFolderAndOpen({ forceNewWindow, dialogOptions: { defaultPath: defaultFolderPath(contextService, historyService) } });
};

export const openFolderInNewWindowCommand = (accessor: ServicesAccessor) => {
	const { windowService, historyService, contextService } = services(accessor);

	windowService.pickFolderAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultFolderPath(contextService, historyService) } });
};

export const openFileInNewWindowCommand = (accessor: ServicesAccessor) => {
	const { windowService, historyService, contextService } = services(accessor);

	windowService.pickFileAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultFilePath(contextService, historyService) } });
};

export const openWorkspaceInNewWindowCommand = (accessor: ServicesAccessor) => {
	const { windowService, historyService, contextService, environmentService } = services(accessor);

	windowService.pickWorkspaceAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultWorkspacePath(contextService, historyService, environmentService) } });
};

function services(accessor: ServicesAccessor): { windowService: IWindowService, historyService: IHistoryService, contextService: IWorkspaceContextService, environmentService: IEnvironmentService } {
	return {
		windowService: accessor.get(IWindowService),
		historyService: accessor.get(IHistoryService),
		contextService: accessor.get(IWorkspaceContextService),
		environmentService: accessor.get(IEnvironmentService)
	};
}

B
Benjamin Pasero 已提交
130
export abstract class BaseWorkspacesAction extends Action {
B
Benjamin Pasero 已提交
131 132 133 134 135

	constructor(
		id: string,
		label: string,
		protected windowService: IWindowService,
B
Benjamin Pasero 已提交
136
		protected environmentService: IEnvironmentService,
137 138
		protected contextService: IWorkspaceContextService,
		protected historyService: IHistoryService
B
Benjamin Pasero 已提交
139 140 141 142
	) {
		super(id, label);
	}

143
	protected pickFolders(buttonLabel: string, title: string): TPromise<string[]> {
B
Benjamin Pasero 已提交
144
		return this.windowService.showOpenDialog({
B
Benjamin Pasero 已提交
145 146
			buttonLabel,
			title,
B
Benjamin Pasero 已提交
147
			properties: ['multiSelections', 'openDirectory', 'createDirectory'],
148
			defaultPath: defaultFolderPath(this.contextService, this.historyService)
B
Benjamin Pasero 已提交
149 150
		});
	}
B
Benjamin Pasero 已提交
151 152
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
function defaultFilePath(contextService: IWorkspaceContextService, historyService: IHistoryService): string {
	let candidate: URI;

	// Check for last active file first...
	candidate = historyService.getLastActiveFile();

	// ...then for last active file root
	if (!candidate) {
		candidate = historyService.getLastActiveWorkspaceRoot('file');
	}

	return candidate ? dirname(candidate.fsPath) : void 0;
}

function defaultFolderPath(contextService: IWorkspaceContextService, historyService: IHistoryService): string {
	let candidate: URI;

	// Check for last active file root first...
	candidate = historyService.getLastActiveWorkspaceRoot('file');

	// ...then for last active file
	if (!candidate) {
		candidate = historyService.getLastActiveFile();
	}

	return candidate ? dirname(candidate.fsPath) : void 0;
}

function defaultWorkspacePath(contextService: IWorkspaceContextService, historyService: IHistoryService, environmentService: IEnvironmentService): string {

	// Check for current workspace config file first...
	if (contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && !isUntitledWorkspace(contextService.getWorkspace().configuration.fsPath, environmentService)) {
		return dirname(contextService.getWorkspace().configuration.fsPath);
	}

	// ...then fallback to default folder path
	return defaultFolderPath(contextService, historyService);
}

function isUntitledWorkspace(path: string, environmentService: IEnvironmentService): boolean {
	return isParent(path, environmentService.workspacesHome, !isLinux /* ignore case */);
}

B
Benjamin Pasero 已提交
196
export class AddRootFolderAction extends BaseWorkspacesAction {
B
Benjamin Pasero 已提交
197

B
Benjamin Pasero 已提交
198 199
	static ID = 'workbench.action.addRootFolder';
	static LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
B
Benjamin Pasero 已提交
200 201 202 203

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
204
		@IWindowService windowService: IWindowService,
B
Benjamin Pasero 已提交
205
		@IWorkspaceContextService contextService: IWorkspaceContextService,
S
Sandeep Somavarapu 已提交
206
		@IEnvironmentService environmentService: IEnvironmentService,
B
Benjamin Pasero 已提交
207
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
208 209
		@IViewletService private viewletService: IViewletService,
		@IHistoryService historyService: IHistoryService
B
Benjamin Pasero 已提交
210
	) {
211
		super(id, label, windowService, environmentService, contextService, historyService);
B
Benjamin Pasero 已提交
212 213 214
	}

	public run(): TPromise<any> {
215 216 217 218
		return super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace")).then(folders => {
			if (!folders || !folders.length) {
				return null;
			}
219

220 221 222
			// Add and show Files Explorer viewlet
			return this.workspaceEditingService.addFolders(folders.map(folder => ({ uri: URI.file(folder) }))).then(() => this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true));
		});
S
Sandeep Somavarapu 已提交
223
	}
B
Benjamin Pasero 已提交
224
}
S
Sandeep Somavarapu 已提交
225

226 227 228 229 230 231 232 233 234 235 236 237
export class GlobalRemoveRootFolderAction extends BaseWorkspacesAction {

	static ID = 'workbench.action.removeRootFolder';
	static LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace...");

	constructor(
		id: string,
		label: string,
		@IWindowService windowService: IWindowService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
		@IEnvironmentService environmentService: IEnvironmentService,
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
238 239
		@ICommandService private commandService: ICommandService,
		@IHistoryService historyService: IHistoryService
240
	) {
241
		super(id, label, windowService, environmentService, contextService, historyService);
242 243 244 245 246 247 248 249 250
	}

	public run(): TPromise<any> {
		const state = this.contextService.getWorkbenchState();

		// Workspace / Folder
		if (state === WorkbenchState.WORKSPACE || state === WorkbenchState.FOLDER) {
			return this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND).then(folder => {
				if (folder) {
S
Sandeep Somavarapu 已提交
251
					return this.workspaceEditingService.removeFolders([folder.uri]).then(() => true);
252 253 254 255 256 257 258 259 260 261
				}

				return true;
			});
		}

		return TPromise.as(true);
	}
}

I
isidor 已提交
262
export class RemoveRootFolderAction extends Action {
B
Benjamin Pasero 已提交
263

I
isidor 已提交
264
	static ID = 'workbench.action.removeRootFolder';
265
	static LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace");
B
Benjamin Pasero 已提交
266 267

	constructor(
I
isidor 已提交
268
		private rootUri: URI,
B
Benjamin Pasero 已提交
269 270
		id: string,
		label: string,
S
Sandeep Somavarapu 已提交
271
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
B
Benjamin Pasero 已提交
272 273 274 275 276
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
S
Sandeep Somavarapu 已提交
277
		return this.workspaceEditingService.removeFolders([this.rootUri]);
B
Benjamin Pasero 已提交
278
	}
I
isidor 已提交
279
}
280

I
isidor 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
export class OpenFolderSettingsAction extends Action {

	static ID = 'workbench.action.openFolderSettings';
	static LABEL = nls.localize('openFolderSettings', "Open Folder Settings");

	constructor(
		private rootUri: URI,
		id: string,
		label: string,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@ICommandService private commandService: ICommandService
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
		const workspaceFolder = this.contextService.getWorkspaceFolder(this.rootUri);
298

I
isidor 已提交
299 300 301 302
		return this.commandService.executeCommand('_workbench.action.openFolderSettings', workspaceFolder);
	}
}

303
export class SaveWorkspaceAsAction extends BaseWorkspacesAction {
304

305 306
	static ID = 'workbench.action.saveWorkspaceAs';
	static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As...");
307 308 309 310

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
311 312
		@IWindowService windowService: IWindowService,
		@IEnvironmentService environmentService: IEnvironmentService,
B
Benjamin Pasero 已提交
313
		@IWorkspaceContextService contextService: IWorkspaceContextService,
314 315
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
		@IHistoryService historyService: IHistoryService
316
	) {
317
		super(id, label, windowService, environmentService, contextService, historyService);
318 319 320
	}

	public run(): TPromise<any> {
321 322 323 324 325 326 327 328 329 330 331
		return this.getNewWorkspaceConfigPath().then(configPath => {
			if (configPath) {
				switch (this.contextService.getWorkbenchState()) {
					case WorkbenchState.EMPTY:
					case WorkbenchState.FOLDER:
						const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri }));
						return this.workspaceEditingService.createAndEnterWorkspace(folders, configPath);

					case WorkbenchState.WORKSPACE:
						return this.workspaceEditingService.saveAndEnterWorkspace(configPath);
				}
S
Sandeep Somavarapu 已提交
332
			}
S
Sandeep Somavarapu 已提交
333

334 335
			return null;
		});
S
Sandeep Somavarapu 已提交
336 337
	}

338
	private getNewWorkspaceConfigPath(): TPromise<string> {
S
Sandeep Somavarapu 已提交
339
		return this.windowService.showSaveDialog({
340
			buttonLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
S
Sandeep Somavarapu 已提交
341 342
			title: nls.localize('saveWorkspace', "Save Workspace"),
			filters: WORKSPACE_FILTER,
343
			defaultPath: defaultWorkspacePath(this.contextService, this.historyService, this.environmentService)
S
Sandeep Somavarapu 已提交
344
		});
345 346 347 348 349 350 351 352 353 354 355 356
	}
}

export class OpenWorkspaceAction extends Action {

	static ID = 'workbench.action.openWorkspace';
	static LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");

	constructor(
		id: string,
		label: string,
		@IWindowService private windowService: IWindowService,
357 358 359
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IHistoryService private historyService: IHistoryService,
		@IEnvironmentService private environmentService: IEnvironmentService
360 361 362 363
	) {
		super(id, label);
	}

364 365
	public run(event?: any, data?: ITelemetryData): TPromise<any> {
		return this.windowService.pickWorkspaceAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultWorkspacePath(this.contextService, this.historyService, this.environmentService) } });
366
	}
367 368
}

369 370
export class OpenWorkspaceConfigFileAction extends Action {

M
Matt Bierner 已提交
371 372
	public static readonly ID = 'workbench.action.openWorkspaceConfigFile';
	public static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File");
373 374 375 376 377 378 379 380

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService
	) {
		super(id, label);
B
Benjamin Pasero 已提交
381

382
		this.enabled = !!this.workspaceContextService.getWorkspace().configuration;
383 384 385 386 387
	}

	public run(): TPromise<any> {
		return this.editorService.openEditor({ resource: this.workspaceContextService.getWorkspace().configuration });
	}
388 389
}

390 391
export class OpenFolderAsWorkspaceInNewWindowAction extends Action {

M
Matt Bierner 已提交
392 393
	public static readonly ID = 'workbench.action.openFolderAsWorkspaceInNewWindow';
	public static readonly LABEL = nls.localize('openFolderAsWorkspaceInNewWindow', "Open Folder as Workspace in New Window");
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423

	constructor(
		id: string,
		label: string,
		@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
		@IWindowsService private windowsService: IWindowsService,
		@ICommandService private commandService: ICommandService,
		@IWorkspacesService private workspacesService: IWorkspacesService
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
		const folders = this.workspaceContextService.getWorkspace().folders;

		let folderPromise: TPromise<IWorkspaceFolder>;
		if (folders.length === 0) {
			folderPromise = TPromise.as(null);
		} else if (folders.length === 1) {
			folderPromise = TPromise.as(folders[0]);
		} else {
			folderPromise = this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND);
		}

		return folderPromise.then(folder => {
			if (!folder) {
				return void 0; // need at least one folder
			}

424
			return this.workspacesService.createWorkspace([{ uri: folder.uri }]).then(newWorkspace => {
425 426 427 428 429 430 431 432
				return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => {
					return this.windowsService.openWindow([newWorkspace.configPath], { forceNewWindow: true });
				});
			});
		});
	}
}

433
export const PICK_WORKSPACE_FOLDER_COMMAND = '_workbench.pickWorkspaceFolder';
434

435
CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND, function (accessor: ServicesAccessor, args?: [IPickOptions, CancellationToken]) {
436 437 438 439 440 441 442 443 444 445 446 447
	const contextService = accessor.get(IWorkspaceContextService);
	const quickOpenService = accessor.get(IQuickOpenService);
	const environmentService = accessor.get(IEnvironmentService);

	const folders = contextService.getWorkspace().folders;
	if (!folders.length) {
		return void 0;
	}

	const folderPicks = folders.map(folder => {
		return {
			label: folder.name,
B
Benjamin Pasero 已提交
448
			description: getPathLabel(resources.dirname(folder.uri), void 0, environmentService),
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 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 490 491 492
			folder,
			resource: folder.uri,
			fileKind: FileKind.ROOT_FOLDER
		} as IFilePickOpenEntry;
	});

	let options: IPickOptions;
	if (args) {
		options = args[0];
	}

	if (!options) {
		options = Object.create(null);
	}

	if (!options.autoFocus) {
		options.autoFocus = { autoFocusFirstEntry: true };
	}

	if (!options.placeHolder) {
		options.placeHolder = nls.localize('workspaceFolderPickerPlaceholder', "Select workspace folder");
	}

	if (typeof options.matchOnDescription !== 'boolean') {
		options.matchOnDescription = true;
	}

	let token: CancellationToken;
	if (args) {
		token = args[1];
	}

	if (!token) {
		token = CancellationToken.None;
	}

	return quickOpenService.pick(folderPicks, options, token).then(pick => {
		if (!pick) {
			return void 0;
		}

		return folders[folderPicks.indexOf(pick)];
	});
});