Path: blob/main/src/vs/workbench/api/node/extHostSearch.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';6import { Schemas } from '../../../base/common/network.js';7import { URI } from '../../../base/common/uri.js';8import * as pfs from '../../../base/node/pfs.js';9import { ILogService } from '../../../platform/log/common/log.js';10import { IExtHostConfiguration } from '../common/extHostConfiguration.js';11import { IExtHostInitDataService } from '../common/extHostInitDataService.js';12import { IExtHostRpcService } from '../common/extHostRpcService.js';13import { ExtHostSearch, reviveQuery } from '../common/extHostSearch.js';14import { IURITransformerService } from '../common/extHostUriTransformerService.js';15import { IFileQuery, IRawFileQuery, ISearchCompleteStats, ISerializedSearchProgressItem, isSerializedFileMatch, ITextQuery } from '../../services/search/common/search.js';16import { TextSearchManager } from '../../services/search/common/textSearchManager.js';17import { SearchService } from '../../services/search/node/rawSearchService.js';18import { RipgrepSearchProvider } from '../../services/search/node/ripgrepSearchProvider.js';19import { OutputChannel } from '../../services/search/node/ripgrepSearchUtils.js';20import { NativeTextSearchManager } from '../../services/search/node/textSearchManager.js';21import type * as vscode from 'vscode';2223export class NativeExtHostSearch extends ExtHostSearch implements IDisposable {2425protected _pfs: typeof pfs = pfs; // allow extending for tests2627private _internalFileSearchHandle: number = -1;28private _internalFileSearchProvider: SearchService | null = null;2930private _registeredEHSearchProvider = false;3132private _numThreadsPromise: Promise<number | undefined> | undefined;3334private readonly _disposables = new DisposableStore();3536private isDisposed = false;3738constructor(39@IExtHostRpcService extHostRpc: IExtHostRpcService,40@IExtHostInitDataService initData: IExtHostInitDataService,41@IURITransformerService _uriTransformer: IURITransformerService,42@IExtHostConfiguration private readonly configurationService: IExtHostConfiguration,43@ILogService _logService: ILogService,44) {45super(extHostRpc, _uriTransformer, _logService);46this.getNumThreads = this.getNumThreads.bind(this);47this.getNumThreadsCached = this.getNumThreadsCached.bind(this);48this.handleConfigurationChanged = this.handleConfigurationChanged.bind(this);49const outputChannel = new OutputChannel('RipgrepSearchUD', this._logService);50this._disposables.add(this.registerTextSearchProvider(Schemas.vscodeUserData, new RipgrepSearchProvider(outputChannel, this.getNumThreadsCached)));51if (initData.remote.isRemote && initData.remote.authority) {52this._registerEHSearchProviders();53}5455configurationService.getConfigProvider().then(provider => {56if (this.isDisposed) {57return;58}59this._disposables.add(provider.onDidChangeConfiguration(this.handleConfigurationChanged));60});61}6263private handleConfigurationChanged(event: vscode.ConfigurationChangeEvent) {64if (!event.affectsConfiguration('search')) {65return;66}67this._numThreadsPromise = undefined;68}6970async getNumThreads(): Promise<number | undefined> {71const configProvider = await this.configurationService.getConfigProvider();72const numThreads = configProvider.getConfiguration('search').get<number>('ripgrep.maxThreads');73return numThreads;74}7576async getNumThreadsCached(): Promise<number | undefined> {77if (!this._numThreadsPromise) {78this._numThreadsPromise = this.getNumThreads();79}80return this._numThreadsPromise;81}8283dispose(): void {84this.isDisposed = true;85this._disposables.dispose();86}8788override $enableExtensionHostSearch(): void {89this._registerEHSearchProviders();90}9192private _registerEHSearchProviders(): void {93if (this._registeredEHSearchProvider) {94return;95}9697this._registeredEHSearchProvider = true;98const outputChannel = new OutputChannel('RipgrepSearchEH', this._logService);99this._disposables.add(this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel, this.getNumThreadsCached)));100this._disposables.add(this.registerInternalFileSearchProvider(Schemas.file, new SearchService('fileSearchProvider', this.getNumThreadsCached)));101}102103private registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {104const handle = this._handlePool++;105this._internalFileSearchProvider = provider;106this._internalFileSearchHandle = handle;107this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));108return toDisposable(() => {109this._internalFileSearchProvider = null;110this._proxy.$unregisterProvider(handle);111});112}113114override $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {115const query = reviveQuery(rawQuery);116if (handle === this._internalFileSearchHandle) {117const start = Date.now();118return this.doInternalFileSearch(handle, session, query, token).then(result => {119const elapsed = Date.now() - start;120this._logService.debug(`Ext host file search time: ${elapsed}ms`);121return result;122});123}124125return super.$provideFileSearchResults(handle, session, rawQuery, token);126}127128override async doInternalFileSearchWithCustomCallback(rawQuery: IFileQuery, token: vscode.CancellationToken, handleFileMatch: (data: URI[]) => void): Promise<ISearchCompleteStats> {129const onResult = (ev: ISerializedSearchProgressItem) => {130if (isSerializedFileMatch(ev)) {131ev = [ev];132}133134if (Array.isArray(ev)) {135handleFileMatch(ev.map(m => URI.file(m.path)));136return;137}138139if (ev.message) {140this._logService.debug('ExtHostSearch', ev.message);141}142};143144if (!this._internalFileSearchProvider) {145throw new Error('No internal file search handler');146}147const numThreads = await this.getNumThreadsCached();148return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, numThreads, onResult, token);149}150151private async doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {152return this.doInternalFileSearchWithCustomCallback(rawQuery, token, (data) => {153this._proxy.$handleFileMatch(handle, session, data);154});155}156157override $clearCache(cacheKey: string): Promise<void> {158this._internalFileSearchProvider?.clearCache(cacheKey);159160return super.$clearCache(cacheKey);161}162163protected override createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider2): TextSearchManager {164return new NativeTextSearchManager(query, provider, undefined, 'textSearchProvider');165}166}167168169