mainThreadFileSystem.ts 7.7 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 11 12 13 14
import { IFileService, IFileSystemProvider, IStat, IFileChange } from 'vs/platform/files/common/files';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IProgress } from 'vs/platform/progress/common/progress';
15
import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService, ILineMatch } from 'vs/platform/search/common/search';
16
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
17
import { onUnexpectedError } from 'vs/base/common/errors';
18
import { values } from 'vs/base/common/map';
19
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
20 21 22 23 24 25 26 27 28 29

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

	private readonly _proxy: ExtHostFileSystemShape;
	private readonly _provider = new Map<number, RemoteFileSystemProvider>();

	constructor(
		extHostContext: IExtHostContext,
		@IFileService private readonly _fileService: IFileService,
J
Johannes Rieken 已提交
30
		@ISearchService private readonly _searchService: ISearchService,
31
		@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService
32
	) {
33
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem);
34 35 36
	}

	dispose(): void {
37 38
		this._provider.forEach(value => dispose());
		this._provider.clear();
39 40 41
	}

	$registerFileSystemProvider(handle: number, scheme: string): void {
J
Johannes Rieken 已提交
42
		this._provider.set(handle, new RemoteFileSystemProvider(this._fileService, this._searchService, scheme, handle, this._proxy));
43 44 45 46 47 48 49
	}

	$unregisterFileSystemProvider(handle: number): void {
		dispose(this._provider.get(handle));
		this._provider.delete(handle);
	}

50 51
	$onDidAddFileSystemRoot(data: UriComponents): void {
		this._workspaceEditingService.addFolders([{ uri: URI.revive(data) }], true).done(null, onUnexpectedError);
52 53
	}

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

J
Johannes Rieken 已提交
58 59
	$reportFileChunk(handle: number, session: number, chunk: number[]): void {
		this._provider.get(handle).reportFileChunk(session, chunk);
60
	}
J
Johannes Rieken 已提交
61 62 63

	// --- search

64 65
	$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void {
		this._provider.get(handle).handleFindMatch(session, data);
J
Johannes Rieken 已提交
66
	}
67 68
}

J
Johannes Rieken 已提交
69 70 71 72 73 74 75 76 77 78 79 80
class FileReadOperation {

	private static _idPool = 0;

	constructor(
		readonly progress: IProgress<Uint8Array>,
		readonly id: number = ++FileReadOperation._idPool
	) {
		//
	}
}

81 82 83 84 85 86 87 88 89 90 91 92 93
class SearchOperation {

	private static _idPool = 0;

	constructor(
		readonly progress: (match: IFileMatch) => any,
		readonly id: number = ++SearchOperation._idPool,
		readonly matches = new Map<string, IFileMatch>()
	) {
		//
	}
}

J
Johannes Rieken 已提交
94
class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProvider {
95 96

	private readonly _onDidChange = new Emitter<IFileChange[]>();
J
Johannes Rieken 已提交
97
	private readonly _registrations: IDisposable[];
98 99
	private readonly _reads = new Map<number, FileReadOperation>();
	private readonly _searches = new Map<number, SearchOperation>();
100 101 102 103 104

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


	constructor(
J
Johannes Rieken 已提交
105 106
		fileService: IFileService,
		searchService: ISearchService,
107
		private readonly _scheme: string,
108 109 110
		private readonly _handle: number,
		private readonly _proxy: ExtHostFileSystemShape
	) {
J
Johannes Rieken 已提交
111
		this._registrations = [
112
			fileService.registerProvider(_scheme, this),
J
Johannes Rieken 已提交
113 114
			searchService.registerSearchResultProvider(this),
		];
115 116 117
	}

	dispose(): void {
J
Johannes Rieken 已提交
118
		dispose(this._registrations);
119 120 121
		this._onDidChange.dispose();
	}

J
Johannes Rieken 已提交
122 123 124 125 126 127
	$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 };
128 129 130 131
	}

	// --- forwarding calls

J
Johannes Rieken 已提交
132 133
	utimes(resource: URI, mtime: number, atime: number): TPromise<IStat, any> {
		return this._proxy.$utimes(this._handle, resource, mtime, atime);
134 135 136 137
	}
	stat(resource: URI): TPromise<IStat, any> {
		return this._proxy.$stat(this._handle, resource);
	}
138
	read(resource: URI, offset: number, count: number, progress: IProgress<Uint8Array>): TPromise<number, any> {
J
Johannes Rieken 已提交
139 140
		const read = new FileReadOperation(progress);
		this._reads.set(read.id, read);
141 142 143 144
		return this._proxy.$read(this._handle, read.id, offset, count, resource).then(value => {
			this._reads.delete(read.id);
			return value;
		});
145
	}
J
Johannes Rieken 已提交
146 147
	reportFileChunk(session: number, chunk: number[]): void {
		this._reads.get(session).progress.report(Buffer.from(chunk));
148 149 150 151 152 153 154
	}
	write(resource: URI, content: Uint8Array): TPromise<void, any> {
		return this._proxy.$write(this._handle, resource, [].slice.call(content));
	}
	unlink(resource: URI): TPromise<void, any> {
		return this._proxy.$unlink(this._handle, resource);
	}
155 156
	move(resource: URI, target: URI): TPromise<IStat, any> {
		return this._proxy.$move(this._handle, resource, target);
157
	}
158
	mkdir(resource: URI): TPromise<IStat, any> {
159 160
		return this._proxy.$mkdir(this._handle, resource);
	}
161
	readdir(resource: URI): TPromise<[URI, IStat][], any> {
J
Johannes Rieken 已提交
162 163 164
		return this._proxy.$readdir(this._handle, resource).then(data => {
			return data.map(tuple => <[URI, IStat]>[URI.revive(tuple[0]), tuple[1]]);
		});
165 166 167 168
	}
	rmdir(resource: URI): TPromise<void, any> {
		return this._proxy.$rmdir(this._handle, resource);
	}
J
Johannes Rieken 已提交
169 170 171 172

	// --- search

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

174 175 176 177 178 179 180 181 182 183 184 185 186 187
		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 };
			}
		}

J
Johannes Rieken 已提交
188
		return new PPromise((resolve, reject, report) => {
189 190 191 192 193 194

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

			const promise = query.type === QueryType.File
				? this._proxy.$findFiles(this._handle, search.id, query.filePattern)
195
				: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
196 197 198 199 200 201 202

			promise.then(() => {
				this._searches.delete(search.id);
				resolve(({ results: values(search.matches), stats: undefined }));
			}, err => {
				this._searches.delete(search.id);
				reject(err);
J
Johannes Rieken 已提交
203 204 205 206
			});
		});
	}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	handleFindMatch(session: number, dataOrUri: UriComponents | [UriComponents, ILineMatch]): void {
		let resource: URI;
		let match: ILineMatch;

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

		const { matches } = this._searches.get(session);
		if (!matches.has(resource.toString())) {
			matches.set(resource.toString(), { resource, lineMatches: [] });
		}
		if (match) {
			matches.get(resource.toString()).lineMatches.push(match);
		}
J
Johannes Rieken 已提交
225
	}
226
}