extHostWorkspace.ts 7.0 KB
Newer Older
E
Erich Gamma 已提交
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';

import URI from 'vs/base/common/uri';
J
Johannes Rieken 已提交
8
import Event, { Emitter } from 'vs/base/common/event';
J
Johannes Rieken 已提交
9
import { normalize } from 'vs/base/common/paths';
10
import { delta } from 'vs/base/common/arrays';
11
import { relative, basename } from 'path';
12
import { Workspace } from 'vs/platform/workspace/common/workspace';
13
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
J
Johannes Rieken 已提交
14
import { TPromise } from 'vs/base/common/winjs.base';
15
import { fromRange, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
16
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol';
17
import * as vscode from 'vscode';
J
Johannes Rieken 已提交
18
import { compare } from "vs/base/common/strings";
J
Johannes Rieken 已提交
19 20
import { asWinJsPromise } from 'vs/base/common/async';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
21
import { TrieMap } from 'vs/base/common/map';
E
Erich Gamma 已提交
22

23
class Workspace2 extends Workspace {
24 25 26 27 28

	static fromData(data: IWorkspaceData) {
		return data ? new Workspace2(data) : null;
	}

29 30
	private readonly _folder: vscode.WorkspaceFolder[] = [];
	private readonly _structure = new TrieMap<vscode.WorkspaceFolder>(s => s.split('/'));
31 32

	private constructor(data: IWorkspaceData) {
33 34
		super(data.id, data.name, data.roots);

35
		// setup the workspace folder data structure
36
		this.roots.forEach((uri, index) => {
37
			const folder = {
38 39 40
				name: basename(uri.fsPath),
				uri,
				index
41 42 43
			};
			this._folder.push(folder);
			this._structure.insert(folder.uri.toString(), folder);
44
		});
45 46
	}

47
	get folders(): vscode.WorkspaceFolder[] {
48
		return this._folder.slice(0);
49 50
	}

51
	getWorkspaceFolder(uri: URI): vscode.WorkspaceFolder {
52 53 54 55 56 57 58 59
		let str = uri.toString();
		let folder = this._structure.lookUp(str);
		if (folder) {
			// `uri` is a workspace folder so we
			let parts = str.split('/');
			while (parts.length) {
				if (parts.pop()) {
					break;
60 61
				}
			}
62
			str = parts.join('/');
63
		}
64
		return this._structure.findSubstr(str);
65 66 67
	}
}

68
export class ExtHostWorkspace extends ExtHostWorkspaceShape {
E
Erich Gamma 已提交
69

70 71
	private static _requestIdPool = 0;

J
Johannes Rieken 已提交
72
	private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
73
	private readonly _proxy: MainThreadWorkspaceShape;
74
	private _workspace: Workspace2;
E
Erich Gamma 已提交
75

J
Johannes Rieken 已提交
76
	readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
J
Johannes Rieken 已提交
77

78
	constructor(mainContext: IMainContext, data: IWorkspaceData) {
79
		super();
80
		this._proxy = mainContext.get(MainContext.MainThreadWorkspace);
81
		this._workspace = Workspace2.fromData(data);
E
Erich Gamma 已提交
82 83
	}

84 85
	// --- workspace ---

J
Johannes Rieken 已提交
86
	get workspace(): Workspace {
87
		return this._workspace;
J
Johannes Rieken 已提交
88 89
	}

90
	getWorkspaceFolders(): vscode.WorkspaceFolder[] {
91 92 93
		if (!this._workspace) {
			return undefined;
		} else {
94 95 96 97
			return this._workspace.folders.slice(0);
		}
	}

98
	getWorkspaceFolder(uri: vscode.Uri): vscode.WorkspaceFolder {
99 100
		if (!this._workspace) {
			return undefined;
101
		}
102
		return this._workspace.getWorkspaceFolder(<URI>uri);
103 104
	}

E
Erich Gamma 已提交
105
	getPath(): string {
J
Johannes Rieken 已提交
106 107 108
		// this is legacy from the days before having
		// multi-root and we keep it only alive if there
		// is just one workspace folder.
109 110 111
		if (!this._workspace) {
			return undefined;
		}
112
		const { roots } = this._workspace;
113 114
		if (roots.length === 0) {
			return undefined;
115
		}
116
		return roots[0].fsPath;
E
Erich Gamma 已提交
117 118
	}

J
Johannes Rieken 已提交
119
	getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string {
E
Erich Gamma 已提交
120 121 122 123

		let path: string;
		if (typeof pathOrUri === 'string') {
			path = pathOrUri;
124
		} else if (typeof pathOrUri !== 'undefined') {
E
Erich Gamma 已提交
125 126 127
			path = pathOrUri.fsPath;
		}

128 129 130 131
		if (!path) {
			return path;
		}

132 133 134 135 136 137
		const folder = this.getWorkspaceFolder(typeof pathOrUri === 'string'
			? URI.file(pathOrUri)
			: pathOrUri
		);

		if (!folder) {
J
Johannes Rieken 已提交
138
			return normalize(path);
E
Erich Gamma 已提交
139 140
		}

J
Johannes Rieken 已提交
141 142 143 144
		if (typeof includeWorkspace === 'undefined') {
			includeWorkspace = this.workspace.roots.length > 1;
		}

145
		let result = relative(folder.uri.fsPath, path);
J
Johannes Rieken 已提交
146
		if (includeWorkspace) {
147
			result = `${folder.name}/${result}`;
148
		}
149
		return normalize(result);
E
Erich Gamma 已提交
150 151
	}

152
	$acceptWorkspaceData(data: IWorkspaceData): void {
J
Johannes Rieken 已提交
153

154 155 156 157
		// keep old workspace folder, build new workspace, and
		// capture new workspace folders. Compute delta between
		// them send that as event
		const oldRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
J
Johannes Rieken 已提交
158

159 160
		this._workspace = Workspace2.fromData(data);
		const newRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
J
Johannes Rieken 已提交
161

162
		const { added, removed } = delta(oldRoots, newRoots, ExtHostWorkspace._compareWorkspaceFolder);
J
Johannes Rieken 已提交
163
		this._onDidChangeWorkspace.fire(Object.freeze({
164 165
			added: Object.freeze<vscode.WorkspaceFolder[]>(added),
			removed: Object.freeze<vscode.WorkspaceFolder[]>(removed)
J
Johannes Rieken 已提交
166 167 168
		}));
	}

169 170
	private static _compareWorkspaceFolder(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
		return compare(a.uri.toString(), b.uri.toString());
171 172 173 174
	}

	// --- search ---

J
Joao Moreno 已提交
175
	findFiles(include: string, exclude: string, maxResults?: number, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {
176 177 178 179 180 181
		const requestId = ExtHostWorkspace._requestIdPool++;
		const result = this._proxy.$startSearch(include, exclude, maxResults, requestId);
		if (token) {
			token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
		}
		return result;
E
Erich Gamma 已提交
182 183 184
	}

	saveAll(includeUntitled?: boolean): Thenable<boolean> {
185
		return this._proxy.$saveAll(includeUntitled);
E
Erich Gamma 已提交
186 187 188 189
	}

	appyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {

190
		let resourceEdits: IResourceEdit[] = [];
E
Erich Gamma 已提交
191 192 193 194 195 196 197 198 199

		let entries = edit.entries();
		for (let entry of entries) {
			let [uri, edits] = entry;

			for (let edit of edits) {
				resourceEdits.push({
					resource: <URI>uri,
					newText: edit.newText,
200 201
					newEol: EndOfLine.from(edit.newEol),
					range: edit.range && fromRange(edit.range)
E
Erich Gamma 已提交
202 203 204 205
				});
			}
		}

206
		return this._proxy.$applyWorkspaceEdit(resourceEdits);
E
Erich Gamma 已提交
207
	}
J
Johannes Rieken 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

	// --- EXPERIMENT: workspace resolver

	private readonly _provider = new Map<number, vscode.FileSystemProvider>();

	public registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable {

		const handle = this._provider.size;
		this._provider.set(handle, provider);
		const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, <URI>e));
		this._proxy.$registerFileSystemProvider(handle, authority);
		return new Disposable(() => {
			this._provider.delete(handle);
			reg.dispose();
		});
	}

	$resolveFile(handle: number, resource: URI): TPromise<string> {
		const provider = this._provider.get(handle);
		return asWinJsPromise(token => provider.resolveContents(resource));
	}

	$storeFile(handle: number, resource: URI, content: string): TPromise<any> {
		const provider = this._provider.get(handle);
		return asWinJsPromise(token => provider.writeContents(resource, content));
	}
E
Erich Gamma 已提交
234
}