workspaceActions.ts 12.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');
S
Sandeep Somavarapu 已提交
11
import { distinct } from 'vs/base/common/arrays';
12
import { IWindowService } from 'vs/platform/windows/common/windows';
13
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
14
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
B
Benjamin Pasero 已提交
15 16 17
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';
18
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
19
import { WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces';
B
Benjamin Pasero 已提交
20 21 22
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isLinux } from 'vs/base/common/platform';
import { dirname } from 'vs/base/common/paths';
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

export class OpenFolderAction extends Action {

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

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

43
	run(event?: any, data?: ITelemetryData): TPromise<any> {
B
Benjamin Pasero 已提交
44
		return this.windowService.pickFolderAndOpen({ telemetryExtraData: data });
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
	}
}

export class OpenFileFolderAction extends Action {

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

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

61
	run(event?: any, data?: ITelemetryData): TPromise<any> {
B
Benjamin Pasero 已提交
62
		return this.windowService.pickFileFolderAndOpen({ telemetryExtraData: data });
63
	}
B
Benjamin Pasero 已提交
64 65
}

B
Benjamin Pasero 已提交
66
export abstract class BaseWorkspacesAction extends Action {
B
Benjamin Pasero 已提交
67 68 69 70 71

	constructor(
		id: string,
		label: string,
		protected windowService: IWindowService,
B
Benjamin Pasero 已提交
72 73
		protected environmentService: IEnvironmentService,
		protected contextService: IWorkspaceContextService
B
Benjamin Pasero 已提交
74 75 76 77
	) {
		super(id, label);
	}

B
Benjamin Pasero 已提交
78
	protected pickFolders(buttonLabel: string, title: string): string[] {
79
		let defaultPath: string;
80
		const workspace = this.contextService.getWorkspace();
S
Sandeep Somavarapu 已提交
81
		if (workspace.folders.length > 0) {
82
			defaultPath = dirname(workspace.folders[0].uri.fsPath); // pick the parent of the first root by default
83 84
		}

B
Benjamin Pasero 已提交
85
		return this.windowService.showOpenDialog({
B
Benjamin Pasero 已提交
86 87
			buttonLabel,
			title,
B
Benjamin Pasero 已提交
88
			properties: ['multiSelections', 'openDirectory', 'createDirectory'],
89
			defaultPath
B
Benjamin Pasero 已提交
90 91
		});
	}
B
Benjamin Pasero 已提交
92 93
}

B
Benjamin Pasero 已提交
94
export class AddRootFolderAction extends BaseWorkspacesAction {
B
Benjamin Pasero 已提交
95

B
Benjamin Pasero 已提交
96 97
	static ID = 'workbench.action.addRootFolder';
	static LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
B
Benjamin Pasero 已提交
98 99 100 101

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
102
		@IWindowService windowService: IWindowService,
B
Benjamin Pasero 已提交
103
		@IWorkspaceContextService contextService: IWorkspaceContextService,
S
Sandeep Somavarapu 已提交
104
		@IEnvironmentService environmentService: IEnvironmentService,
B
Benjamin Pasero 已提交
105 106 107
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
		@IViewletService private viewletService: IViewletService
B
Benjamin Pasero 已提交
108
	) {
S
Sandeep Somavarapu 已提交
109
		super(id, label, windowService, environmentService, contextService);
B
Benjamin Pasero 已提交
110 111 112
	}

	public run(): TPromise<any> {
113 114 115
		let addFoldersPromise: TPromise<void>;

		// Workspace
116 117 118 119 120
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
			const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"));
			if (!folders || !folders.length) {
				return TPromise.as(null);
			}
121

122 123 124 125 126 127
			addFoldersPromise = this.workspaceEditingService.addFolders(folders.map(folder => URI.file(folder)));
		}

		// Empty or Folder
		else {
			addFoldersPromise = this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().folders.map(folder => folder.uri)).run();
B
Benjamin Pasero 已提交
128
		}
129

130 131
		// Add and show Files Explorer viewlet
		return addFoldersPromise.then(() => this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true));
S
Sandeep Somavarapu 已提交
132
	}
B
Benjamin Pasero 已提交
133
}
S
Sandeep Somavarapu 已提交
134

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 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
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,
		@ICommandService private commandService: ICommandService
	) {
		super(id, label, windowService, environmentService, contextService);
	}

	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) {

					// Folder: close workspace
					if (state === WorkbenchState.FOLDER) {
						return this.windowService.closeWorkspace().then(() => true);
					}

					// Workspace: remove folder
					return this.workspaceEditingService.removeFolders([folder.uri]).then(() => true);
				}

				return true;
			});
		}

		return TPromise.as(true);
	}
}

177
class NewWorkspaceAction extends BaseWorkspacesAction {
B
Benjamin Pasero 已提交
178 179 180 181 182 183 184

	static ID = 'workbench.action.newWorkspace';
	static LABEL = nls.localize('newWorkspace', "New Workspace...");

	constructor(
		id: string,
		label: string,
185
		private presetRoots: URI[],
B
Benjamin Pasero 已提交
186 187
		@IWindowService windowService: IWindowService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
S
Sandeep Somavarapu 已提交
188
		@IEnvironmentService environmentService: IEnvironmentService,
189
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
190
	) {
S
Sandeep Somavarapu 已提交
191
		super(id, label, windowService, environmentService, contextService);
192 193
	}

S
Sandeep Somavarapu 已提交
194
	public run(): TPromise<any> {
195
		const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace"));
196
		if (folders && folders.length) {
197
			return this.createWorkspace([...this.presetRoots, ...folders.map(folder => URI.file(folder))]);
B
Benjamin Pasero 已提交
198
		}
B
Benjamin Pasero 已提交
199

B
Benjamin Pasero 已提交
200 201
		return TPromise.as(null);
	}
S
Sandeep Somavarapu 已提交
202

B
Benjamin Pasero 已提交
203
	private createWorkspace(folders: URI[]): TPromise<void> {
204
		const workspaceFolders = distinct(folders.map(folder => folder.fsPath), folder => isLinux ? folder : folder.toLowerCase());
205

206
		return this.workspaceEditingService.createAndEnterWorkspace(workspaceFolders);
207 208 209
	}
}

I
isidor 已提交
210
export class RemoveRootFolderAction extends Action {
B
Benjamin Pasero 已提交
211

I
isidor 已提交
212
	static ID = 'workbench.action.removeRootFolder';
213
	static LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace");
B
Benjamin Pasero 已提交
214 215

	constructor(
I
isidor 已提交
216
		private rootUri: URI,
B
Benjamin Pasero 已提交
217 218 219 220 221 222 223 224
		id: string,
		label: string,
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
S
Sandeep Somavarapu 已提交
225
		return this.workspaceEditingService.removeFolders([this.rootUri]);
B
Benjamin Pasero 已提交
226
	}
I
isidor 已提交
227
}
228

229
export class SaveWorkspaceAsAction extends BaseWorkspacesAction {
230

231 232
	static ID = 'workbench.action.saveWorkspaceAs';
	static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As...");
233 234 235 236

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
237 238
		@IWindowService windowService: IWindowService,
		@IEnvironmentService environmentService: IEnvironmentService,
B
Benjamin Pasero 已提交
239
		@IWorkspaceContextService contextService: IWorkspaceContextService,
240
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
241
	) {
S
Sandeep Somavarapu 已提交
242
		super(id, label, windowService, environmentService, contextService);
243 244 245
	}

	public run(): TPromise<any> {
S
Sandeep Somavarapu 已提交
246 247
		const configPath = this.getNewWorkspaceConfigPath();
		if (configPath) {
248 249
			switch (this.contextService.getWorkbenchState()) {
				case WorkbenchState.EMPTY:
250
				case WorkbenchState.FOLDER:
251
					const workspaceFolders = this.contextService.getWorkspace().folders.map(root => root.uri.fsPath);
252
					return this.workspaceEditingService.createAndEnterWorkspace(workspaceFolders, configPath);
253

254
				case WorkbenchState.WORKSPACE:
255
					return this.workspaceEditingService.saveAndEnterWorkspace(configPath);
S
Sandeep Somavarapu 已提交
256 257
			}
		}
S
Sandeep Somavarapu 已提交
258

S
Sandeep Somavarapu 已提交
259 260 261
		return TPromise.as(null);
	}

262
	private getNewWorkspaceConfigPath(): string {
B
Benjamin Pasero 已提交
263 264
		const workspace = this.contextService.getWorkspace();
		let defaultPath: string;
265
		if (workspace.configuration && !this.isUntitledWorkspace(workspace.configuration.fsPath)) {
B
Benjamin Pasero 已提交
266
			defaultPath = workspace.configuration.fsPath;
S
Sandeep Somavarapu 已提交
267
		} else if (workspace.folders.length > 0) {
268
			defaultPath = dirname(workspace.folders[0].uri.fsPath); // pick the parent of the first root by default
B
Benjamin Pasero 已提交
269 270
		}

S
Sandeep Somavarapu 已提交
271
		return this.windowService.showSaveDialog({
272
			buttonLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
S
Sandeep Somavarapu 已提交
273 274
			title: nls.localize('saveWorkspace', "Save Workspace"),
			filters: WORKSPACE_FILTER,
B
Benjamin Pasero 已提交
275
			defaultPath
S
Sandeep Somavarapu 已提交
276
		});
277
	}
B
Benjamin Pasero 已提交
278 279 280 281

	private isUntitledWorkspace(path: string): boolean {
		return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */);
	}
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
}

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,
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
298
		return this.windowService.openWorkspace();
299
	}
300 301
}

302 303 304 305 306 307 308 309 310 311 312 313
export class OpenWorkspaceConfigFileAction extends Action {

	public static ID = 'workbench.action.openWorkspaceConfigFile';
	public static LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File");

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

315
		this.enabled = !!this.workspaceContextService.getWorkspace().configuration;
316 317 318 319 320
	}

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

323
export const PICK_WORKSPACE_FOLDER_COMMAND = '_workbench.pickWorkspaceFolder';
324

325
CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND, function (accessor: ServicesAccessor, args?: [IPickOptions, CancellationToken]) {
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
	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,
			description: getPathLabel(dirname(folder.uri.fsPath), void 0, environmentService),
			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)];
	});
});