workspaceActions.ts 11.7 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, IWindowsService } from 'vs/platform/windows/common/windows';
13
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
B
Benjamin Pasero 已提交
14 15 16 17
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
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 } from "vs/platform/instantiation/common/instantiation";
19
import { IWorkspacesService, WORKSPACE_FILTER } from "vs/platform/workspaces/common/workspaces";
20
import { IMessageService, Severity } from "vs/platform/message/common/message";
B
Benjamin Pasero 已提交
21
import { IEnvironmentService } from "vs/platform/environment/common/environment";
22
import { isLinux } from "vs/base/common/platform";
B
Benjamin Pasero 已提交
23
import { dirname } from "vs/base/common/paths";
24
import { mnemonicButtonLabel } from "vs/base/common/labels";
B
Benjamin Pasero 已提交
25
import { isParent } from "vs/platform/files/common/files";
26 27 28 29 30 31 32 33 34 35 36 37 38 39

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

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

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

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

B
Benjamin Pasero 已提交
63
export abstract class BaseWorkspacesAction extends Action {
B
Benjamin Pasero 已提交
64 65 66 67 68

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

S
Sandeep Somavarapu 已提交
75
	protected handleNotInMultiFolderWorkspaceCase(message: string, actionLabel: string): boolean {
76
		const newWorkspace = { label: mnemonicButtonLabel(actionLabel), canceled: false };
B
Benjamin Pasero 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89
		const cancel = { label: nls.localize('cancel', "Cancel"), canceled: true };

		const buttons: { label: string; canceled: boolean; }[] = [];
		if (isLinux) {
			buttons.push(cancel, newWorkspace);
		} else {
			buttons.push(newWorkspace, cancel);
		}

		const opts: Electron.ShowMessageBoxOptions = {
			title: this.environmentService.appNameLong,
			message,
			noLink: true,
B
Benjamin Pasero 已提交
90
			type: 'question',
B
Benjamin Pasero 已提交
91 92 93 94 95
			buttons: buttons.map(button => button.label),
			cancelId: buttons.indexOf(cancel)
		};

		if (isLinux) {
B
Benjamin Pasero 已提交
96
			opts.defaultId = 1;
B
Benjamin Pasero 已提交
97 98 99
		}

		const res = this.windowService.showMessageBox(opts);
S
Sandeep Somavarapu 已提交
100
		return !buttons[res].canceled;
B
Benjamin Pasero 已提交
101 102
	}

B
Benjamin Pasero 已提交
103
	protected pickFolders(buttonLabel: string, title: string): string[] {
104 105 106 107 108 109
		const workspace = this.contextService.getWorkspace();
		let defaultPath: string;
		if (workspace && workspace.roots.length > 0) {
			defaultPath = dirname(workspace.roots[0].fsPath); // pick the parent of the first root by default
		}

B
Benjamin Pasero 已提交
110
		return this.windowService.showOpenDialog({
B
Benjamin Pasero 已提交
111 112
			buttonLabel,
			title,
B
Benjamin Pasero 已提交
113
			properties: ['multiSelections', 'openDirectory', 'createDirectory'],
114
			defaultPath
B
Benjamin Pasero 已提交
115 116
		});
	}
B
Benjamin Pasero 已提交
117 118
}

119
export class NewWorkspaceFromExistingAction extends BaseWorkspacesAction {
B
Benjamin Pasero 已提交
120

121 122
	static ID = 'workbench.action.newWorkspaceFromExisting';
	static LABEL = nls.localize('newWorkspaceFormExisting', "New Workspace From Existing...");
B
Benjamin Pasero 已提交
123 124 125 126

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
127
		@IWindowService windowService: IWindowService,
B
Benjamin Pasero 已提交
128
		@IWorkspaceContextService contextService: IWorkspaceContextService,
S
Sandeep Somavarapu 已提交
129 130 131
		@IEnvironmentService environmentService: IEnvironmentService,
		@IWorkspacesService protected workspacesService: IWorkspacesService,
		@IWindowsService protected windowsService: IWindowsService,
B
Benjamin Pasero 已提交
132
	) {
S
Sandeep Somavarapu 已提交
133
		super(id, label, windowService, environmentService, contextService);
B
Benjamin Pasero 已提交
134 135 136
	}

	public run(): TPromise<any> {
137
		let folders = this.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace"));
S
Sandeep Somavarapu 已提交
138
		if (folders && folders.length) {
139 140 141
			if (this.contextService.hasWorkspace()) {
				return this.createWorkspace([this.contextService.getWorkspace().roots[0], ...folders.map(folder => URI.file(folder))]);
			}
B
Benjamin Pasero 已提交
142 143
		}

S
Sandeep Somavarapu 已提交
144 145 146
		return TPromise.as(null);
	}

147
	private createWorkspace(folders: URI[]): TPromise<void> {
S
Sandeep Somavarapu 已提交
148 149 150 151 152 153
		return this.workspacesService.createWorkspace(distinct(folders.map(folder => folder.toString(true /* encoding */))))
			.then(({ configPath }) => this.windowsService.openWindow([configPath]));
	}
}

export class AddRootFolderAction extends BaseWorkspacesAction {
154

S
Sandeep Somavarapu 已提交
155 156
	static ID = 'workbench.action.addRootFolder';
	static LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
157 158 159 160

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
161 162
		@IWindowService windowService: IWindowService,
		@IWorkspaceContextService contextService: IWorkspaceContextService,
S
Sandeep Somavarapu 已提交
163 164
		@IEnvironmentService environmentService: IEnvironmentService,
		@IInstantiationService private instantiationService: IInstantiationService,
165
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
S
Sandeep Somavarapu 已提交
166
		@IViewletService private viewletService: IViewletService
167
	) {
S
Sandeep Somavarapu 已提交
168
		super(id, label, windowService, environmentService, contextService);
169 170
	}

S
Sandeep Somavarapu 已提交
171
	public run(): TPromise<any> {
172 173 174 175 176
		if (!this.contextService.hasWorkspace()) {
			return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL).run();
		}

		if (this.contextService.hasFolderWorkspace()) {
S
Sandeep Somavarapu 已提交
177
			if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "Before we can add another folder we need to upgrade your workspace to support multiple folders and reload the window."), nls.localize({ key: 'upgradeAndAdd', comment: ['&& denotes a mnemonic'] }, "&&Upgrade and Add"))) {
S
Sandeep Somavarapu 已提交
178
				return this.instantiationService.createInstance(NewWorkspaceFromExistingAction, NewWorkspaceFromExistingAction.ID, NewWorkspaceFromExistingAction.LABEL).run();
S
Sandeep Somavarapu 已提交
179
			}
S
Sandeep Somavarapu 已提交
180
			return TPromise.as(null);
B
Benjamin Pasero 已提交
181
		}
B
Benjamin Pasero 已提交
182

183
		const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"));
S
Sandeep Somavarapu 已提交
184 185 186 187 188 189 190
		if (!folders || !folders.length) {
			return TPromise.as(null);
		}

		return this.workspaceEditingService.addRoots(folders.map(folder => URI.file(folder))).then(() => {
			return this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true);
		});
191 192 193
	}
}

I
isidor 已提交
194
export class RemoveRootFolderAction extends Action {
B
Benjamin Pasero 已提交
195

I
isidor 已提交
196
	static ID = 'workbench.action.removeRootFolder';
197
	static LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace");
B
Benjamin Pasero 已提交
198 199

	constructor(
I
isidor 已提交
200
		private rootUri: URI,
B
Benjamin Pasero 已提交
201 202 203 204 205 206 207 208
		id: string,
		label: string,
		@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
I
isidor 已提交
209
		return this.workspaceEditingService.removeRoots([this.rootUri]);
B
Benjamin Pasero 已提交
210
	}
I
isidor 已提交
211
}
212

213
export class SaveWorkspaceAsAction extends BaseWorkspacesAction {
214

215 216
	static ID = 'workbench.action.saveWorkspaceAs';
	static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As...");
217 218 219 220

	constructor(
		id: string,
		label: string,
B
Benjamin Pasero 已提交
221 222
		@IWindowService windowService: IWindowService,
		@IEnvironmentService environmentService: IEnvironmentService,
B
Benjamin Pasero 已提交
223
		@IWorkspaceContextService contextService: IWorkspaceContextService,
S
Sandeep Somavarapu 已提交
224
		@IWorkspacesService protected workspacesService: IWorkspacesService,
225 226
		@IWindowsService private windowsService: IWindowsService,
		@IMessageService private messageService: IMessageService
227
	) {
S
Sandeep Somavarapu 已提交
228
		super(id, label, windowService, environmentService, contextService);
229 230 231
	}

	public run(): TPromise<any> {
S
Sandeep Somavarapu 已提交
232 233
		if (this.contextService.hasFolderWorkspace()) {
			return this.saveFolderWorkspace();
234 235
		}

S
Sandeep Somavarapu 已提交
236
		if (this.contextService.hasMultiFolderWorkspace()) {
237
			return this.saveMultiFolderWorkspace();
S
Sandeep Somavarapu 已提交
238 239
		}

B
Benjamin Pasero 已提交
240
		this.messageService.show(Severity.Info, nls.localize('saveEmptyWorkspaceNotSupported', "Saving a workspace is not supported when the window is opened without a folder. Please open a folder first."));
241
		return TPromise.as(null);
S
Sandeep Somavarapu 已提交
242 243 244
	}

	private saveFolderWorkspace(): TPromise<void> {
S
Sandeep Somavarapu 已提交
245
		if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('saveNotSupported', "Before we can save we need to upgrade your workspace to support multiple folders and reload the window."), nls.localize({ key: 'upgradeAndSave', comment: ['&& denotes a mnemonic'] }, "&&Upgrade and Save"))) {
246
			const configPath = this.getNewWorkspaceConfigPath();
S
Sandeep Somavarapu 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
			if (configPath) {
				// Create workspace first
				this.workspacesService.createWorkspace(this.contextService.getWorkspace().roots.map(root => root.toString(true /* skip encoding */)))
					.then(workspaceIdentifier => {
						// Save the workspace in new location
						return this.workspacesService.saveWorkspace(workspaceIdentifier, configPath)
							// Open the saved workspace
							.then(({ configPath }) => this.windowsService.openWindow([configPath]));
					});
			}
		}
		return TPromise.as(null);
	}

	private saveMultiFolderWorkspace(): TPromise<void> {
262
		const target = this.getNewWorkspaceConfigPath();
263 264

		if (target) {
265
			return this.contextService.saveWorkspace(URI.file(target));
266 267
		}

S
Sandeep Somavarapu 已提交
268 269 270
		return TPromise.as(null);
	}

271
	private getNewWorkspaceConfigPath(): string {
B
Benjamin Pasero 已提交
272 273 274 275
		const workspace = this.contextService.getWorkspace();
		let defaultPath: string;
		if (this.contextService.hasMultiFolderWorkspace() && !this.isUntitledWorkspace(workspace.configuration.fsPath)) {
			defaultPath = workspace.configuration.fsPath;
276 277
		} else if (workspace && workspace.roots.length > 0) {
			defaultPath = dirname(workspace.roots[0].fsPath); // pick the parent of the first root by default
B
Benjamin Pasero 已提交
278 279
		}

S
Sandeep Somavarapu 已提交
280
		return this.windowService.showSaveDialog({
281
			buttonLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
S
Sandeep Somavarapu 已提交
282 283
			title: nls.localize('saveWorkspace', "Save Workspace"),
			filters: WORKSPACE_FILTER,
B
Benjamin Pasero 已提交
284
			defaultPath
S
Sandeep Somavarapu 已提交
285
		});
286
	}
B
Benjamin Pasero 已提交
287 288 289 290

	private isUntitledWorkspace(path: string): boolean {
		return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */);
	}
291 292 293 294 295 296 297 298 299 300 301
}

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,
B
Benjamin Pasero 已提交
302
		@IWorkspaceContextService private contextService: IWorkspaceContextService
303 304 305 306 307
	) {
		super(id, label);
	}

	public run(): TPromise<any> {
308
		return this.windowService.openWorkspace();
309
	}
310 311
}

312
class NewWorkspaceAction extends Action {
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

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

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

	public run(): TPromise<any> {
		return this.windowService.newWorkspace();
	}
329
}