extHostWorkspace.ts 8.3 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';
22 23
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Progress } from 'vs/platform/progress/common/progress';
E
Erich Gamma 已提交
24

25
class Workspace2 extends Workspace {
26 27 28 29 30

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

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

	private constructor(data: IWorkspaceData) {
35 36
		super(data.id, data.name, data.roots);

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

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

53
	getWorkspaceFolder(uri: URI): vscode.WorkspaceFolder {
54 55 56 57 58 59 60 61
		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;
62 63
				}
			}
64
			str = parts.join('/');
65
		}
66
		return this._structure.findSubstr(str);
67 68 69
	}
}

70
export class ExtHostWorkspace implements ExtHostWorkspaceShape {
E
Erich Gamma 已提交
71

72 73
	private static _requestIdPool = 0;

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

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

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

85 86
	// --- workspace ---

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

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

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

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

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

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

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

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

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

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

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

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

155 156 157 158
		// 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 已提交
159

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

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

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

	// --- search ---

J
Joao Moreno 已提交
176
	findFiles(include: string, exclude: string, maxResults?: number, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {
177 178 179 180 181 182
		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 已提交
183 184 185
	}

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

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

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

		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,
201 202
					newEol: EndOfLine.from(edit.newEol),
					range: edit.range && fromRange(edit.range)
E
Erich Gamma 已提交
203 204 205 206
				});
			}
		}

207
		return this._proxy.$applyWorkspaceEdit(resourceEdits);
E
Erich Gamma 已提交
208
	}
J
Johannes Rieken 已提交
209 210 211 212

	// --- EXPERIMENT: workspace resolver


213 214 215 216
	private _handlePool = 0;
	private readonly _fsProvider = new Map<number, vscode.FileSystemProvider>();
	private readonly _searchProvider = new Map<number, vscode.SearchProvider>();
	private readonly _searchSession = new Map<number, CancellationTokenSource>();
J
Johannes Rieken 已提交
217

218 219 220
	registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable {
		const handle = ++this._handlePool;
		this._fsProvider.set(handle, provider);
J
Johannes Rieken 已提交
221 222 223
		const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, <URI>e));
		this._proxy.$registerFileSystemProvider(handle, authority);
		return new Disposable(() => {
224
			this._fsProvider.delete(handle);
J
Johannes Rieken 已提交
225 226 227 228 229
			reg.dispose();
		});
	}

	$resolveFile(handle: number, resource: URI): TPromise<string> {
230
		const provider = this._fsProvider.get(handle);
J
Johannes Rieken 已提交
231 232 233 234
		return asWinJsPromise(token => provider.resolveContents(resource));
	}

	$storeFile(handle: number, resource: URI, content: string): TPromise<any> {
235
		const provider = this._fsProvider.get(handle);
J
Johannes Rieken 已提交
236 237
		return asWinJsPromise(token => provider.writeContents(resource, content));
	}
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

	registerSearchProvider(provider: vscode.SearchProvider): vscode.Disposable {
		const handle = ++this._handlePool;
		this._searchProvider.set(handle, provider);
		return new Disposable(() => this._fsProvider.delete(handle));
	}

	$startSearch(handle: number, session: number, query): void {
		const provider = this._searchProvider.get(handle);
		const source = new CancellationTokenSource();
		const progress = new Progress<any>(chunk => this._proxy.$updateSearchSession(session, chunk));

		this._searchSession.set(session, source);
		TPromise.wrap(provider.provideSearchResults(query, progress, source.token)).then(() => {
			this._proxy.$finishSearchSession(session);
		}, err => {
			this._proxy.$finishSearchSession(session, err);
		});
	}

	$cancelSearch(handle: number, session: number): void {
		if (this._searchSession.has(session)) {
			this._searchSession.get(session).cancel();
			this._searchSession.delete(session);
		}
	}
E
Erich Gamma 已提交
264
}