extHostWorkspace.ts 18.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import { join } from 'vs/base/common/path';
7
import { delta as arrayDelta, mapArrayOrNot } from 'vs/base/common/arrays';
8
import { CancellationToken } from 'vs/base/common/cancellation';
R
Rob Lourens 已提交
9
import { Emitter, Event } from 'vs/base/common/event';
10
import { TernarySearchTree } from 'vs/base/common/map';
11
import { Counter } from 'vs/base/common/numbers';
12
import { isLinux } from 'vs/base/common/platform';
13
import { basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources';
R
Rob Lourens 已提交
14
import { compare } from 'vs/base/common/strings';
15
import { URI } from 'vs/base/common/uri';
B
Benjamin Pasero 已提交
16
import { localize } from 'vs/nls';
17
import { ILogService } from 'vs/platform/log/common/log';
R
Rob Lourens 已提交
18
import { Severity } from 'vs/platform/notification/common/notification';
19
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
R
Rob Lourens 已提交
20
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
21
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
22
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
R
Rob Lourens 已提交
23
import * as vscode from 'vscode';
J
Johannes Rieken 已提交
24
import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, IMainContext, MainContext, IStaticWorkspaceData } from './extHost.protocol';
25
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
26
import { Barrier } from 'vs/base/common/async';
27

28 29
export interface IExtHostWorkspaceProvider {
	getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined>;
S
Sandeep Somavarapu 已提交
30
	resolveWorkspaceFolder(uri: vscode.Uri): Promise<vscode.WorkspaceFolder | undefined>;
31 32 33 34
	getWorkspaceFolders2(): Promise<vscode.WorkspaceFolder[] | undefined>;
	resolveProxy(url: string): Promise<string | undefined>;
}

35 36 37
function isFolderEqual(folderA: URI, folderB: URI): boolean {
	return isEqual(folderA, folderB, !isLinux);
}
E
Erich Gamma 已提交
38

B
Benjamin Pasero 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51
function compareWorkspaceFolderByUri(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
	return isFolderEqual(a.uri, b.uri) ? 0 : compare(a.uri.toString(), b.uri.toString());
}

function compareWorkspaceFolderByUriAndNameAndIndex(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
	if (a.index !== b.index) {
		return a.index < b.index ? -1 : 1;
	}

	return isFolderEqual(a.uri, b.uri) ? compare(a.name, b.name) : compare(a.uri.toString(), b.uri.toString());
}

function delta(oldFolders: vscode.WorkspaceFolder[], newFolders: vscode.WorkspaceFolder[], compare: (a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder) => number): { removed: vscode.WorkspaceFolder[], added: vscode.WorkspaceFolder[] } {
B
Benjamin Pasero 已提交
52 53
	const oldSortedFolders = oldFolders.slice(0).sort(compare);
	const newSortedFolders = newFolders.slice(0).sort(compare);
B
Benjamin Pasero 已提交
54 55 56 57

	return arrayDelta(oldSortedFolders, newSortedFolders, compare);
}

58 59 60 61 62
interface MutableWorkspaceFolder extends vscode.WorkspaceFolder {
	name: string;
	index: number;
}

B
Benjamin Pasero 已提交
63
class ExtHostWorkspaceImpl extends Workspace {
64

65
	static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
66
		if (!data) {
67 68 69 70
			return { workspace: null, added: [], removed: [] };
		}

		const { id, name, folders } = data;
71
		const newWorkspaceFolders: vscode.WorkspaceFolder[] = [];
72 73 74 75

		// If we have an existing workspace, we try to find the folders that match our
		// data and update their properties. It could be that an extension stored them
		// for later use and we want to keep them "live" if they are still present.
76
		const oldWorkspace = previousConfirmedWorkspace;
M
Matt Bierner 已提交
77
		if (previousConfirmedWorkspace) {
78 79
			folders.forEach((folderData, index) => {
				const folderUri = URI.revive(folderData.uri);
80
				const existingFolder = ExtHostWorkspaceImpl._findFolder(previousUnconfirmedWorkspace || previousConfirmedWorkspace, folderUri);
81 82 83 84 85 86 87

				if (existingFolder) {
					existingFolder.name = folderData.name;
					existingFolder.index = folderData.index;

					newWorkspaceFolders.push(existingFolder);
				} else {
88
					newWorkspaceFolders.push({ uri: folderUri, name: folderData.name, index });
89 90
				}
			});
91
		} else {
92
			newWorkspaceFolders.push(...folders.map(({ uri, name, index }) => ({ uri: URI.revive(uri), name, index })));
93 94
		}

95 96 97
		// make sure to restore sort order based on index
		newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1);

B
Benjamin Pasero 已提交
98
		const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders);
B
Benjamin Pasero 已提交
99
		const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri);
100 101 102 103

		return { workspace, added, removed };
	}

104
	private static _findFolder(workspace: ExtHostWorkspaceImpl, folderUriToFind: URI): MutableWorkspaceFolder | undefined {
105
		for (let i = 0; i < workspace.folders.length; i++) {
106
			const folder = workspace.workspaceFolders[i];
107 108 109
			if (isFolderEqual(folder.uri, folderUriToFind)) {
				return folder;
			}
110
		}
111 112

		return undefined;
113 114
	}

115
	private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
116
	private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();
117

118
	constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) {
I
isidor 已提交
119
		super(id, folders.map(f => new WorkspaceFolder(f)));
120

121
		// setup the workspace folder data structure
122 123 124
		folders.forEach(folder => {
			this._workspaceFolders.push(folder);
			this._structure.set(folder.uri.toString(), folder);
125
		});
126 127
	}

I
isidor 已提交
128 129 130 131
	get name(): string {
		return this._name;
	}

S
Sandeep Somavarapu 已提交
132
	get workspaceFolders(): vscode.WorkspaceFolder[] {
133
		return this._workspaceFolders.slice(0);
134 135
	}

M
Matt Bierner 已提交
136
	getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder | undefined {
137
		if (resolveParent && this._structure.get(uri.toString())) {
138
			// `uri` is a workspace folder so we check for its parent
M
Martin Aeschlimann 已提交
139
			uri = dirname(uri);
140
		}
141
		return this._structure.findSubstr(uri.toString());
142
	}
D
Dirk Baeumer 已提交
143

M
Matt Bierner 已提交
144
	resolveWorkspaceFolder(uri: URI): vscode.WorkspaceFolder | undefined {
D
Dirk Baeumer 已提交
145 146
		return this._structure.get(uri.toString());
	}
147 148
}

149 150 151 152
export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspaceProvider {

	private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
	readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
E
Erich Gamma 已提交
153

154 155 156
	private readonly _logService: ILogService;
	private readonly _requestIdProvider: Counter;
	private readonly _barrier: Barrier;
157 158 159 160 161 162 163 164

	private _confirmedWorkspace?: ExtHostWorkspaceImpl;
	private _unconfirmedWorkspace?: ExtHostWorkspaceImpl;

	private readonly _proxy: MainThreadWorkspaceShape;
	private readonly _messageService: MainThreadMessageServiceShape;

	private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
165 166 167 168

	constructor(
		mainContext: IMainContext,
		logService: ILogService,
169 170
		requestIdProvider: Counter,
		data?: IStaticWorkspaceData
171 172 173 174 175
	) {
		this._logService = logService;
		this._requestIdProvider = requestIdProvider;
		this._barrier = new Barrier();

176 177 178
		this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
		this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
		this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, []) : undefined;
179 180 181
	}

	$initializeWorkspace(data: IWorkspaceData): void {
182
		this.$acceptWorkspaceData(data);
183 184 185
		this._barrier.open();
	}

186 187
	waitForInitializeCall(): Promise<boolean> {
		return this._barrier.wait();
E
Erich Gamma 已提交
188 189
	}

190 191
	// --- workspace ---

M
Matt Bierner 已提交
192
	get workspace(): Workspace | undefined {
193 194 195
		return this._actualWorkspace;
	}

196
	get name(): string | undefined {
I
isidor 已提交
197 198 199
		return this._actualWorkspace ? this._actualWorkspace.name : undefined;
	}

M
Matt Bierner 已提交
200
	private get _actualWorkspace(): ExtHostWorkspaceImpl | undefined {
201
		return this._unconfirmedWorkspace || this._confirmedWorkspace;
J
Johannes Rieken 已提交
202 203
	}

204
	getWorkspaceFolders(): vscode.WorkspaceFolder[] | undefined {
205
		if (!this._actualWorkspace) {
206
			return undefined;
207
		}
208
		return this._actualWorkspace.workspaceFolders.slice(0);
209 210
	}

211 212 213 214 215 216 217 218
	async getWorkspaceFolders2(): Promise<vscode.WorkspaceFolder[] | undefined> {
		await this._barrier.wait();
		if (!this._actualWorkspace) {
			return undefined;
		}
		return this._actualWorkspace.workspaceFolders.slice(0);
	}

B
Benjamin Pasero 已提交
219
	updateWorkspaceFolders(extension: IExtensionDescription, index: number, deleteCount: number, ...workspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[]): boolean {
B
Benjamin Pasero 已提交
220
		const validatedDistinctWorkspaceFoldersToAdd: { uri: vscode.Uri, name?: string }[] = [];
221 222
		if (Array.isArray(workspaceFoldersToAdd)) {
			workspaceFoldersToAdd.forEach(folderToAdd => {
223
				if (URI.isUri(folderToAdd.uri) && !validatedDistinctWorkspaceFoldersToAdd.some(f => isFolderEqual(f.uri, folderToAdd.uri))) {
B
Benjamin Pasero 已提交
224
					validatedDistinctWorkspaceFoldersToAdd.push({ uri: folderToAdd.uri, name: folderToAdd.name || basenameOrAuthority(folderToAdd.uri) });
225 226 227 228
				}
			});
		}

B
Benjamin Pasero 已提交
229 230 231 232
		if (!!this._unconfirmedWorkspace) {
			return false; // prevent accumulated calls without a confirmed workspace
		}

B
Benjamin Pasero 已提交
233
		if ([index, deleteCount].some(i => typeof i !== 'number' || i < 0)) {
234
			return false; // validate numbers
235 236
		}

B
Benjamin Pasero 已提交
237
		if (deleteCount === 0 && validatedDistinctWorkspaceFoldersToAdd.length === 0) {
238
			return false; // nothing to delete or add
239 240
		}

B
Benjamin Pasero 已提交
241
		const currentWorkspaceFolders: MutableWorkspaceFolder[] = this._actualWorkspace ? this._actualWorkspace.workspaceFolders : [];
242
		if (index + deleteCount > currentWorkspaceFolders.length) {
243
			return false; // cannot delete more than we have
244 245
		}

B
Benjamin Pasero 已提交
246
		// Simulate the updateWorkspaceFolders method on our data to do more validation
247
		const newWorkspaceFolders = currentWorkspaceFolders.slice(0);
M
Matt Bierner 已提交
248
		newWorkspaceFolders.splice(index, deleteCount, ...validatedDistinctWorkspaceFoldersToAdd.map(f => ({ uri: f.uri, name: f.name || basenameOrAuthority(f.uri), index: undefined! /* fixed later */ })));
B
Benjamin Pasero 已提交
249 250 251 252 253 254 255 256

		for (let i = 0; i < newWorkspaceFolders.length; i++) {
			const folder = newWorkspaceFolders[i];
			if (newWorkspaceFolders.some((otherFolder, index) => index !== i && isFolderEqual(folder.uri, otherFolder.uri))) {
				return false; // cannot add the same folder multiple times
			}
		}

B
Benjamin Pasero 已提交
257
		newWorkspaceFolders.forEach((f, index) => f.index = index); // fix index
B
Benjamin Pasero 已提交
258
		const { added, removed } = delta(currentWorkspaceFolders, newWorkspaceFolders, compareWorkspaceFolderByUriAndNameAndIndex);
259
		if (added.length === 0 && removed.length === 0) {
260 261 262 263
			return false; // nothing actually changed
		}

		// Trigger on main side
B
Benjamin Pasero 已提交
264
		if (this._proxy) {
B
Benjamin Pasero 已提交
265
			const extName = extension.displayName || extension.name;
R
Rob Lourens 已提交
266
			this._proxy.$updateWorkspaceFolders(extName, index, deleteCount, validatedDistinctWorkspaceFoldersToAdd).then(undefined, error => {
B
Benjamin Pasero 已提交
267 268 269 270 271 272

				// in case of an error, make sure to clear out the unconfirmed workspace
				// because we cannot expect the acknowledgement from the main side for this
				this._unconfirmedWorkspace = undefined;

				// show error to user
273
				this._messageService.$showMessage(Severity.Error, localize('updateerror', "Extension '{0}' failed to update workspace folders: {1}", extName, error), { extension }, []);
B
Benjamin Pasero 已提交
274
			});
B
Benjamin Pasero 已提交
275
		}
276

B
Benjamin Pasero 已提交
277
		// Try to accept directly
B
Benjamin Pasero 已提交
278 279 280
		this.trySetWorkspaceFolders(newWorkspaceFolders);

		return true;
281 282
	}

M
Matt Bierner 已提交
283
	getWorkspaceFolder(uri: vscode.Uri, resolveParent?: boolean): vscode.WorkspaceFolder | undefined {
284
		if (!this._actualWorkspace) {
285
			return undefined;
286
		}
287
		return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
288 289
	}

290 291 292 293 294 295 296 297
	async getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined> {
		await this._barrier.wait();
		if (!this._actualWorkspace) {
			return undefined;
		}
		return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
	}

S
Sandeep Somavarapu 已提交
298
	async resolveWorkspaceFolder(uri: vscode.Uri): Promise<vscode.WorkspaceFolder | undefined> {
299 300 301 302 303 304 305
		await this._barrier.wait();
		if (!this._actualWorkspace) {
			return undefined;
		}
		return this._actualWorkspace.resolveWorkspaceFolder(uri);
	}

306
	getPath(): string | undefined {
307

J
Johannes Rieken 已提交
308 309 310
		// this is legacy from the days before having
		// multi-root and we keep it only alive if there
		// is just one workspace folder.
311
		if (!this._actualWorkspace) {
312 313
			return undefined;
		}
314 315

		const { folders } = this._actualWorkspace;
S
Sandeep Somavarapu 已提交
316
		if (folders.length === 0) {
317
			return undefined;
318
		}
319
		// #54483 @Joh Why are we still using fsPath?
320
		return folders[0].uri.fsPath;
E
Erich Gamma 已提交
321 322
	}

323
	getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string {
E
Erich Gamma 已提交
324

325
		let resource: URI | undefined;
326
		let path: string = '';
E
Erich Gamma 已提交
327
		if (typeof pathOrUri === 'string') {
328
			resource = URI.file(pathOrUri);
M
Martin Aeschlimann 已提交
329
			path = pathOrUri;
330
		} else if (typeof pathOrUri !== 'undefined') {
331
			resource = pathOrUri;
M
Martin Aeschlimann 已提交
332
			path = pathOrUri.fsPath;
E
Erich Gamma 已提交
333 334
		}

335
		if (!resource) {
M
Martin Aeschlimann 已提交
336
			return path;
337 338
		}

339
		const folder = this.getWorkspaceFolder(
340
			resource,
341
			true
342 343 344
		);

		if (!folder) {
M
Martin Aeschlimann 已提交
345
			return path;
E
Erich Gamma 已提交
346 347
		}

M
Matt Bierner 已提交
348
		if (typeof includeWorkspace === 'undefined' && this._actualWorkspace) {
349
			includeWorkspace = this._actualWorkspace.folders.length > 1;
J
Johannes Rieken 已提交
350 351
		}

352 353
		let result = relativePath(folder.uri, resource);
		if (includeWorkspace && folder.name) {
354
			result = `${folder.name}/${result}`;
355
		}
356
		return result!;
E
Erich Gamma 已提交
357 358
	}

B
Benjamin Pasero 已提交
359
	private trySetWorkspaceFolders(folders: vscode.WorkspaceFolder[]): void {
B
Benjamin Pasero 已提交
360 361 362 363

		// Update directly here. The workspace is unconfirmed as long as we did not get an
		// acknowledgement from the main side (via $acceptWorkspaceData)
		if (this._actualWorkspace) {
B
Benjamin Pasero 已提交
364 365 366 367 368
			this._unconfirmedWorkspace = ExtHostWorkspaceImpl.toExtHostWorkspace({
				id: this._actualWorkspace.id,
				name: this._actualWorkspace.name,
				configuration: this._actualWorkspace.configuration,
				folders
M
Matt Bierner 已提交
369
			} as IWorkspaceData, this._actualWorkspace).workspace || undefined;
B
Benjamin Pasero 已提交
370 371 372
		}
	}

373
	$acceptWorkspaceData(data: IWorkspaceData): void {
J
Johannes Rieken 已提交
374

B
Benjamin Pasero 已提交
375
		const { workspace, added, removed } = ExtHostWorkspaceImpl.toExtHostWorkspace(data, this._confirmedWorkspace, this._unconfirmedWorkspace);
J
Johannes Rieken 已提交
376

377 378
		// Update our workspace object. We have a confirmed workspace, so we drop our
		// unconfirmed workspace.
M
Matt Bierner 已提交
379
		this._confirmedWorkspace = workspace || undefined;
380
		this._unconfirmedWorkspace = undefined;
J
Johannes Rieken 已提交
381

382
		// Events
383
		this._onDidChangeWorkspace.fire(Object.freeze({
384 385
			added,
			removed,
386
		}));
387 388
	}

389 390
	// --- search ---

391
	findFiles(include: string | RelativePattern | undefined, exclude: vscode.GlobPattern | undefined, maxResults: number | undefined, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
392
		this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId.value}, entryPoint: findFiles`);
393

394 395
		let includePattern: string | undefined;
		let includeFolder: URI | undefined;
396 397 398 399 400
		if (include) {
			if (typeof include === 'string') {
				includePattern = include;
			} else {
				includePattern = include.pattern;
401 402 403

				// include.base must be an absolute path
				includeFolder = include.baseFolder || URI.file(include.base);
404 405 406
			}
		}

R
Rob Lourens 已提交
407
		let excludePatternOrDisregardExcludes: string | false | undefined = undefined;
J
Johannes Rieken 已提交
408 409 410
		if (exclude === null) {
			excludePatternOrDisregardExcludes = false;
		} else if (exclude) {
411
			if (typeof exclude === 'string') {
J
Johannes Rieken 已提交
412
				excludePatternOrDisregardExcludes = exclude;
413
			} else {
J
Johannes Rieken 已提交
414
				excludePatternOrDisregardExcludes = exclude.pattern;
415 416 417
			}
		}

418
		if (token && token.isCancellationRequested) {
J
Johannes Rieken 已提交
419
			return Promise.resolve([]);
420 421
		}

R
Rob Lourens 已提交
422 423
		return this._proxy.$startFileSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, token)
			.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
E
Erich Gamma 已提交
424 425
	}

426
	findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete | undefined> {
427
		this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles`);
R
Rob Lourens 已提交
428

429
		const requestId = this._requestIdProvider.getNext();
R
Rob Lourens 已提交
430

431 432 433 434 435 436 437 438
		const globPatternToString = (pattern: vscode.GlobPattern | string) => {
			if (typeof pattern === 'string') {
				return pattern;
			}

			return join(pattern.base, pattern.pattern);
		};

439 440
		const previewOptions: vscode.TextSearchPreviewOptions = typeof options.previewOptions === 'undefined' ?
			{
R
Rob Lourens 已提交
441 442
				matchLines: 100,
				charsPerLine: 10000
443 444 445
			} :
			options.previewOptions;

446
		const queryOptions: ITextQueryBuilderOptions = {
R
Rob Lourens 已提交
447 448
			ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
			disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
P
pkoushik 已提交
449
			disregardGlobalIgnoreFiles: typeof options.useGlobalIgnoreFiles === 'boolean' ? !options.useGlobalIgnoreFiles : undefined,
450
			disregardExcludeSettings: options.exclude === null,
R
Rob Lourens 已提交
451 452
			fileEncoding: options.encoding,
			maxResults: options.maxResults,
453
			previewOptions,
454 455
			afterContext: options.afterContext,
			beforeContext: options.beforeContext,
R
Rob Lourens 已提交
456

457
			includePattern: options.include && globPatternToString(options.include),
458
			excludePattern: options.exclude ? globPatternToString(options.exclude) : undefined
R
Rob Lourens 已提交
459 460
		};

461
		const isCanceled = false;
462

R
Rob Lourens 已提交
463
		this._activeSearchCallbacks[requestId] = p => {
464 465 466 467
			if (isCanceled) {
				return;
			}

468
			const uri = URI.revive(p.resource);
469
			p.results!.forEach(result => {
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
				if (resultIsMatch(result)) {
					callback(<vscode.TextSearchMatch>{
						uri,
						preview: {
							text: result.preview.text,
							matches: mapArrayOrNot(
								result.preview.matches,
								m => new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn))
						},
						ranges: mapArrayOrNot(
							result.ranges,
							r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn))
					});
				} else {
					callback(<vscode.TextSearchContext>{
						uri,
						text: result.text,
						lineNumber: result.lineNumber
					});
				}
R
Rob Lourens 已提交
490 491 492
			});
		};

R
Rob Lourens 已提交
493
		if (token.isCancellationRequested) {
J
Johannes Rieken 已提交
494
			return Promise.resolve(undefined);
R
Rob Lourens 已提交
495 496
		}

J
Johannes Rieken 已提交
497 498 499 500 501 502 503
		return this._proxy.$startTextSearch(query, queryOptions, requestId, token).then(result => {
			delete this._activeSearchCallbacks[requestId];
			return result;
		}, err => {
			delete this._activeSearchCallbacks[requestId];
			return Promise.reject(err);
		});
R
Rob Lourens 已提交
504 505 506 507 508 509 510 511
	}

	$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
		if (this._activeSearchCallbacks[requestId]) {
			this._activeSearchCallbacks[requestId](result);
		}
	}

J
Johannes Rieken 已提交
512
	saveAll(includeUntitled?: boolean): Promise<boolean> {
513
		return this._proxy.$saveAll(includeUntitled);
E
Erich Gamma 已提交
514
	}
515

516
	resolveProxy(url: string): Promise<string | undefined> {
517 518
		return this._proxy.$resolveProxy(url);
	}
E
Erich Gamma 已提交
519
}