diff --git a/extensions/search-rg/src/extension.ts b/extensions/search-rg/src/extension.ts index f999cb0cb6c..2a7baff9b32 100644 --- a/extensions/search-rg/src/extension.ts +++ b/extensions/search-rg/src/extension.ts @@ -13,12 +13,13 @@ export function activate(): void { const outputChannel = vscode.window.createOutputChannel('search-rg'); const provider = new RipgrepSearchProvider(outputChannel); vscode.workspace.registerSearchProvider('file', provider); + vscode.workspace.registerTextSearchProvider('file', provider); } } type SearchEngine = RipgrepFileSearchEngine | RipgrepTextSearchEngine; -class RipgrepSearchProvider implements vscode.SearchProvider { +class RipgrepSearchProvider implements vscode.SearchProvider, vscode.TextSearchProvider { private cachedProvider: CachedSearchProvider; private inProgress: Set = new Set(); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 42cea59c678..34941463e72 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -160,6 +160,25 @@ declare module 'vscode' { preview: TextSearchResultPreview; } + // interface FileIndexProvider { + // provideFileIndex(options: FileSearchOptions, token: CancellationToken): Thenable + // } + + // interface FileSearchProvider { + // provideFileSearchResults(query: FileSear, options, token): Thenable + // } + + interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults?(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): Thenable; + } + /** * A SearchProvider provides search results for files or text in files. It can be invoked by quickopen, the search viewlet, and other extensions. */ @@ -178,15 +197,6 @@ declare module 'vscode' { * @param cacheKey The same key that was passed as `query.cacheKey`. */ clearCache?(cacheKey: string): void; - - /** - * Provide results that match the given text pattern. - * @param query The parameters for this query. - * @param options A set of options to consider while searching. - * @param progress A progress callback that must be invoked for all results. - * @param token A cancellation token. - */ - provideTextSearchResults?(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): Thenable; } /** @@ -243,6 +253,17 @@ declare module 'vscode' { */ export function registerSearchProvider(scheme: string, provider: SearchProvider): Disposable; + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + /** * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 7f33441ca83..fefa5e6ebad 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -585,6 +585,9 @@ export function createApiFactory( registerSearchProvider: proposedApiFunction(extension, (scheme, provider) => { return extHostSearch.registerSearchProvider(scheme, provider); }), + registerTextSearchProvider: proposedApiFunction(extension, (scheme, provider) => { + return extHostSearch.registerTextSearchProvider(scheme, provider); + }), registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => { return exthostCommentProviders.registerDocumentCommentProvider(provider); }), diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index e7bcb39550d..dc0a664b713 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -25,6 +25,7 @@ export class ExtHostSearch implements ExtHostSearchShape { private readonly _proxy: MainThreadSearchShape; private readonly _searchProvider = new Map(); + private readonly _textSearchProvider = new Map(); private _handlePool: number = 0; private _fileSearchManager: FileSearchManager; @@ -51,6 +52,16 @@ export class ExtHostSearch implements ExtHostSearchShape { }); } + registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider) { + const handle = this._handlePool++; + this._textSearchProvider.set(handle, provider); + this._proxy.$registerSearchProvider(handle, this._transformScheme(scheme)); + return toDisposable(() => { + this._searchProvider.delete(handle); + this._proxy.$unregisterProvider(handle); + }); + } + $provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery): TPromise { const provider = this._searchProvider.get(handle); if (!provider.provideFileSearchResults) { @@ -74,7 +85,7 @@ export class ExtHostSearch implements ExtHostSearchShape { } $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, rawQuery: IRawSearchQuery): TPromise { - const provider = this._searchProvider.get(handle); + const provider = this._textSearchProvider.get(handle); if (!provider.provideTextSearchResults) { return TPromise.as(undefined); } @@ -363,7 +374,7 @@ class TextSearchEngine { private resultCount = 0; private isCanceled: boolean; - constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.SearchProvider, private _extfs: typeof extfs) { + constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) { } public cancel(): void { diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index bad1d62b9d7..a0f9038ea06 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -32,6 +32,10 @@ class MockMainThreadSearch implements MainThreadSearchShape { this.lastHandle = handle; } + $registerTextSearchProvider(handle: number, scheme: string): void { + this.lastHandle = handle; + } + $unregisterProvider(handle: number): void { } @@ -53,6 +57,11 @@ class MockMainThreadSearch implements MainThreadSearchShape { let mockExtfs: Partial; suite('ExtHostSearch', () => { + async function registerTestTextSearchProvider(provider: vscode.TextSearchProvider, scheme = 'file'): Promise { + disposables.push(extHostSearch.registerTextSearchProvider(scheme, provider)); + await rpcProtocol.sync(); + } + async function registerTestSearchProvider(provider: vscode.SearchProvider, scheme = 'file'): Promise { disposables.push(extHostSearch.registerSearchProvider(scheme, provider)); await rpcProtocol.sync(); @@ -734,7 +743,7 @@ suite('ExtHostSearch', () => { } test('no results', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { return TPromise.wrap(null); } @@ -751,7 +760,7 @@ suite('ExtHostSearch', () => { makeTextResult(rootFolderA, 'file2.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); @@ -764,7 +773,7 @@ suite('ExtHostSearch', () => { }); test('all provider calls get global include/excludes', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { assert.equal(options.includes.length, 1); assert.equal(options.excludes.length, 1); @@ -793,7 +802,7 @@ suite('ExtHostSearch', () => { }); test('global/local include/excludes combined', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { if (options.folder.toString() === rootFolderA.toString()) { assert.deepEqual(options.includes.sort(), ['*.ts', 'foo']); @@ -834,7 +843,7 @@ suite('ExtHostSearch', () => { }); test('include/excludes resolved correctly', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); @@ -871,7 +880,7 @@ suite('ExtHostSearch', () => { }); test('provider fail', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { throw new Error('Provider fail'); } @@ -902,7 +911,7 @@ suite('ExtHostSearch', () => { makeTextResult(rootFolderA, 'file1.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); @@ -946,7 +955,7 @@ suite('ExtHostSearch', () => { } }; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { let reportedResults; if (options.folder.fsPath === rootFolderA.fsPath) { @@ -1010,7 +1019,7 @@ suite('ExtHostSearch', () => { makeTextResult(rootFolderA, 'file1.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); @@ -1040,7 +1049,7 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); @@ -1072,7 +1081,7 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); @@ -1103,7 +1112,7 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); @@ -1129,7 +1138,7 @@ suite('ExtHostSearch', () => { test('multiroot max results', async () => { let cancels = 0; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => cancels++); return new TPromise(r => process.nextTick(r)) @@ -1166,7 +1175,7 @@ suite('ExtHostSearch', () => { makeTextResult(fancySchemeFolderA, 'file3.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null);