feature: make number of ripgrep threads configurable (#213511)

* feature: add threads property

* add setting

* use setting

* debug

* add option

* fix types

* use commonquery

* rename argument

* remove comma

* add threads for file search

* set threads to undefined

* update description

* remove unused code

* remove unused code

* Update src/vs/workbench/services/search/node/ripgrepFileSearch.ts

Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>

* Update src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts

Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>

* use ICommonQueryBuilderOptions

* set threads property from caller

* simplify code

* Discard changes to src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts

* Discard changes to src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts

* remove unused code

* Discard changes to src/vs/workbench/services/search/common/textSearchManager.ts

* add back code

* fix types

* update formatting

* add semicolon

* rename setting

* use different way to pass numThreads to ripgrep

* Discard changes to src/vs/workbench/services/search/common/queryBuilder.ts

* Discard changes to src/vs/workbench/services/search/common/search.ts

* tsc

* tsc

* tsc

* fix imports

* use configuration service

* tsc

* tsc

* tsc

* improve types

* Update src/vs/workbench/contrib/search/browser/search.contribution.ts

Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>

* simplify type

* rename types

* move numThreads parameter last

* simplify code

* Update src/vs/workbench/services/search/common/textSearchManager.ts

Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>

* Update src/vs/workbench/services/search/common/searchExtTypesInternal.ts

Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>

* Discard changes to src/vs/workbench/services/search/test/node/rawSearchService.integrationTest.ts

* fix typo

* fix types

* Discard changes to src/vs/monaco.d.ts

* rename interface

* rename variable

* rename setting

* rename setting

* update settings

* tsc

* fix: pass threads property also below

* cache setting

* use affectsConfiguration

* fix test

---------

Co-authored-by: Andrea Mah <31675041+andreamah@users.noreply.github.com>
This commit is contained in:
Simon Siefke 2024-06-06 19:29:41 +02:00 committed by GitHub
parent a38eb611fa
commit 0036990784
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 138 additions and 38 deletions

View file

@ -8,6 +8,7 @@ import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ExtHostSearch, reviveQuery } from 'vs/workbench/api/common/extHostSearch';
@ -29,24 +30,59 @@ export class NativeExtHostSearch extends ExtHostSearch implements IDisposable {
private _registeredEHSearchProvider = false;
private _numThreadsPromise: Promise<number | undefined> | undefined;
private readonly _disposables = new DisposableStore();
private isDisposed = false;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IURITransformerService _uriTransformer: IURITransformerService,
@IExtHostConfiguration private readonly configurationService: IExtHostConfiguration,
@ILogService _logService: ILogService,
) {
super(extHostRpc, _uriTransformer, _logService);
this.getNumThreads = this.getNumThreads.bind(this);
this.getNumThreadsCached = this.getNumThreadsCached.bind(this);
this.handleConfigurationChanged = this.handleConfigurationChanged.bind(this);
const outputChannel = new OutputChannel('RipgrepSearchUD', this._logService);
this._disposables.add(this.registerTextSearchProvider(Schemas.vscodeUserData, new RipgrepSearchProvider(outputChannel)));
this._disposables.add(this.registerTextSearchProvider(Schemas.vscodeUserData, new RipgrepSearchProvider(outputChannel, this.getNumThreadsCached)));
if (initData.remote.isRemote && initData.remote.authority) {
this._registerEHSearchProviders();
}
configurationService.getConfigProvider().then(provider => {
if (this.isDisposed) {
return;
}
this._disposables.add(provider.onDidChangeConfiguration(this.handleConfigurationChanged));
});
}
private handleConfigurationChanged(event: vscode.ConfigurationChangeEvent) {
if (!event.affectsConfiguration('search')) {
return;
}
this._numThreadsPromise = undefined;
}
async getNumThreads(): Promise<number | undefined> {
const configProvider = await this.configurationService.getConfigProvider();
const numThreads = configProvider.getConfiguration('search').get<number>('ripgrep.maxThreads');
return numThreads;
}
async getNumThreadsCached(): Promise<number | undefined> {
if (!this._numThreadsPromise) {
this._numThreadsPromise = this.getNumThreads();
}
return this._numThreadsPromise;
}
dispose(): void {
this.isDisposed = true;
this._disposables.dispose();
}
@ -61,8 +97,8 @@ export class NativeExtHostSearch extends ExtHostSearch implements IDisposable {
this._registeredEHSearchProvider = true;
const outputChannel = new OutputChannel('RipgrepSearchEH', this._logService);
this._disposables.add(this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel)));
this._disposables.add(this.registerInternalFileSearchProvider(Schemas.file, new SearchService('fileSearchProvider')));
this._disposables.add(this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel, this.getNumThreadsCached)));
this._disposables.add(this.registerInternalFileSearchProvider(Schemas.file, new SearchService('fileSearchProvider', this.getNumThreadsCached)));
}
private registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
@ -90,7 +126,7 @@ export class NativeExtHostSearch extends ExtHostSearch implements IDisposable {
return super.$provideFileSearchResults(handle, session, rawQuery, token);
}
override doInternalFileSearchWithCustomCallback(rawQuery: IFileQuery, token: vscode.CancellationToken, handleFileMatch: (data: URI[]) => void): Promise<ISearchCompleteStats> {
override async doInternalFileSearchWithCustomCallback(rawQuery: IFileQuery, token: vscode.CancellationToken, handleFileMatch: (data: URI[]) => void): Promise<ISearchCompleteStats> {
const onResult = (ev: ISerializedSearchProgressItem) => {
if (isSerializedFileMatch(ev)) {
ev = [ev];
@ -109,8 +145,8 @@ export class NativeExtHostSearch extends ExtHostSearch implements IDisposable {
if (!this._internalFileSearchProvider) {
throw new Error('No internal file search handler');
}
return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token);
const numThreads = await this.getNumThreadsCached();
return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, numThreads, onResult, token);
}
private async doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {

View file

@ -16,6 +16,7 @@ import { mock } from 'vs/base/test/common/mock';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
import { NullLogService } from 'vs/platform/log/common/log';
import { MainContext, MainThreadSearchShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration.js';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { Range } from 'vs/workbench/api/common/extHostTypes';
import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
@ -144,6 +145,26 @@ suite('ExtHostSearch', () => {
rpcProtocol,
new class extends mock<IExtHostInitDataService>() { override remote = { isRemote: false, authority: undefined, connectionData: null }; },
new URITransformerService(null),
new class extends mock<IExtHostConfiguration>() {
override async getConfigProvider(): Promise<ExtHostConfigProvider> {
return {
onDidChangeConfiguration(_listener: (event: vscode.ConfigurationChangeEvent) => void) { },
getConfiguration(): vscode.WorkspaceConfiguration {
return {
get() { },
has() {
return false;
},
inspect() {
return undefined;
},
async update() { }
};
},
} as ExtHostConfigProvider;
}
},
logService
);
this._pfs = mockPFS as any;

View file

@ -198,6 +198,11 @@ configurationRegistry.registerConfiguration({
description: nls.localize('search.quickOpen.includeSymbols', "Whether to include results from a global symbol search in the file results for Quick Open."),
default: false
},
'search.ripgrep.maxThreads': {
type: 'number',
description: nls.localize('search.ripgrep.maxThreads', "Number of threads to use for searching. When set to 0, the engine automatically determines this value."),
default: 0
},
'search.quickOpen.includeHistory': {
type: 'boolean',
description: nls.localize('search.quickOpen.includeHistory', "Whether to include results from recently opened files in the file results for Quick Open."),

View file

@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { FileSearchOptions, TextSearchOptions } from './searchExtTypes';
interface RipgrepSearchOptionsCommon {
numThreads?: number;
}
export interface RipgrepTextSearchOptions extends TextSearchOptions, RipgrepSearchOptionsCommon { }
export interface RipgrepFileSearchOptions extends FileSearchOptions, RipgrepSearchOptionsCommon { }

View file

@ -105,7 +105,7 @@ export class FileWalker {
killCmds.forEach(cmd => cmd());
}
walk(folderQueries: IFolderQuery[], extraFiles: URI[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, done: (error: Error | null, isLimitHit: boolean) => void): void {
walk(folderQueries: IFolderQuery[], extraFiles: URI[], numThreads: number | undefined, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, done: (error: Error | null, isLimitHit: boolean) => void): void {
this.fileWalkSW = StopWatch.create(false);
// Support that the file pattern is a full path to a file that exists
@ -128,7 +128,7 @@ export class FileWalker {
// For each root folder
this.parallel<IFolderQuery, void>(folderQueries, (folderQuery: IFolderQuery, rootFolderDone: (err: Error | null, result: void) => void) => {
this.call(this.cmdTraversal, this, folderQuery, onResult, onMessage, (err?: Error) => {
this.call(this.cmdTraversal, this, folderQuery, numThreads, onResult, onMessage, (err?: Error) => {
if (err) {
const errorMessage = toErrorMessage(err);
console.error(errorMessage);
@ -181,7 +181,7 @@ export class FileWalker {
}
}
private cmdTraversal(folderQuery: IFolderQuery, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, cb: (err?: Error) => void): void {
private cmdTraversal(folderQuery: IFolderQuery, numThreads: number | undefined, onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgressMessage) => void, cb: (err?: Error) => void): void {
const rootFolder = folderQuery.folder.fsPath;
const isMac = platform.isMacintosh;
@ -196,7 +196,7 @@ export class FileWalker {
let leftover = '';
const tree = this.initDirectoryTree();
const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder.fsPath)!.expression);
const ripgrep = spawnRipgrepCmd(this.config, folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder.fsPath)!.expression, numThreads);
const cmd = ripgrep.cmd;
const noSiblingsClauses = !Object.keys(ripgrep.siblingClauses).length;
@ -628,16 +628,18 @@ export class Engine implements ISearchEngine<IRawFileMatch> {
private folderQueries: IFolderQuery[];
private extraFiles: URI[];
private walker: FileWalker;
private numThreads?: number;
constructor(config: IFileQuery) {
constructor(config: IFileQuery, numThreads?: number) {
this.folderQueries = config.folderQueries;
this.extraFiles = config.extraFileResources || [];
this.numThreads = numThreads;
this.walker = new FileWalker(config);
}
search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgressMessage) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void): void {
this.walker.walk(this.folderQueries, this.extraFiles, onResult, onProgress, (err: Error | null, isLimitHit: boolean) => {
this.walker.walk(this.folderQueries, this.extraFiles, this.numThreads, onResult, onProgress, (err: Error | null, isLimitHit: boolean) => {
done(err, {
limitHit: isLimitHit,
stats: this.walker.getStats(),

View file

@ -26,7 +26,7 @@ export class SearchService implements IRawSearchService {
private caches: { [cacheKey: string]: Cache } = Object.create(null);
constructor(private readonly processType: IFileSearchStats['type'] = 'searchProcess') { }
constructor(private readonly processType: IFileSearchStats['type'] = 'searchProcess', private readonly getNumThreads?: () => Promise<number | undefined>) { }
fileSearch(config: IRawFileQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete> {
let promise: CancelablePromise<ISerializedSearchSuccess>;
@ -34,8 +34,9 @@ export class SearchService implements IRawSearchService {
const query = reviveQuery(config);
const emitter = new Emitter<ISerializedSearchProgressItem | ISerializedSearchComplete>({
onDidAddFirstListener: () => {
promise = createCancelablePromise(token => {
return this.doFileSearchWithEngine(FileSearchEngine, query, p => emitter.fire(p), token);
promise = createCancelablePromise(async token => {
const numThreads = await this.getNumThreads?.();
return this.doFileSearchWithEngine(FileSearchEngine, query, p => emitter.fire(p), token, SearchService.BATCH_SIZE, numThreads);
});
promise.then(
@ -72,9 +73,10 @@ export class SearchService implements IRawSearchService {
return emitter.event;
}
private ripgrepTextSearch(config: ITextQuery, progressCallback: IProgressCallback, token: CancellationToken): Promise<ISerializedSearchSuccess> {
private async ripgrepTextSearch(config: ITextQuery, progressCallback: IProgressCallback, token: CancellationToken): Promise<ISerializedSearchSuccess> {
config.maxFileSize = this.getPlatformFileLimits().maxFileSize;
const engine = new TextSearchEngineAdapter(config);
const numThreads = await this.getNumThreads?.();
const engine = new TextSearchEngineAdapter(config, numThreads);
return engine.search(token, progressCallback, progressCallback);
}
@ -85,11 +87,11 @@ export class SearchService implements IRawSearchService {
};
}
doFileSearch(config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken): Promise<ISerializedSearchSuccess> {
return this.doFileSearchWithEngine(FileSearchEngine, config, progressCallback, token);
doFileSearch(config: IFileQuery, numThreads: number | undefined, progressCallback: IProgressCallback, token?: CancellationToken): Promise<ISerializedSearchSuccess> {
return this.doFileSearchWithEngine(FileSearchEngine, config, progressCallback, token, SearchService.BATCH_SIZE, numThreads);
}
doFileSearchWithEngine(EngineClass: { new(config: IFileQuery): ISearchEngine<IRawFileMatch> }, config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken, batchSize = SearchService.BATCH_SIZE): Promise<ISerializedSearchSuccess> {
doFileSearchWithEngine(EngineClass: { new(config: IFileQuery, numThreads?: number | undefined): ISearchEngine<IRawFileMatch> }, config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken, batchSize = SearchService.BATCH_SIZE, threads?: number): Promise<ISerializedSearchSuccess> {
let resultCount = 0;
const fileProgressCallback: IFileProgressCallback = progress => {
if (Array.isArray(progress)) {
@ -107,7 +109,7 @@ export class SearchService implements IRawSearchService {
let sortedSearch = this.trySortedSearchFromCache(config, fileProgressCallback, token);
if (!sortedSearch) {
const walkerConfig = config.maxResults ? Object.assign({}, config, { maxResults: null }) : config;
const engine = new EngineClass(walkerConfig);
const engine = new EngineClass(walkerConfig, threads);
sortedSearch = this.doSortedSearch(engine, config, progressCallback, fileProgressCallback, token);
}
@ -120,7 +122,7 @@ export class SearchService implements IRawSearchService {
});
}
const engine = new EngineClass(config);
const engine = new EngineClass(config, threads);
return this.doSearch(engine, fileProgressCallback, batchSize, token).then(complete => {
return {

View file

@ -17,8 +17,8 @@ import { rgPath } from '@vscode/ripgrep';
// If @vscode/ripgrep is in an .asar file, then the binary is unpacked.
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, includePattern?: glob.IExpression, excludePattern?: glob.IExpression) {
const rgArgs = getRgArgs(config, folderQuery, includePattern, excludePattern);
export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, includePattern?: glob.IExpression, excludePattern?: glob.IExpression, numThreads?: number) {
const rgArgs = getRgArgs(config, folderQuery, includePattern, excludePattern, numThreads);
const cwd = folderQuery.folder.fsPath;
return {
cmd: cp.spawn(rgDiskPath, rgArgs.args, { cwd }),
@ -29,7 +29,7 @@ export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, i
};
}
function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern?: glob.IExpression, excludePattern?: glob.IExpression) {
function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern?: glob.IExpression, excludePattern?: glob.IExpression, numThreads?: number) {
const args = ['--files', '--hidden', '--case-sensitive', '--no-require-git'];
// includePattern can't have siblingClauses
@ -71,6 +71,10 @@ function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern
args.push('--quiet');
}
if (numThreads) {
args.push('--threads', `${numThreads}`);
}
args.push('--no-config');
if (folderQuery.disregardGlobalIgnoreFiles) {
args.push('--no-ignore-global');

View file

@ -6,27 +6,33 @@
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine';
import { TextSearchProvider, TextSearchComplete, TextSearchResult, TextSearchQuery, TextSearchOptions } from 'vs/workbench/services/search/common/searchExtTypes';
import { TextSearchProvider, TextSearchComplete, TextSearchResult, TextSearchQuery, TextSearchOptions, } from 'vs/workbench/services/search/common/searchExtTypes';
import { Progress } from 'vs/platform/progress/common/progress';
import { Schemas } from 'vs/base/common/network';
import type { RipgrepTextSearchOptions } from 'vs/workbench/services/search/common/searchExtTypesInternal';
export class RipgrepSearchProvider implements TextSearchProvider {
private inProgress: Set<CancellationTokenSource> = new Set();
constructor(private outputChannel: OutputChannel) {
constructor(private outputChannel: OutputChannel, private getNumThreads: () => Promise<number | undefined>) {
process.once('exit', () => this.dispose());
}
provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Promise<TextSearchComplete> {
const engine = new RipgrepTextSearchEngine(this.outputChannel);
async provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Promise<TextSearchComplete> {
const numThreads = await this.getNumThreads();
const engine = new RipgrepTextSearchEngine(this.outputChannel, numThreads);
const extendedOptions: RipgrepTextSearchOptions = {
...options,
numThreads,
};
if (options.folder.scheme === Schemas.vscodeUserData) {
// Ripgrep search engine can only provide file-scheme results, but we want to use it to search some schemes that are backed by the filesystem, but with some other provider as the frontend,
// case in point vscode-userdata. In these cases we translate the query to a file, and translate the results back to the frontend scheme.
const translatedOptions = { ...options, folder: options.folder.with({ scheme: Schemas.file }) };
const translatedOptions = { ...extendedOptions, folder: options.folder.with({ scheme: Schemas.file }) };
const progressTranslator = new Progress<TextSearchResult>(data => progress.report({ ...data, uri: data.uri.with({ scheme: options.folder.scheme }) }));
return this.withToken(token, token => engine.provideTextSearchResults(query, translatedOptions, progressTranslator, token));
} else {
return this.withToken(token, token => engine.provideTextSearchResults(query, options, progress, token));
return this.withToken(token, token => engine.provideTextSearchResults(query, extendedOptions, progress, token));
}
}

View file

@ -18,13 +18,14 @@ import { Range, TextSearchComplete, TextSearchContext, TextSearchMatch, TextSear
import { AST as ReAST, RegExpParser, RegExpVisitor } from 'vscode-regexpp';
import { rgPath } from '@vscode/ripgrep';
import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe } from './ripgrepSearchUtils';
import type { RipgrepTextSearchOptions } from 'vs/workbench/services/search/common/searchExtTypesInternal';
// If @vscode/ripgrep is in an .asar file, then the binary is unpacked.
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
export class RipgrepTextSearchEngine {
constructor(private outputChannel: IOutputChannel) { }
constructor(private outputChannel: IOutputChannel, private readonly _numThreads?: number | undefined) { }
provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Promise<TextSearchComplete> {
this.outputChannel.appendLine(`provideTextSearchResults ${query.pattern}, ${JSON.stringify({
@ -37,7 +38,11 @@ export class RipgrepTextSearchEngine {
return new Promise((resolve, reject) => {
token.onCancellationRequested(() => cancel());
const rgArgs = getRgArgs(query, options);
const extendedOptions: RipgrepTextSearchOptions = {
...options,
numThreads: this._numThreads
};
const rgArgs = getRgArgs(query, extendedOptions);
const cwd = options.folder.fsPath;
@ -368,7 +373,7 @@ function getNumLinesAndLastNewlineLength(text: string): { numLines: number; last
}
// exported for testing
export function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): string[] {
export function getRgArgs(query: TextSearchQuery, options: RipgrepTextSearchOptions): string[] {
const args = ['--hidden', '--no-require-git'];
args.push(query.isCaseSensitive ? '--case-sensitive' : '--ignore-case');
@ -422,6 +427,10 @@ export function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): s
args.push('--encoding', options.encoding);
}
if (options.numThreads) {
args.push('--threads', `${options.numThreads}`);
}
// Ripgrep handles -- as a -- arg separator. Only --.
// - is ok, --- is ok, --some-flag is also ok. Need to special case.
if (query.pattern === '--') {

View file

@ -11,7 +11,7 @@ import { NativeTextSearchManager } from 'vs/workbench/services/search/node/textS
export class TextSearchEngineAdapter {
constructor(private query: ITextQuery) { }
constructor(private query: ITextQuery, private numThreads?: number) { }
search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgressMessage) => void): Promise<ISerializedSearchSuccess> {
if ((!this.query.folderQueries || !this.query.folderQueries.length) && (!this.query.extraFileResources || !this.query.extraFileResources.length)) {
@ -30,7 +30,7 @@ export class TextSearchEngineAdapter {
onMessage({ message: msg });
}
};
const textSearchManager = new NativeTextSearchManager(this.query, new RipgrepTextSearchEngine(pretendOutputChannel), pfs);
const textSearchManager = new NativeTextSearchManager(this.query, new RipgrepTextSearchEngine(pretendOutputChannel, this.numThreads), pfs);
return new Promise((resolve, reject) => {
return textSearchManager
.search(

View file

@ -25,11 +25,13 @@ const MULTIROOT_QUERIES: IFolderQuery[] = [
{ folder: URI.file(MORE_FIXTURES) }
];
const numThreads = undefined;
async function doSearchTest(query: IFileQuery, expectedResultCount: number | Function): Promise<void> {
const svc = new SearchService();
const results: ISerializedSearchProgressItem[] = [];
await svc.doFileSearch(query, e => {
await svc.doFileSearch(query, numThreads, e => {
if (!isProgressMessage(e)) {
if (Array.isArray(e)) {
results.push(...e);