From 3673539e09b9792e84d1fae06af2aa7da22c6390 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 16 Aug 2021 20:47:34 -0600 Subject: [PATCH] Keep search code sync when possible --- src/vs/base/common/types.ts | 4 +++ .../services/search/common/search.ts | 34 ++++++++++++------- .../search/common/textSearchManager.ts | 28 +++++++++------ 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 5525a20d605..9f047e0caa9 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -290,3 +290,7 @@ export function NotImplementedProxy(name: string): { new(): T } { export function assertNever(value: never, message = 'Unreachable') { throw new Error(message); } + +export function isPromise(obj: unknown): obj is Promise { + return !!obj && typeof (obj as Promise).then === 'function' && typeof (obj as Promise).catch === 'function'; +} diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index f5f670c7956..32cb38af55f 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -18,6 +18,7 @@ import { Event } from 'vs/base/common/event'; import * as paths from 'vs/base/common/path'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes'; +import { isPromise } from 'vs/base/common/types'; export { TextSearchCompleteMessageType }; @@ -655,22 +656,29 @@ export class QueryGlobTester { } /** - * Guaranteed async. + * Evaluating the exclude expression is only async if it includes sibling clauses. As an optimization, avoid doing anything with Promises + * unless the expression is async. */ - includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): Promise { - const excludeP = Promise.resolve(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result); - - return excludeP.then(excluded => { - if (excluded) { - return false; - } + includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): Promise | boolean { + const excluded = this._parsedExcludeExpression(testPath, basename, hasSibling); + const isIncluded = () => { return this._parsedIncludeExpression ? - Promise.resolve(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - Promise.resolve(true); - }).then(included => { - return included; - }); + !!(this._parsedIncludeExpression(testPath, basename, hasSibling)) : + true; + }; + + if (isPromise(excluded)) { + return excluded.then(excluded => { + if (excluded) { + return false; + } + + return isIncluded(); + }); + } + + return isIncluded(); } hasSiblingExcludeClauses(): boolean { diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index 30df394a8a9..7625eb34d19 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'vs/base/common/path'; import { flatten, mapArrayOrNot } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import * as resources from 'vs/base/common/resources'; import * as glob from 'vs/base/common/glob'; +import { Schemas } from 'vs/base/common/network'; +import * as path from 'vs/base/common/path'; +import * as resources from 'vs/base/common/resources'; +import { isArray, isPromise } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search'; -import { TextSearchProvider, TextSearchResult, TextSearchMatch, TextSearchComplete, Range, TextSearchOptions, TextSearchQuery } from 'vs/workbench/services/search/common/searchExtTypes'; -import { Schemas } from 'vs/base/common/network'; -import { isArray } from 'vs/base/common/types'; +import { Range, TextSearchComplete, TextSearchMatch, TextSearchOptions, TextSearchProvider, TextSearchQuery, TextSearchResult } from 'vs/workbench/services/search/common/searchExtTypes'; export interface IFileUtils { readdir: (resource: URI) => Promise; @@ -131,20 +131,28 @@ export class TextSearchManager { const relativePath = resources.relativePath(folderQuery.folder, result.uri); if (relativePath) { - testingPs.push( - queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling) - .then(included => { - if (included) { + // This method is only async when the exclude contains sibling clauses + const included = queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling); + if (isPromise(included)) { + testingPs.push( + included.then(isIncluded => { + if (isIncluded) { onResult(result); } })); + } else if (included) { + onResult(result); + } } } }; const searchOptions = this.getSearchOptionsForFolder(folderQuery); const result = await this.provider.provideTextSearchResults(patternInfoToQuery(this.query.contentPattern), searchOptions, progress, token); - await Promise.all(testingPs); + if (testingPs.length) { + await Promise.all(testingPs); + } + return result; } -- GitLab