提交 3ebdc67d 编写于 作者: R Rob Lourens

Add search.useRipgrep to turn on ripgrep searching,

and restore original search code. Should work exactly the same when !useRipgrep.
上级 24e2cdc7
......@@ -35,6 +35,7 @@ export interface IQueryOptions {
sortByScore?: boolean;
cacheKey?: string;
fileEncoding?: string;
useRipgrep?: boolean;
}
export interface ISearchQuery extends IQueryOptions {
......@@ -127,6 +128,7 @@ export class LineMatch implements ILineMatch {
export interface ISearchConfiguration extends IFilesConfiguration {
search: {
exclude: IExpression;
useRipgrep: boolean;
};
}
......
......@@ -204,6 +204,11 @@ configurationRegistry.registerConfiguration({
]
}
},
'search.useRipgrep': {
'type': 'boolean',
'description': nls.localize('useRipgrep', "Controls whether to use ripgrep in text search"),
'default': false
},
'search.quickOpen.includeSymbols': {
'type': 'boolean',
'description': nls.localize('search.quickOpen.includeSymbols', "Configure to include results from a global symbol search in the file results for Quick Open."),
......
......@@ -35,7 +35,7 @@ import { Viewlet } from 'vs/workbench/browser/viewlet';
import { Match, FileMatch, SearchModel, FileMatchOrMatch, IChangeEvent, ISearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel';
import { QueryBuilder } from 'vs/workbench/parts/search/common/searchQuery';
import { MessageType, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { getExcludes, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration } from 'vs/platform/search/common/search';
import { getExcludes, ISearchProgressItem, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration } from 'vs/platform/search/common/search';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
......@@ -988,7 +988,13 @@ export class SearchViewlet extends Viewlet {
private onQueryTriggered(query: ISearchQuery, excludePattern: string, includePattern: string): void {
this.viewModel.cancelSearch();
let progressRunner = this.progressService.show(/*infinite=*/true);
// Progress total is 100.0% for more progress bar granularity
let progressTotal = 1000;
let progressWorked = 0;
let progressRunner = query.useRipgrep ?
this.progressService.show(/*infinite=*/true) :
this.progressService.show(progressTotal);
this.loading = true;
this.searchWidget.searchInput.clearMessage();
......@@ -1012,7 +1018,14 @@ export class SearchViewlet extends Viewlet {
let isDone = false;
let onComplete = (completed?: ISearchComplete) => {
isDone = true;
progressRunner.done();
// Complete up to 100% as needed
if (completed && !query.useRipgrep) {
progressRunner.worked(progressTotal - progressWorked);
setTimeout(() => progressRunner.done(), 200);
} else {
progressRunner.done();
}
this.onSearchResultsChanged().then(() => autoExpand(true));
this.viewModel.replaceString = this.searchWidget.getReplaceValue();
......@@ -1116,7 +1129,18 @@ export class SearchViewlet extends Viewlet {
}
};
let total: number = 0;
let worked: number = 0;
let visibleMatches = 0;
let onProgress = (p: ISearchProgressItem) => {
// Progress
if (p.total) {
total = p.total;
}
if (p.worked) {
worked = p.worked;
}
};
// Handle UI updates in an interval to show frequent progress and results
let uiRefreshHandle = setInterval(() => {
......@@ -1125,6 +1149,30 @@ export class SearchViewlet extends Viewlet {
return;
}
if (!query.useRipgrep) {
// Progress bar update
let fakeProgress = true;
if (total > 0 && worked > 0) {
let ratio = Math.round((worked / total) * progressTotal);
if (ratio > progressWorked) { // never show less progress than what we have already
progressRunner.worked(ratio - progressWorked);
progressWorked = ratio;
fakeProgress = false;
}
}
// Fake progress up to 90%, or when actual progress beats it
const fakeMax = 900;
const fakeMultiplier = 12;
if (fakeProgress && progressWorked < fakeMax) {
// Linearly decrease the rate of fake progress.
// 1 is the smallest allowed amount of progress.
const fakeAmt = Math.round((fakeMax - progressWorked) / fakeMax * fakeMultiplier) || 1;
progressWorked += fakeAmt;
progressRunner.worked(fakeAmt);
}
}
// Search result tree update
const fileCount = this.viewModel.searchResult.fileCount();
if (visibleMatches !== fileCount) {
......@@ -1145,7 +1193,7 @@ export class SearchViewlet extends Viewlet {
this.searchWidget.setReplaceAllActionState(false);
// this.replaceService.disposeAllReplacePreviews();
this.viewModel.search(query).done(onComplete, onError);
this.viewModel.search(query).done(onComplete, onError, query.useRipgrep ? undefined : onProgress);
}
private updateSearchResultCount(): void {
......
......@@ -86,6 +86,7 @@ export interface IWorkbenchSearchConfiguration extends ISearchConfiguration {
quickOpen: {
includeSymbols: boolean;
},
exclude: glob.IExpression;
exclude: glob.IExpression,
useRipgrep: boolean
};
}
\ No newline at end of file
......@@ -42,7 +42,8 @@ export class QueryBuilder {
sortByScore: options.sortByScore,
cacheKey: options.cacheKey,
fileEncoding: options.fileEncoding,
contentPattern: contentPattern
contentPattern: contentPattern,
useRipgrep: configuration.search.useRipgrep
};
}
}
\ No newline at end of file
......@@ -17,9 +17,11 @@ import objects = require('vs/base/common/objects');
import scorer = require('vs/base/common/scorer');
import strings = require('vs/base/common/strings');
import { PPromise, TPromise } from 'vs/base/common/winjs.base';
import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch';
import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch';
import { MAX_FILE_SIZE } from 'vs/platform/files/common/files';
import { RipgrepEngine } from 'vs/workbench/services/search/node/ripgrepTextSearch';
import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch';
import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider';
import { IRawSearchService, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchProgressItem, ISerializedSearchComplete, ISearchEngine } from './search';
import { ICachedSearchStats, IProgress } from 'vs/platform/search/common/search';
......@@ -31,13 +33,60 @@ export class SearchService implements IRawSearchService {
private caches: { [cacheKey: string]: Cache; } = Object.create(null);
private textSearchWorkerProvider: TextSearchWorkerProvider;
public fileSearch(config: IRawSearch): PPromise<ISerializedSearchComplete, ISerializedSearchProgressItem> {
return this.doFileSearch(FileSearchEngine, config, SearchService.BATCH_SIZE);
}
public textSearch(config: IRawSearch): PPromise<ISerializedSearchComplete, ISerializedSearchProgressItem> {
return config.useRipgrep ?
this.ripgrepTextSearch(config) :
this.legacyTextSearch(config);
}
public ripgrepTextSearch(config: IRawSearch): PPromise<ISerializedSearchComplete, ISerializedSearchProgressItem> {
config.maxFilesize = MAX_FILE_SIZE;
let engine = new RipgrepEngine(config);
return new PPromise<ISerializedSearchComplete, IRawProgressItem<ISerializedFileMatch>>((c, e, p) => {
// Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned
const collector = new BatchedCollector<ISerializedFileMatch>(SearchService.BATCH_SIZE, p);
engine.search((match) => {
collector.addItem(match, match.numMatches);
}, (progress) => {
p(progress);
}, (error, stats) => {
collector.flush();
if (error) {
e(error);
} else {
c(stats);
}
});
}, () => {
engine.cancel();
});
}
public legacyTextSearch(config: IRawSearch): PPromise<ISerializedSearchComplete, ISerializedSearchProgressItem> {
if (!this.textSearchWorkerProvider) {
this.textSearchWorkerProvider = new TextSearchWorkerProvider();
}
let engine = new TextSearchEngine(
config,
new FileWalker({
rootFolders: config.rootFolders,
extraFiles: config.extraFiles,
includePattern: config.includePattern,
excludePattern: config.excludePattern,
filePattern: config.filePattern,
maxFilesize: MAX_FILE_SIZE
}),
this.textSearchWorkerProvider);
return this.doTextSearch(engine, SearchService.BATCH_SIZE);
}
......@@ -262,12 +311,13 @@ export class SearchService implements IRawSearchService {
});
}
private doTextSearch(engine: RipgrepEngine, batchSize: number): PPromise<ISerializedSearchComplete, IRawProgressItem<ISerializedFileMatch>> {
private doTextSearch(engine: TextSearchEngine, batchSize: number): PPromise<ISerializedSearchComplete, IRawProgressItem<ISerializedFileMatch>> {
return new PPromise<ISerializedSearchComplete, IRawProgressItem<ISerializedFileMatch>>((c, e, p) => {
// Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned
const collector = new BatchedCollector<ISerializedFileMatch>(batchSize, p);
engine.search((match) => {
collector.addItem(match, match.numMatches);
engine.search((matches) => {
const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0);
collector.addItems(matches, totalMatches);
}, (progress) => {
p(progress);
}, (error, stats) => {
......@@ -393,9 +443,31 @@ class BatchedCollector<T> {
}
}
addItems(items: T[], size: number): void {
if (!items) {
return;
}
if (this.maxBatchSize > 0) {
this.addItemsToBatch(items, size);
} else {
this.cb(items);
}
}
private addItemToBatch(item: T, size: number): void {
this.batch.push(item);
this.batchSize += size;
this.onUpdate();
}
private addItemsToBatch(item: T[], size: number): void {
this.batch = this.batch.concat(item);
this.batchSize += size;
this.onUpdate();
}
private onUpdate(): void {
if (this.totalNumberCompleted < BatchedCollector.START_BATCH_AFTER_COUNT) {
// Flush because we aren't batching yet
this.flush();
......
......@@ -21,6 +21,7 @@ export interface IRawSearch {
cacheKey?: string;
maxFilesize?: number;
fileEncoding?: string;
useRipgrep?: boolean;
}
export interface IRawSearchService {
......
......@@ -234,7 +234,8 @@ export class DiskSearch {
includePattern: query.includePattern,
maxResults: query.maxResults,
sortByScore: query.sortByScore,
cacheKey: query.cacheKey
cacheKey: query.cacheKey,
useRipgrep: query.useRipgrep
};
if (query.type === QueryType.Text) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册