workspaceEditingService.ts 8.5 KB
Newer Older
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';

B
Benjamin Pasero 已提交
8 9
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import URI from 'vs/base/common/uri';
10
import { TPromise } from 'vs/base/common/winjs.base';
11
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
12
import { IWindowsService, IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
B
Benjamin Pasero 已提交
13
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
14
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
J
Johannes Rieken 已提交
15
import { IWorkspacesService, IStoredWorkspaceFolder, IWorkspaceIdentifier, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
16 17 18
import { dirname } from 'path';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces';
19
import { isLinux } from 'vs/base/common/platform';
20
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
21 22 23 24 25 26
import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { StorageService } from 'vs/platform/storage/common/storageService';
import { ConfigurationScope, IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
27 28
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
J
Johannes Rieken 已提交
29
import { Schemas } from 'vs/base/common/network';
30 31 32 33 34 35

export class WorkspaceEditingService implements IWorkspaceEditingService {

	public _serviceBrand: any;

	constructor(
36
		@IJSONEditingService private jsonEditingService: IJSONEditingService,
37
		@IWorkspaceContextService private contextService: WorkspaceService,
38 39
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IWindowsService private windowsService: IWindowsService,
40
		@IWindowService private windowService: IWindowService,
41
		@IWorkspacesService private workspacesService: IWorkspacesService,
42 43
		@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
		@IStorageService private storageService: IStorageService,
44 45
		@IExtensionService private extensionService: IExtensionService,
		@IBackupFileService private backupFileService: IBackupFileService
46 47 48
	) {
	}

S
Sandeep Somavarapu 已提交
49
	public addFolders(foldersToAdd: URI[]): TPromise<void> {
50
		if (!this.isSupported()) {
51 52 53
			return TPromise.as(void 0); // we need a workspace to begin with
		}

54 55 56
		const currentWorkspaceFolders = this.contextService.getWorkspace().folders;
		const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
		const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw);
57

58 59 60 61
		const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];

		const workspaceConfigFolder = dirname(this.contextService.getWorkspace().configuration.fsPath);

J
Johannes Rieken 已提交
62 63
		foldersToAdd.forEach(folderToAdd => {
			if (this.contains(currentWorkspaceFolderUris, folderToAdd)) {
64 65 66
				return; // already existing
			}

67
			// File resource: use "path" property
J
Johannes Rieken 已提交
68 69 70 71
			if (folderToAdd.scheme === Schemas.file) {
				storedFoldersToAdd.push({
					path: massageFolderPathForWorkspace(folderToAdd.fsPath, workspaceConfigFolder, currentStoredFolders)
				});
72 73 74 75
			}

			// Any other resource: use "uri" property
			else {
J
Johannes Rieken 已提交
76 77 78 79
				storedFoldersToAdd.push({
					uri: folderToAdd.toString(true)
				});
			}
80 81 82 83 84 85 86
		});

		if (storedFoldersToAdd.length > 0) {
			return this.doSetFolders([...currentStoredFolders, ...storedFoldersToAdd]);
		}

		return TPromise.as(void 0);
87 88
	}

S
Sandeep Somavarapu 已提交
89
	public removeFolders(foldersToRemove: URI[]): TPromise<void> {
90
		if (!this.isSupported()) {
91 92 93
			return TPromise.as(void 0); // we need a workspace to begin with
		}

94 95
		const currentWorkspaceFolders = this.contextService.getWorkspace().folders;
		const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw);
96

97
		const newStoredFolders: IStoredWorkspaceFolder[] = currentStoredFolders.filter((folder, index) => {
J
Johannes Rieken 已提交
98
			if (!isStoredWorkspaceFolder(folder)) {
99 100
				return true; // keep entries which are unrelated
			}
101

102 103
			return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated
		});
104

105 106
		if (newStoredFolders.length !== currentStoredFolders.length) {
			return this.doSetFolders(newStoredFolders);
107 108
		}

109 110
		return TPromise.as(void 0);
	}
111

112
	private doSetFolders(folders: IStoredWorkspaceFolder[]): TPromise<void> {
B
Benjamin Pasero 已提交
113
		const workspace = this.contextService.getWorkspace();
114

B
Benjamin Pasero 已提交
115
		return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value: folders }, true);
116
	}
B
Benjamin Pasero 已提交
117

118
	private isSupported(): boolean {
119
		return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; // we need a multi folder workspace to begin with;
120 121 122 123 124 125 126
	}

	private contains(resources: URI[], toCheck: URI): boolean {
		return resources.some(resource => {
			if (isLinux) {
				return resource.toString() === toCheck.toString();
			}
B
Benjamin Pasero 已提交
127

128 129
			return resource.toString().toLowerCase() === toCheck.toString().toLowerCase();
		});
130
	}
131

B
Benjamin Pasero 已提交
132 133
	public createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise<void> {
		return this.doEnterWorkspace(() => this.windowService.createAndEnterWorkspace(folderPaths, path));
134 135
	}

136 137
	public saveAndEnterWorkspace(path: string): TPromise<void> {
		return this.doEnterWorkspace(() => this.windowService.saveAndEnterWorkspace(path));
138 139
	}

140
	private doEnterWorkspace(mainSidePromise: () => TPromise<IEnterWorkspaceResult>): TPromise<void> {
141

142
		// Stop the extension host first to give extensions most time to shutdown
143 144
		this.extensionService.stopExtensionHost();

145
		return mainSidePromise().then(result => {
146
			let enterWorkspacePromise: TPromise<void> = TPromise.as(void 0);
147
			if (result) {
148

149
				// Migrate storage and settings
150
				enterWorkspacePromise = this.migrate(result.workspace).then(() => {
151

152 153 154 155 156
					// Reinitialize backup service
					const backupFileService = this.backupFileService as BackupFileService; // TODO@Ben ugly cast
					backupFileService.initialize(result.backupPath);

					// Reinitialize configuration service
157
					const workspaceImpl = this.contextService as WorkspaceService; // TODO@Ben TODO@Sandeep ugly cast
158
					return workspaceImpl.initialize(result.workspace);
159 160 161 162 163
				});
			}

			// Finally bring the extension host back online
			return enterWorkspacePromise.then(() => this.extensionService.startExtensionHost());
164 165 166
		});
	}

B
Benjamin Pasero 已提交
167
	private migrate(toWorkspace: IWorkspaceIdentifier): TPromise<void> {
168 169

		// Storage (UI State) migration
B
Benjamin Pasero 已提交
170
		this.migrateStorage(toWorkspace);
171

172 173 174 175 176 177
		// Settings migration (only if we come from a folder workspace)
		if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
			return this.copyWorkspaceSettings(toWorkspace);
		}

		return TPromise.as(void 0);
178 179
	}

B
Benjamin Pasero 已提交
180
	private migrateStorage(toWorkspace: IWorkspaceIdentifier): void {
181 182 183

		// TODO@Ben revisit this when we move away from local storage to a file based approach
		const storageImpl = this.storageService as StorageService;
B
Benjamin Pasero 已提交
184 185
		const newWorkspaceId = migrateStorageToMultiRootWorkspace(storageImpl.workspaceId, toWorkspace, storageImpl.workspaceStorage);
		storageImpl.setWorkspaceId(newWorkspaceId);
186 187
	}

188
	public copyWorkspaceSettings(toWorkspace: IWorkspaceIdentifier): TPromise<void> {
189 190 191
		const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
		const targetWorkspaceConfiguration = {};
		for (const key of this.workspaceConfigurationService.keys().workspace) {
S
Sandeep Somavarapu 已提交
192
			if (configurationProperties[key] && !configurationProperties[key].isFromExtensions && configurationProperties[key].scope === ConfigurationScope.WINDOW) {
193
				targetWorkspaceConfiguration[key] = this.workspaceConfigurationService.inspect(key).workspace;
194 195 196
			}
		}

B
Benjamin Pasero 已提交
197
		return this.jsonEditingService.write(URI.file(toWorkspace.configPath), { key: 'settings', value: targetWorkspaceConfiguration }, true);
198
	}
J
Johannes Rieken 已提交
199
}