diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 18776fcaae2..9d2e814577c 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -209,6 +209,12 @@ declare module 'vscode' { * The maximum number of results to be returned. */ maxResults?: number; + + /** + * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, + * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. + */ + session?: CancellationToken; } /** diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index c59b1ab89e0..b8a6a502d77 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -552,9 +552,9 @@ export class FileIndexSearchManager { }); } - public clearCache(cacheKey: string): Promise { + public clearCache(cacheKey: string): void { if (!this.folderCacheKeys.has(cacheKey)) { - return Promise.resolve(undefined); + return undefined; } const expandedKeys = this.folderCacheKeys.get(cacheKey); @@ -562,7 +562,7 @@ export class FileIndexSearchManager { this.folderCacheKeys.delete(cacheKey); - return Promise.resolve(undefined); + return undefined; } private preventCancellation(promise: CancelablePromise): CancelablePromise { diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index cf20b24f285..a06729009c6 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -158,9 +158,10 @@ export class ExtHostSearch implements ExtHostSearchShape { this._internalFileSearchProvider.clearCache(cacheKey); } - // Actually called once per provider. - // Only relevant to file index search. - return this._fileIndexSearchManager.clearCache(cacheKey); + this._fileSearchManager.clearCache(cacheKey); + this._fileIndexSearchManager.clearCache(cacheKey); + + return Promise.resolve(undefined); } $provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: CancellationToken): Thenable { diff --git a/src/vs/workbench/services/search/node/fileSearchManager.ts b/src/vs/workbench/services/search/node/fileSearchManager.ts index 2713a180fb3..39fd6f33ef6 100644 --- a/src/vs/workbench/services/search/node/fileSearchManager.ts +++ b/src/vs/workbench/services/search/node/fileSearchManager.ts @@ -47,7 +47,7 @@ class FileSearchEngine { private globalExcludePattern?: glob.ParsedExpression; - constructor(private config: IFileQuery, private provider: vscode.FileSearchProvider) { + constructor(private config: IFileQuery, private provider: vscode.FileSearchProvider, private sessionToken?: CancellationToken) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || undefined; @@ -107,7 +107,7 @@ class FileSearchEngine { } private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): Promise { - let cancellation = new CancellationTokenSource(); + const cancellation = new CancellationTokenSource(); return new Promise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); @@ -185,7 +185,8 @@ class FileSearchEngine { useIgnoreFiles: !fq.disregardIgnoreFiles, useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles, followSymlinks: !fq.ignoreSymlinks, - maxResults: this.config.maxResults + maxResults: this.config.maxResults, + session: this.sessionToken }; } @@ -282,8 +283,11 @@ export class FileSearchManager { private static readonly BATCH_SIZE = 512; + private readonly sessions = new Map(); + fileSearch(config: IFileQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { - const engine = new FileSearchEngine(config, provider); + const sessionTokenSource = this.getSessionTokenSource(config.cacheKey); + const engine = new FileSearchEngine(config, provider, sessionTokenSource && sessionTokenSource.token); let resultCount = 0; const onInternalResult = (batch: IInternalFileMatch[]) => { @@ -305,6 +309,25 @@ export class FileSearchManager { }); } + clearCache(cacheKey: string): void { + const sessionTokenSource = this.getSessionTokenSource(cacheKey); + if (sessionTokenSource) { + sessionTokenSource.cancel(); + } + } + + private getSessionTokenSource(cacheKey: string | undefined): CancellationTokenSource | undefined { + if (!cacheKey) { + return undefined; + } + + if (!this.sessions.has(cacheKey)) { + this.sessions.set(cacheKey, new CancellationTokenSource()); + } + + return this.sessions.get(cacheKey); + } + private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch { if (match.relativePath) { return { diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 0c6db0e6d25..2cb88eddd97 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -441,7 +441,8 @@ export class SearchService extends Disposable implements ISearchService { public clearCache(cacheKey: string): Promise { const clearPs = [ this.diskSearch, - ...values(this.fileIndexProviders) + ...values(this.fileIndexProviders), + ...values(this.fileSearchProviders) ].map(provider => provider && provider.clearCache(cacheKey)); return Promise.all(clearPs)