workspacesMainService.ts 5.9 KB
Newer Older
B
Benjamin Pasero 已提交
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
import { IWorkspacesMainService, IWorkspaceIdentifier, IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME } from 'vs/platform/workspaces/common/workspaces';
9
import { TPromise } from 'vs/base/common/winjs.base';
B
Benjamin Pasero 已提交
10 11 12 13 14 15 16 17 18 19 20 21
import { isParent } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { extname, join, dirname } from 'path';
import { mkdirp, writeFile } from 'vs/base/node/pfs';
import { readFileSync } from 'fs';
import { isLinux } from 'vs/base/common/platform';
import { copy, delSync, readdirSync } from 'vs/base/node/extfs';
import { nfcall } from 'vs/base/common/async';
import Event, { Emitter } from 'vs/base/common/event';
import { ILogService } from 'vs/platform/log/common/log';
import { isEqual } from 'vs/base/common/paths';
import { coalesce } from 'vs/base/common/arrays';
B
Benjamin Pasero 已提交
22 23 24 25 26

export class WorkspacesMainService implements IWorkspacesMainService {

	public _serviceBrand: any;

B
Benjamin Pasero 已提交
27
	protected workspacesHome: string;
B
Benjamin Pasero 已提交
28

29
	private _onWorkspaceSaved: Emitter<IWorkspaceSavedEvent>;
30
	private _onWorkspaceDeleted: Emitter<IWorkspaceIdentifier>;
31

32 33
	constructor(
		@IEnvironmentService private environmentService: IEnvironmentService,
34
		@ILogService private logService: ILogService
35
	) {
B
Benjamin Pasero 已提交
36
		this.workspacesHome = environmentService.workspacesHome;
37

38
		this._onWorkspaceSaved = new Emitter<IWorkspaceSavedEvent>();
39
		this._onWorkspaceDeleted = new Emitter<IWorkspaceIdentifier>();
40 41 42 43
	}

	public get onWorkspaceSaved(): Event<IWorkspaceSavedEvent> {
		return this._onWorkspaceSaved.event;
B
Benjamin Pasero 已提交
44 45
	}

46 47 48 49
	public get onWorkspaceDeleted(): Event<IWorkspaceIdentifier> {
		return this._onWorkspaceDeleted.event;
	}

50
	public resolveWorkspaceSync(path: string): IStoredWorkspace {
51
		const isWorkspace = this.isInsideWorkspacesHome(path) || extname(path) === `.${WORKSPACE_EXTENSION}`;
52 53 54 55 56 57
		if (!isWorkspace) {
			return null; // does not look like a valid workspace config file
		}

		try {
			const workspace = JSON.parse(readFileSync(path, 'utf8')) as IStoredWorkspace;
B
Benjamin Pasero 已提交
58
			if (typeof workspace.id !== 'string' || !Array.isArray(workspace.folders) || workspace.folders.length === 0) {
59 60
				this.logService.log(`${path} looks like an invalid workspace file.`);

61 62 63
				return null; // looks like an invalid workspace file
			}

64
			return workspace;
65
		} catch (error) {
66 67
			this.logService.log(`${path} cannot be parsed as JSON file (${error}).`);

68 69
			return null; // unable to read or parse as workspace file
		}
B
Benjamin Pasero 已提交
70 71
	}

72 73 74 75
	private isInsideWorkspacesHome(path: string): boolean {
		return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */);
	}

B
Benjamin Pasero 已提交
76 77 78 79 80
	public createWorkspace(folders: string[]): TPromise<IWorkspaceIdentifier> {
		if (!folders.length) {
			return TPromise.wrapError(new Error('Creating a workspace requires at least one folder.'));
		}

B
Benjamin Pasero 已提交
81 82
		const workspaceId = this.nextWorkspaceId();
		const workspaceConfigFolder = join(this.workspacesHome, workspaceId);
83
		const workspaceConfigPath = join(workspaceConfigFolder, UNTITLED_WORKSPACE_NAME);
B
Benjamin Pasero 已提交
84 85 86 87 88 89 90 91 92

		return mkdirp(workspaceConfigFolder).then(() => {
			const storedWorkspace: IStoredWorkspace = {
				id: workspaceId,
				folders
			};

			return writeFile(workspaceConfigPath, JSON.stringify(storedWorkspace, null, '\t')).then(() => ({
				id: workspaceId,
93
				configPath: workspaceConfigPath
B
Benjamin Pasero 已提交
94 95 96 97 98 99 100
			}));
		});
	}

	private nextWorkspaceId(): string {
		return (Date.now() + Math.round(Math.random() * 1000)).toString();
	}
B
Benjamin Pasero 已提交
101 102 103 104

	public isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean {
		return this.isInsideWorkspacesHome(workspace.configPath);
	}
105 106

	public saveWorkspace(workspace: IWorkspaceIdentifier, target: string): TPromise<IWorkspaceIdentifier> {
B
Benjamin Pasero 已提交
107 108 109 110 111 112 113

		// Return early if target is same as source
		if (isEqual(workspace.configPath, target, !isLinux)) {
			return TPromise.as(workspace);
		}

		// Copy to new target
114 115
		return nfcall(copy, workspace.configPath, target).then(() => {
			const savedWorkspace = this.resolveWorkspaceSync(target);
116
			const savedWorkspaceIdentifier = { id: savedWorkspace.id, configPath: target };
117

118
			// Event
119
			this._onWorkspaceSaved.fire({ workspace: savedWorkspaceIdentifier, oldConfigPath: workspace.configPath });
120

121
			// Delete untitled workspace
122
			this.deleteUntitledWorkspaceSync(workspace);
123

124
			return savedWorkspaceIdentifier;
125 126
		});
	}
127

128
	public deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void {
129
		if (!this.isUntitledWorkspace(workspace)) {
130
			return; // only supported for untitled workspaces
131 132
		}

133
		// Delete from disk
134
		this.doDeleteUntitledWorkspaceSync(workspace.configPath);
135 136 137

		// Event
		this._onWorkspaceDeleted.fire(workspace);
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

	private doDeleteUntitledWorkspaceSync(configPath: string): void {
		try {
			delSync(dirname(configPath));
		} catch (error) {
			this.logService.log(`Unable to delete untitled workspace ${configPath} (${error}).`);
		}
	}

	public getUntitledWorkspacesSync(): IWorkspaceIdentifier[] {
		let untitledWorkspacePaths: string[] = [];
		try {
			untitledWorkspacePaths = readdirSync(this.workspacesHome).map(folder => join(this.workspacesHome, folder, UNTITLED_WORKSPACE_NAME));
		} catch (error) {
			this.logService.log(`Unable to read folders in ${this.workspacesHome} (${error}).`);
		}

		const untitledWorkspaces: IWorkspaceIdentifier[] = coalesce(untitledWorkspacePaths.map(untitledWorkspacePath => {
			const workspace = this.resolveWorkspaceSync(untitledWorkspacePath);
			if (!workspace) {
				this.doDeleteUntitledWorkspaceSync(untitledWorkspacePath);

				return null; // invalid workspace
			}

			return { id: workspace.id, configPath: untitledWorkspacePath };
		}));

		return untitledWorkspaces;
	}
B
Benjamin Pasero 已提交
169
}