mainThreadFileSystem.ts 7.4 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

7
import URI, { UriComponents } from 'vs/base/common/uri';
J
Johannes Rieken 已提交
8
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
J
Johannes Rieken 已提交
9
import { ExtHostContext, MainContext, IExtHostContext, MainThreadFileSystemShape, ExtHostFileSystemShape, IFileChangeDto } from '../node/extHost.protocol';
10
import { IFileService, IStat, IFileChange, ISimpleReadWriteProvider, IFileSystemProviderBase } from 'vs/platform/files/common/files';
11
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
M
Matt Bierner 已提交
12
import { Event, Emitter } from 'vs/base/common/event';
13
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
14 15
import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService, ILineMatch } from 'vs/platform/search/common/search';
import { values } from 'vs/base/common/map';
16
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
17 18 19 20 21

@extHostNamedCustomer(MainContext.MainThreadFileSystem)
export class MainThreadFileSystem implements MainThreadFileSystemShape {

	private readonly _proxy: ExtHostFileSystemShape;
22 23
	private readonly _fileProvider = new Map<number, RemoteFileSystemProvider>();
	private readonly _searchProvider = new Map<number, RemoteSearchProvider>();
24 25 26 27

	constructor(
		extHostContext: IExtHostContext,
		@IFileService private readonly _fileService: IFileService,
28
		@ISearchService private readonly _searchService: ISearchService
29
	) {
30
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem);
31 32 33
	}

	dispose(): void {
34 35
		this._fileProvider.forEach(value => dispose());
		this._fileProvider.clear();
36 37 38
	}

	$registerFileSystemProvider(handle: number, scheme: string): void {
39
		this._fileProvider.set(handle, new RemoteFileSystemProvider(this._fileService, scheme, handle, this._proxy));
40 41
	}

42 43 44 45 46 47 48 49 50 51
	$registerSearchProvider(handle: number, scheme: string): void {
		this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, scheme, handle, this._proxy));
	}

	$unregisterProvider(handle: number): void {
		dispose(this._fileProvider.get(handle));
		this._fileProvider.delete(handle);

		dispose(this._searchProvider.get(handle));
		this._searchProvider.delete(handle);
52 53
	}

J
Johannes Rieken 已提交
54
	$onFileSystemChange(handle: number, changes: IFileChangeDto[]): void {
55
		this._fileProvider.get(handle).$onFileSystemChange(changes);
56
	}
J
Johannes Rieken 已提交
57 58
	// --- search

59
	$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void {
60
		this._searchProvider.get(handle).handleFindMatch(session, data);
J
Johannes Rieken 已提交
61
	}
62 63
}

64 65 66
class RemoteFileSystemProvider implements ISimpleReadWriteProvider, IFileSystemProviderBase {

	_type: 'simple' = 'simple';
67 68

	private readonly _onDidChange = new Emitter<IFileChange[]>();
J
Johannes Rieken 已提交
69
	private readonly _registrations: IDisposable[];
70 71 72 73

	readonly onDidChange: Event<IFileChange[]> = this._onDidChange.event;

	constructor(
J
Johannes Rieken 已提交
74
		fileService: IFileService,
75
		scheme: string,
76 77 78
		private readonly _handle: number,
		private readonly _proxy: ExtHostFileSystemShape
	) {
79
		this._registrations = [fileService.registerProvider(scheme, this)];
80 81 82
	}

	dispose(): void {
J
Johannes Rieken 已提交
83
		dispose(this._registrations);
84 85 86
		this._onDidChange.dispose();
	}

J
Johannes Rieken 已提交
87 88 89 90 91 92
	$onFileSystemChange(changes: IFileChangeDto[]): void {
		this._onDidChange.fire(changes.map(RemoteFileSystemProvider._createFileChange));
	}

	private static _createFileChange(dto: IFileChangeDto): IFileChange {
		return { resource: URI.revive(dto.resource), type: dto.type };
93 94 95 96 97 98 99
	}

	// --- forwarding calls

	stat(resource: URI): TPromise<IStat, any> {
		return this._proxy.$stat(this._handle, resource);
	}
100 101 102
	readFile(resource: URI): TPromise<Uint8Array, any> {
		return this._proxy.$readFile(this._handle, resource).then(encoded => {
			return Buffer.from(encoded, 'base64');
103
		});
104
	}
105
	writeFile(resource: URI, content: Uint8Array): TPromise<void, any> {
J
Johannes Rieken 已提交
106 107
		let encoded = Buffer.isBuffer(content)
			? content.toString('base64')
J
Johannes Rieken 已提交
108
			: Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64');
109
		return this._proxy.$writeFile(this._handle, resource, encoded);
110
	}
111 112
	delete(resource: URI): TPromise<void, any> {
		return this._proxy.$delete(this._handle, resource);
113
	}
J
Johannes Rieken 已提交
114
	rename(resource: URI, target: URI): TPromise<IStat, any> {
115
		return this._proxy.$move(this._handle, resource, target);
116
	}
117
	mkdir(resource: URI): TPromise<IStat, any> {
118 119
		return this._proxy.$mkdir(this._handle, resource);
	}
120 121
	readdir(resource: URI): TPromise<[string, IStat][], any> {
		return this._proxy.$readdir(this._handle, resource);
122
	}
123
}
J
Johannes Rieken 已提交
124

125 126 127 128 129 130 131 132 133 134 135
class SearchOperation {

	private static _idPool = 0;

	constructor(
		readonly progress: (match: IFileMatch) => any,
		readonly id: number = ++SearchOperation._idPool,
		readonly matches = new Map<string, IFileMatch>()
	) {
		//
	}
136 137 138 139 140 141 142 143 144 145

	addMatch(resource: URI, match: ILineMatch): void {
		if (!this.matches.has(resource.toString())) {
			this.matches.set(resource.toString(), { resource, lineMatches: [] });
		}
		if (match) {
			this.matches.get(resource.toString()).lineMatches.push(match);
		}
		this.progress(this.matches.get(resource.toString()));
	}
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
}

class RemoteSearchProvider implements ISearchResultProvider {

	private readonly _registrations: IDisposable[];
	private readonly _searches = new Map<number, SearchOperation>();


	constructor(
		searchService: ISearchService,
		private readonly _scheme: string,
		private readonly _handle: number,
		private readonly _proxy: ExtHostFileSystemShape
	) {
		this._registrations = [searchService.registerSearchResultProvider(this)];
	}

	dispose(): void {
		dispose(this._registrations);
	}
J
Johannes Rieken 已提交
166 167

	search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182
		if (isFalsyOrEmpty(query.folderQueries)) {
			return PPromise.as(undefined);
		}

		let includes = { ...query.includePattern };
		let excludes = { ...query.excludePattern };

		for (const folderQuery of query.folderQueries) {
			if (folderQuery.folder.scheme === this._scheme) {
				includes = { ...includes, ...folderQuery.includePattern };
				excludes = { ...excludes, ...folderQuery.excludePattern };
			}
		}

183 184
		let outer: TPromise;

J
Johannes Rieken 已提交
185
		return new PPromise((resolve, reject, report) => {
186 187 188 189

			const search = new SearchOperation(report);
			this._searches.set(search.id, search);

190 191
			outer = query.type === QueryType.File
				? this._proxy.$provideFileSearchResults(this._handle, search.id, query.filePattern)
192
				: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
193

194
			outer.then(() => {
195 196 197 198 199
				this._searches.delete(search.id);
				resolve(({ results: values(search.matches), stats: undefined }));
			}, err => {
				this._searches.delete(search.id);
				reject(err);
J
Johannes Rieken 已提交
200
			});
201 202 203 204
		}, () => {
			if (outer) {
				outer.cancel();
			}
J
Johannes Rieken 已提交
205 206 207
		});
	}

208
	handleFindMatch(session: number, dataOrUri: UriComponents | [UriComponents, ILineMatch]): void {
209 210 211 212
		if (!this._searches.has(session)) {
			// ignore...
			return;
		}
213 214 215 216 217 218 219 220 221 222
		let resource: URI;
		let match: ILineMatch;

		if (Array.isArray(dataOrUri)) {
			resource = URI.revive(dataOrUri[0]);
			match = dataOrUri[1];
		} else {
			resource = URI.revive(dataOrUri);
		}

223
		this._searches.get(session).addMatch(resource, match);
J
Johannes Rieken 已提交
224
	}
225
}