mirror of
https://github.com/Microsoft/vscode
synced 2024-10-05 19:02:54 +00:00
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:
parent
a38eb611fa
commit
0036990784
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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."),
|
||||
|
|
|
@ -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 { }
|
|
@ -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(),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 === '--') {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue