From c2395f766d57e5d6392ff48dde30ca6eca547feb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Aug 2017 15:32:38 +0200 Subject: [PATCH 1/6] prototype - add ISearchResultProvider interface and wire things up --- src/vs/platform/search/common/search.ts | 6 + .../services/search/node/searchService.ts | 105 +++++++++++++----- 2 files changed, 81 insertions(+), 30 deletions(-) diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 6dee0d2009196..ab315d50f0fe3 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -11,6 +11,7 @@ import * as paths from 'vs/base/common/paths'; import * as glob from 'vs/base/common/glob'; import { IFilesConfiguration } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; export const ID = 'searchService'; @@ -24,6 +25,11 @@ export interface ISearchService { search(query: ISearchQuery): PPromise; extendQuery(query: ISearchQuery): void; clearCache(cacheKey: string): TPromise; + registerSearchResultProvider(provider: ISearchResultProvider): IDisposable; +} + +export interface ISearchResultProvider { + search(query: ISearchQuery): PPromise; } export interface IFolderQuery { diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 6ad79f8ab0723..77486bafa701a 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -11,7 +11,7 @@ import scorer = require('vs/base/common/scorer'); import strings = require('vs/base/common/strings'); import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import { IProgress, LineMatch, FileMatch, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchQuery, ISearchConfiguration, ISearchService, pathIncludedInQuery } from 'vs/platform/search/common/search'; +import { IProgress, LineMatch, FileMatch, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchQuery, ISearchConfiguration, ISearchService, pathIncludedInQuery, ISearchResultProvider } from 'vs/platform/search/common/search'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -20,11 +20,13 @@ import { IRawSearch, IFolderSearch, ISerializedSearchComplete, ISerializedSearch import { ISearchChannel, SearchChannelClient } from './searchIpc'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ResourceMap } from 'vs/base/common/map'; +import { IDisposable } from "vs/base/common/lifecycle"; export class SearchService implements ISearchService { public _serviceBrand: any; private diskSearch: DiskSearch; + private readonly resultProvider: ISearchResultProvider[] = []; constructor( @IModelService private modelService: IModelService, @@ -34,6 +36,38 @@ export class SearchService implements ISearchService { @IConfigurationService private configurationService: IConfigurationService ) { this.diskSearch = new DiskSearch(!environmentService.isBuilt || environmentService.verbose); + this.registerSearchResultProvider(this.diskSearch); + + this.registerSearchResultProvider({ + search(query): PPromise { + return new PPromise(resolve => { + resolve({ + limitHit: false, + stats: undefined, + results: [{ + resource: uri.parse('foo://auth/path/name.abc'), + lineMatches: [{ + lineNumber: 1, + preview: 'foo', + offsetAndLengths: [[0, 3]] + }] + }] + }); + }); + } + }); + } + + public registerSearchResultProvider(provider: ISearchResultProvider): IDisposable { + this.resultProvider.push(provider); + return { + dispose: () => { + const idx = this.resultProvider.indexOf(provider); + if (idx >= 0) { + this.resultProvider.splice(idx, 1); + } + } + }; } public extendQuery(query: ISearchQuery): void { @@ -59,10 +93,10 @@ export class SearchService implements ISearchService { } public search(query: ISearchQuery): PPromise { - let rawSearchQuery: PPromise; - return new PPromise((onComplete, onError, onProgress) => { - const searchP = this.diskSearch.search(query); + let combinedPromise: TPromise; + + return new PPromise((onComplete, onError, onProgress) => { // Get local results from dirty/untitled const localResults = this.getLocalResults(query); @@ -70,38 +104,49 @@ export class SearchService implements ISearchService { // Allow caller to register progress callback process.nextTick(() => localResults.values().filter((res) => !!res).forEach(onProgress)); - rawSearchQuery = searchP.then( - - // on Complete - (complete) => { - onComplete({ - limitHit: complete.limitHit, - results: complete.results.filter((match) => !localResults.has(match.resource)), // dont override local results - stats: complete.stats - }); - }, - - // on Error - (error) => { - onError(error); + const providerPromises = this.resultProvider.map(provider => provider.search(query).then(e => e, + err => { + // TODO@joh + // single provider fail. fail all? + onError(err); }, - - // on Progress - (progress) => { - - // Match + progress => { if (progress.resource) { + // Match if (!localResults.has(progress.resource)) { // don't override local results onProgress(progress); } + } else { + // Progress + onProgress(progress); } + } + )); - // Progress - else { - onProgress(progress); + combinedPromise = TPromise.join(providerPromises).then(values => { + + const result: ISearchComplete = { + limitHit: false, + results: [], + stats: undefined + }; + + // TODO@joh + // sorting, disjunct results, individual stats/limit? + for (const value of values) { + result.limitHit = value.limitHit || result.limitHit; + for (const match of value.results) { + if (!localResults.has(match.resource)) { + result.results.push(match); + } } - }); - }, () => rawSearchQuery && rawSearchQuery.cancel()); + } + + return result; + + }).then(onComplete, onError); + + }, () => combinedPromise && combinedPromise.cancel()); } private getLocalResults(query: ISearchQuery): ResourceMap { @@ -176,7 +221,7 @@ export class SearchService implements ISearchService { } } -export class DiskSearch { +export class DiskSearch implements ISearchResultProvider { private raw: IRawSearchService; @@ -286,4 +331,4 @@ export class DiskSearch { public clearCache(cacheKey: string): TPromise { return this.raw.clearCache(cacheKey); } -} \ No newline at end of file +} From fe20718355b9418082127b3f0e293532c482f4d3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Aug 2017 16:18:02 +0200 Subject: [PATCH 2/6] disable dummy provider --- .../services/search/node/searchService.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 77486bafa701a..8bb2b22035c00 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -38,24 +38,24 @@ export class SearchService implements ISearchService { this.diskSearch = new DiskSearch(!environmentService.isBuilt || environmentService.verbose); this.registerSearchResultProvider(this.diskSearch); - this.registerSearchResultProvider({ - search(query): PPromise { - return new PPromise(resolve => { - resolve({ - limitHit: false, - stats: undefined, - results: [{ - resource: uri.parse('foo://auth/path/name.abc'), - lineMatches: [{ - lineNumber: 1, - preview: 'foo', - offsetAndLengths: [[0, 3]] - }] - }] - }); - }); - } - }); + // this.registerSearchResultProvider({ + // search(query): PPromise { + // return new PPromise(resolve => { + // resolve({ + // limitHit: false, + // stats: undefined, + // results: [{ + // resource: uri.parse('foo://auth/path/name.abc'), + // lineMatches: [{ + // lineNumber: 1, + // preview: 'foo', + // offsetAndLengths: [[0, 3]] + // }] + // }] + // }); + // }); + // } + // }); } public registerSearchResultProvider(provider: ISearchResultProvider): IDisposable { From af195c9638271f636443e9c402bec94ad8f5b911 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Aug 2017 10:43:23 +0200 Subject: [PATCH 3/6] api-ify search provider, needs query logic... --- src/vs/platform/progress/common/progress.ts | 6 +-- src/vs/vscode.proposed.d.ts | 4 ++ .../electron-browser/mainThreadWorkspace.ts | 49 ++++++++++++++++++- src/vs/workbench/api/node/extHost.protocol.ts | 8 +++ src/vs/workbench/api/node/extHostWorkspace.ts | 45 ++++++++++++++--- 5 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index f5139f273d734..cd966c7e1935b 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -39,10 +39,10 @@ export const emptyProgress: IProgress = Object.freeze({ report() { } }); export class Progress implements IProgress { - private _callback: () => void; + private _callback: (data: T) => void; private _value: T; - constructor(callback: () => void) { + constructor(callback: (data: T) => void) { this._callback = callback; } @@ -52,7 +52,7 @@ export class Progress implements IProgress { report(item: T) { this._value = item; - this._callback(); + this._callback(this._value); } } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index d237dad287e8e..354bb16a34ec7 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -16,6 +16,10 @@ declare module 'vscode' { writeContents(resource: Uri, contents: string): void | Thenable; } + export interface SearchProvider { + provideSearchResults(query: any, progress: Progress, token?: CancellationToken): Thenable; + } + export namespace workspace { export function registerFileSystemProvider(authority: string, provider: FileSystemProvider): Disposable; diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 677837f0293ad..07dd447031392 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -12,7 +12,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon'; import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { TPromise, PPromise } from 'vs/base/common/winjs.base'; import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -101,6 +101,53 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { // --- save & edit resources --- + private readonly _searchProvider = new Map(); + private readonly _searchSession = new Map(); + private _searchSessionIdPool: number = 0; + + $registerSearchProvider(handle: number, type: number): void { + this._searchProvider.set(handle, this._searchService.registerSearchResultProvider({ + search: (query) => { + if (query.type !== type) { + return null; + } + const session = ++this._searchSessionIdPool; + return new PPromise((resolve, reject, progress) => { + this._searchSession.set(session, { resolve, reject, progress }); + this._proxy.$startSearch(handle, session, query); + }, () => { + this._proxy.$cancelSearch(handle, session); + }); + } + })); + } + + $unregisterSearchProvider(handle: number): void { + const registration = this._searchProvider.get(handle); + if (registration) { + registration.dispose(); + this._searchProvider.delete(handle); + } + } + + $updateSearchSession(session: number, data): void { + if (this._searchSession.has(session)) { + this._searchSession.get(session).progress(data); + } + } + + $finishSearchSession(session: number, err?: any): void { + if (this._searchSession.has(session)) { + const { resolve, reject } = this._searchSession.get(session); + this._searchSession.delete(session); + if (err) { + reject(err); + } else { + resolve(); + } + } + } + $saveAll(includeUntitled?: boolean): Thenable { return this._textFileService.saveAll(includeUntitled).then(result => { return result.results.every(each => each.success === true); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 395ee8ae8e986..908610522e796 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -280,6 +280,11 @@ export interface MainThreadWorkspaceShape extends IDisposable { $applyWorkspaceEdit(edits: IResourceEdit[]): TPromise; $registerFileSystemProvider(handle: number, authority: string): void; $onFileSystemChange(handle: number, resource: URI): void; + + $registerSearchProvider(handle: number, type: number): void; + $unregisterSearchProvider(handle: number): void; + $updateSearchSession(session: number, data): void; + $finishSearchSession(session: number, err?: any): void; } export interface MainThreadTaskShape extends IDisposable { @@ -418,8 +423,11 @@ export interface ExtHostTreeViewsShape { export interface ExtHostWorkspaceShape { $acceptWorkspaceData(workspace: IWorkspaceData): void; + $resolveFile(handle: number, resource: URI): TPromise; $storeFile(handle: number, resource: URI, content: string): TPromise; + $startSearch(handle: number, session: number, query): void; + $cancelSearch(handle: number, session: number): void; } export interface ExtHostExtensionServiceShape { diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 2c61808088e73..7dcf5921b8bd4 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -19,6 +19,8 @@ import { compare } from "vs/base/common/strings"; import { asWinJsPromise } from 'vs/base/common/async'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { TrieMap } from 'vs/base/common/map'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Progress } from 'vs/platform/progress/common/progress'; class Workspace2 extends Workspace { @@ -207,27 +209,56 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { // --- EXPERIMENT: workspace resolver - private readonly _provider = new Map(); - public registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable { + private _handlePool = 0; + private readonly _fsProvider = new Map(); + private readonly _searchProvider = new Map(); + private readonly _searchSession = new Map(); - const handle = this._provider.size; - this._provider.set(handle, provider); + registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable { + const handle = ++this._handlePool; + this._fsProvider.set(handle, provider); const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, e)); this._proxy.$registerFileSystemProvider(handle, authority); return new Disposable(() => { - this._provider.delete(handle); + this._fsProvider.delete(handle); reg.dispose(); }); } $resolveFile(handle: number, resource: URI): TPromise { - const provider = this._provider.get(handle); + const provider = this._fsProvider.get(handle); return asWinJsPromise(token => provider.resolveContents(resource)); } $storeFile(handle: number, resource: URI, content: string): TPromise { - const provider = this._provider.get(handle); + const provider = this._fsProvider.get(handle); return asWinJsPromise(token => provider.writeContents(resource, content)); } + + 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(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); + } + } } From 52f98f46efd26903c9a35c4e03564e6548681bd8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Aug 2017 12:36:30 +0200 Subject: [PATCH 4/6] move find-file logic into fsprovider, wire things up end to end --- src/vs/vscode.proposed.d.ts | 7 +- .../electron-browser/mainThreadWorkspace.ts | 108 +++++++++--------- src/vs/workbench/api/node/extHost.protocol.ts | 7 +- src/vs/workbench/api/node/extHostWorkspace.ts | 13 +-- .../services/search/node/searchService.ts | 5 +- 5 files changed, 68 insertions(+), 72 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 354bb16a34ec7..393b2ddfe8963 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -14,14 +14,13 @@ declare module 'vscode' { resolveContents(resource: Uri): string | Thenable; writeContents(resource: Uri, contents: string): void | Thenable; - } - export interface SearchProvider { - provideSearchResults(query: any, progress: Progress, token?: CancellationToken): Thenable; + // -- search + // todo@joh - extract into its own provider? + findFiles(query: string, progress: Progress, token?: CancellationToken): Thenable; } export namespace workspace { - export function registerFileSystemProvider(authority: string, provider: FileSystemProvider): Disposable; } diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 07dd447031392..9bbf81999ba04 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -6,7 +6,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import URI from 'vs/base/common/uri'; -import { ISearchService, QueryType, ISearchQuery } from 'vs/platform/search/common/search'; +import { ISearchService, QueryType, ISearchQuery, ISearchProgressItem, ISearchComplete } from 'vs/platform/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -16,8 +16,8 @@ import { TPromise, PPromise } from 'vs/base/common/winjs.base'; import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IFileService } from 'vs/platform/files/common/files'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { RemoteFileService, IRemoteFileSystemProvider } from 'vs/workbench/services/files/electron-browser/remoteFileService'; +import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; +import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService'; import { Emitter } from 'vs/base/common/event'; import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; @@ -101,53 +101,6 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { // --- save & edit resources --- - private readonly _searchProvider = new Map(); - private readonly _searchSession = new Map(); - private _searchSessionIdPool: number = 0; - - $registerSearchProvider(handle: number, type: number): void { - this._searchProvider.set(handle, this._searchService.registerSearchResultProvider({ - search: (query) => { - if (query.type !== type) { - return null; - } - const session = ++this._searchSessionIdPool; - return new PPromise((resolve, reject, progress) => { - this._searchSession.set(session, { resolve, reject, progress }); - this._proxy.$startSearch(handle, session, query); - }, () => { - this._proxy.$cancelSearch(handle, session); - }); - } - })); - } - - $unregisterSearchProvider(handle: number): void { - const registration = this._searchProvider.get(handle); - if (registration) { - registration.dispose(); - this._searchProvider.delete(handle); - } - } - - $updateSearchSession(session: number, data): void { - if (this._searchSession.has(session)) { - this._searchSession.get(session).progress(data); - } - } - - $finishSearchSession(session: number, err?: any): void { - if (this._searchSession.has(session)) { - const { resolve, reject } = this._searchSession.get(session); - this._searchSession.delete(session); - if (err) { - reject(err); - } else { - resolve(); - } - } - } - $saveAll(includeUntitled?: boolean): Thenable { return this._textFileService.saveAll(includeUntitled).then(result => { return result.results.every(each => each.success === true); @@ -171,7 +124,9 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { // --- EXPERIMENT: workspace provider - private _provider = new Map]>(); + private _idPool: number = 0; + private readonly _provider = new Map]>(); + private readonly _searchSessions = new Map void, reject: Function, progress: (item: ISearchProgressItem) => void, matches: URI[] }>(); $registerFileSystemProvider(handle: number, authority: string): void { if (!(this._fileService instanceof RemoteFileService)) { @@ -187,13 +142,60 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return this._proxy.$storeFile(handle, resource, value); } }; - this._provider.set(handle, [provider, emitter]); - this._fileService.registerProvider(authority, provider); + const searchProvider = { + search: (query) => { + if (query.type !== QueryType.File) { + return undefined; + } + const session = ++this._idPool; + return new PPromise((resolve, reject, progress) => { + this._searchSessions.set(session, { resolve, reject, progress, matches: [] }); + this._proxy.$startSearch(handle, session, query.filePattern); + }, () => { + this._proxy.$cancelSearch(handle, session); + }); + } + }; + const registrations = combinedDisposable([ + this._fileService.registerProvider(authority, provider), + this._searchService.registerSearchResultProvider(searchProvider), + ]); + this._provider.set(handle, [registrations, emitter]); + } + + $unregisterFileSystemProvider(handle: number): void { + if (this._provider.has(handle)) { + dispose(this._provider.get(handle)[0]); + this._provider.delete(handle); + } } $onFileSystemChange(handle: number, resource: URI) { const [, emitter] = this._provider.get(handle); emitter.fire(resource); }; + + $updateSearchSession(session: number, data: URI): void { + if (this._searchSessions.has(session)) { + this._searchSessions.get(session).progress({ resource: data }); + this._searchSessions.get(session).matches.push(data); + } + } + + $finishSearchSession(session: number, err?: any): void { + if (this._searchSessions.has(session)) { + const { matches, resolve, reject } = this._searchSessions.get(session); + this._searchSessions.delete(session); + if (err) { + reject(err); + } else { + resolve({ + limitHit: false, + stats: undefined, + results: matches.map(resource => ({ resource })) + }); + } + } + } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 908610522e796..ac5336fd3b5b8 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -278,11 +278,10 @@ export interface MainThreadWorkspaceShape extends IDisposable { $cancelSearch(requestId: number): Thenable; $saveAll(includeUntitled?: boolean): Thenable; $applyWorkspaceEdit(edits: IResourceEdit[]): TPromise; + $registerFileSystemProvider(handle: number, authority: string): void; + $unregisterFileSystemProvider(handle): void; $onFileSystemChange(handle: number, resource: URI): void; - - $registerSearchProvider(handle: number, type: number): void; - $unregisterSearchProvider(handle: number): void; $updateSearchSession(session: number, data): void; $finishSearchSession(session: number, err?: any): void; } @@ -426,7 +425,7 @@ export interface ExtHostWorkspaceShape { $resolveFile(handle: number, resource: URI): TPromise; $storeFile(handle: number, resource: URI, content: string): TPromise; - $startSearch(handle: number, session: number, query): void; + $startSearch(handle: number, session: number, query: string): void; $cancelSearch(handle: number, session: number): void; } diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 7dcf5921b8bd4..56af8da9ce07b 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -212,7 +212,6 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { private _handlePool = 0; private readonly _fsProvider = new Map(); - private readonly _searchProvider = new Map(); private readonly _searchSession = new Map(); registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable { @@ -236,19 +235,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { return asWinJsPromise(token => provider.writeContents(resource, content)); } - 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); + $startSearch(handle: number, session: number, query: string): void { + const provider = this._fsProvider.get(handle); const source = new CancellationTokenSource(); const progress = new Progress(chunk => this._proxy.$updateSearchSession(session, chunk)); this._searchSession.set(session, source); - TPromise.wrap(provider.provideSearchResults(query, progress, source.token)).then(() => { + TPromise.wrap(provider.findFiles(query, progress, source.token)).then(() => { this._proxy.$finishSearchSession(session); }, err => { this._proxy.$finishSearchSession(session, err); diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 8bb2b22035c00..05e6a1885d446 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -104,7 +104,7 @@ export class SearchService implements ISearchService { // Allow caller to register progress callback process.nextTick(() => localResults.values().filter((res) => !!res).forEach(onProgress)); - const providerPromises = this.resultProvider.map(provider => provider.search(query).then(e => e, + const providerPromises = this.resultProvider.map(provider => TPromise.wrap(provider.search(query)).then(e => e, err => { // TODO@joh // single provider fail. fail all? @@ -134,6 +134,9 @@ export class SearchService implements ISearchService { // TODO@joh // sorting, disjunct results, individual stats/limit? for (const value of values) { + if (!value) { + continue; + } result.limitHit = value.limitHit || result.limitHit; for (const match of value.results) { if (!localResults.has(match.resource)) { From bd63061a31138e440eb8b15ea49ec0707bd596ce Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Aug 2017 16:29:53 +0200 Subject: [PATCH 5/6] :lipstick: remove dummy provider --- .../services/search/node/searchService.ts | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 05e6a1885d446..93096953b0494 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -20,13 +20,13 @@ import { IRawSearch, IFolderSearch, ISerializedSearchComplete, ISerializedSearch import { ISearchChannel, SearchChannelClient } from './searchIpc'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ResourceMap } from 'vs/base/common/map'; -import { IDisposable } from "vs/base/common/lifecycle"; +import { IDisposable } from 'vs/base/common/lifecycle'; export class SearchService implements ISearchService { public _serviceBrand: any; private diskSearch: DiskSearch; - private readonly resultProvider: ISearchResultProvider[] = []; + private readonly searchProvider: ISearchResultProvider[] = []; constructor( @IModelService private modelService: IModelService, @@ -37,34 +37,15 @@ export class SearchService implements ISearchService { ) { this.diskSearch = new DiskSearch(!environmentService.isBuilt || environmentService.verbose); this.registerSearchResultProvider(this.diskSearch); - - // this.registerSearchResultProvider({ - // search(query): PPromise { - // return new PPromise(resolve => { - // resolve({ - // limitHit: false, - // stats: undefined, - // results: [{ - // resource: uri.parse('foo://auth/path/name.abc'), - // lineMatches: [{ - // lineNumber: 1, - // preview: 'foo', - // offsetAndLengths: [[0, 3]] - // }] - // }] - // }); - // }); - // } - // }); } public registerSearchResultProvider(provider: ISearchResultProvider): IDisposable { - this.resultProvider.push(provider); + this.searchProvider.push(provider); return { dispose: () => { - const idx = this.resultProvider.indexOf(provider); + const idx = this.searchProvider.indexOf(provider); if (idx >= 0) { - this.resultProvider.splice(idx, 1); + this.searchProvider.splice(idx, 1); } } }; @@ -104,7 +85,7 @@ export class SearchService implements ISearchService { // Allow caller to register progress callback process.nextTick(() => localResults.values().filter((res) => !!res).forEach(onProgress)); - const providerPromises = this.resultProvider.map(provider => TPromise.wrap(provider.search(query)).then(e => e, + const providerPromises = this.searchProvider.map(provider => TPromise.wrap(provider.search(query)).then(e => e, err => { // TODO@joh // single provider fail. fail all? From cc899278aa52b27dddb8851d03a78438f3ab17dd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Aug 2017 16:34:11 +0200 Subject: [PATCH 6/6] make sure there are always stats --- src/vs/workbench/services/search/node/searchService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 93096953b0494..860a1fdc7ba4c 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -113,12 +113,15 @@ export class SearchService implements ISearchService { }; // TODO@joh - // sorting, disjunct results, individual stats/limit? + // sorting, disjunct results for (const value of values) { if (!value) { continue; } + // TODO@joh individual stats/limit + result.stats = value.stats || result.stats; result.limitHit = value.limitHit || result.limitHit; + for (const match of value.results) { if (!localResults.has(match.resource)) { result.results.push(match);