提交 0f312a4c 编写于 作者: C Christof Marti

Explore using ripgrep for QuickOpen (#24074)

上级 30b21868
......@@ -9,6 +9,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IExperiments {
ripgrepQuickSearch: boolean;
deployToAzureQuickLink: boolean;
}
......@@ -57,11 +58,12 @@ function applyOverrides(experiments: IExperiments, configurationService: IConfig
function splitExperimentsRandomness(storageService: IStorageService): IExperiments {
const random1 = getExperimentsRandomness(storageService);
const [random2, /* showTaskDocumentation */] = splitRandom(random1);
const [random2, ripgrepQuickSearch] = splitRandom(random1);
const [/* random3 */, deployToAzureQuickLink] = splitRandom(random2);
// const [random4, /* mergeQuickLinks */] = splitRandom(random3);
// const [random5, /* enableWelcomePage */] = splitRandom(random4);
return {
ripgrepQuickSearch,
deployToAzureQuickLink
};
}
......
......@@ -32,6 +32,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IRange } from 'vs/editor/common/core/range';
import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search';
import { IExperimentService } from 'vs/platform/telemetry/common/experiments';
export class FileQuickOpenModel extends QuickOpenModel {
......@@ -122,6 +123,7 @@ export class OpenFileHandler extends QuickOpenHandler {
@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@ISearchService private searchService: ISearchService,
@IExperimentService private experimentService: IExperimentService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
super();
......@@ -193,7 +195,8 @@ export class OpenFileHandler extends QuickOpenHandler {
filePattern: '',
cacheKey: cacheKey,
maxResults: 0,
sortByScore: true
sortByScore: true,
useRipgrep: this.experimentService.getExperiments().ripgrepQuickSearch
};
const folderResources = this.contextService.hasWorkspace() ? this.contextService.getWorkspace().roots : [];
......
......@@ -55,7 +55,7 @@ export class QueryBuilder {
}
}
const useRipgrep = !folderResources || folderResources.every(folder => {
const useRipgrep = type === QueryType.File ? options.useRipgrep : !folderResources || folderResources.every(folder => {
const folderConfig = this.configurationService.getConfiguration<ISearchConfiguration>(undefined, { resource: folder });
return folderConfig.search.useRipgrep;
});
......
......@@ -26,12 +26,14 @@ import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/searc
import extfs = require('vs/base/node/extfs');
import flow = require('vs/base/node/flow');
import { IRawFileMatch, ISerializedSearchComplete, IRawSearch, ISearchEngine, IFolderSearch } from './search';
import { spawnRipgrepCmd } from './ripgrepFileSearch';
enum Traversal {
Node = 1,
MacFind,
WindowsDir,
LinuxFind
LinuxFind,
Ripgrep
}
interface IDirectoryEntry {
......@@ -151,16 +153,19 @@ export class FileWalker {
let traverse = this.nodeJSTraversal;
if (!this.maxFilesize) {
if (platform.isMacintosh) {
if (this.config.useRipgrep) {
this.traversal = Traversal.Ripgrep;
traverse = this.cmdTraversal;
} else if (platform.isMacintosh) {
this.traversal = Traversal.MacFind;
traverse = this.findTraversal;
traverse = this.cmdTraversal;
// Disable 'dir' for now (#11181, #11179, #11183, #11182).
} /* else if (platform.isWindows) {
this.traversal = Traversal.WindowsDir;
traverse = this.windowsDirTraversal;
} */ else if (platform.isLinux) {
this.traversal = Traversal.LinuxFind;
traverse = this.findTraversal;
traverse = this.cmdTraversal;
}
}
......@@ -200,7 +205,7 @@ export class FileWalker {
}
}
private findTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, cb: (err?: Error) => void): void {
private cmdTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, cb: (err?: Error) => void): void {
const rootFolder = folderQuery.folder;
const isMac = platform.isMacintosh;
let done = (err?: Error) => {
......@@ -210,7 +215,8 @@ export class FileWalker {
let leftover = '';
let first = true;
const tree = this.initDirectoryTree();
const cmd = this.spawnFindCmd(folderQuery);
const useRipgrep = this.config.useRipgrep;
const cmd = useRipgrep ? spawnRipgrepCmd(folderQuery, this.config.includePattern, this.folderExcludePatterns.get(folderQuery.folder)).cmd : this.spawnFindCmd(folderQuery);
this.collectStdout(cmd, 'utf8', (err: Error, stdout?: string, last?: boolean) => {
if (err) {
done(err);
......@@ -219,7 +225,7 @@ export class FileWalker {
// Mac: uses NFD unicode form on disk, but we want NFC
const normalized = leftover + (isMac ? strings.normalizeNFC(stdout) : stdout);
const relativeFiles = normalized.split('\n./');
const relativeFiles = normalized.split(useRipgrep ? '\n' : '\n./');
if (first && normalized.length >= 2) {
first = false;
relativeFiles[0] = relativeFiles[0].trim().substr(2);
......@@ -240,6 +246,7 @@ export class FileWalker {
return;
}
// TODO: Optimize siblings clauses with ripgrep here.
this.addDirectoryEntries(tree, rootFolder, relativeFiles, onResult);
if (last) {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import { rgPath } from 'vscode-ripgrep';
import * as glob from 'vs/base/common/glob';
import { IFolderSearch } from './search';
import { foldersToIncludeGlobs, foldersToRgExcludeGlobs } from './ripgrepTextSearch';
export function spawnRipgrepCmd(folderQuery: IFolderSearch, includePattern: glob.IExpression, excludePattern: glob.IExpression) {
const rgArgs = getRgArgs(folderQuery, includePattern, excludePattern);
return {
cmd: cp.spawn(rgPath, rgArgs.globArgs, { cwd: folderQuery.folder }),
siblingClauses: rgArgs.siblingClauses
};
}
function getRgArgs(folderQuery: IFolderSearch, includePattern: glob.IExpression, excludePattern: glob.IExpression) {
const args = ['--files', '--hidden', '--case-sensitive'];
// includePattern can't have siblingClauses
foldersToIncludeGlobs([folderQuery], includePattern, false).forEach(globArg => {
args.push('-g', globArg);
});
let siblingClauses: glob.IExpression;
const rgGlobs = foldersToRgExcludeGlobs([folderQuery], excludePattern, undefined, false);
rgGlobs.globArgs
.forEach(rgGlob => args.push('-g', `!${rgGlob}`));
siblingClauses = rgGlobs.siblingClauses;
// Don't use .gitignore or .ignore
args.push('--no-ignore');
// Follow symlinks
args.push('--follow');
// Folder to search
args.push('--');
args.push('.');
return { globArgs: args, siblingClauses };
}
......@@ -363,17 +363,17 @@ export class LineMatch implements ILineMatch {
}
}
interface IRgGlobResult {
export interface IRgGlobResult {
globArgs: string[];
siblingClauses: glob.IExpression;
}
function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: glob.IExpression, excludesToSkip: Set<string>): IRgGlobResult {
export function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: glob.IExpression, excludesToSkip?: Set<string>, absoluteGlobs = true): IRgGlobResult {
const globArgs: string[] = [];
let siblingClauses: glob.IExpression = {};
folderQueries.forEach(folderQuery => {
const totalExcludePattern = objects.assign({}, folderQuery.excludePattern || {}, globalExclude || {});
const result = globExprsToRgGlobs(totalExcludePattern, folderQuery.folder, excludesToSkip);
const result = globExprsToRgGlobs(totalExcludePattern, absoluteGlobs && folderQuery.folder, excludesToSkip);
globArgs.push(...result.globArgs);
if (result.siblingClauses) {
siblingClauses = objects.assign(siblingClauses, result.siblingClauses);
......@@ -383,18 +383,18 @@ function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude:
return { globArgs, siblingClauses };
}
function foldersToIncludeGlobs(folderQueries: IFolderSearch[], globalInclude: glob.IExpression): string[] {
export function foldersToIncludeGlobs(folderQueries: IFolderSearch[], globalInclude: glob.IExpression, absoluteGlobs = true): string[] {
const globArgs: string[] = [];
folderQueries.forEach(folderQuery => {
const totalIncludePattern = objects.assign({}, globalInclude || {}, folderQuery.includePattern || {});
const result = globExprsToRgGlobs(totalIncludePattern, folderQuery.folder);
const result = globExprsToRgGlobs(totalIncludePattern, absoluteGlobs && folderQuery.folder);
globArgs.push(...result.globArgs);
});
return globArgs;
}
function globExprsToRgGlobs(patterns: glob.IExpression, folder: string, excludesToSkip?: Set<string>): IRgGlobResult {
function globExprsToRgGlobs(patterns: glob.IExpression, folder?: string, excludesToSkip?: Set<string>): IRgGlobResult {
const globArgs: string[] = [];
let siblingClauses: glob.IExpression = null;
Object.keys(patterns)
......@@ -404,7 +404,7 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder: string, excludes
}
const value = patterns[key];
key = getAbsoluteGlob(folder, key);
key = trimTrailingSlash(folder ? getAbsoluteGlob(folder, key) : key);
if (typeof value === 'boolean' && value) {
globArgs.push(fixDriveC(key));
......@@ -427,11 +427,9 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder: string, excludes
* Exported for testing
*/
export function getAbsoluteGlob(folder: string, key: string): string {
const absolute = paths.isAbsolute(key) ?
return paths.isAbsolute(key) ?
key :
path.join(folder, key);
return trimTrailingSlash(absolute);
}
function trimTrailingSlash(str: string): string {
......
......@@ -12,6 +12,7 @@ import { createSyncDescriptor } from 'vs/platform/instantiation/common/descripto
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ISearchService } from 'vs/platform/search/common/search';
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { IExperimentService, IExperiments } from 'vs/platform/telemetry/common/experiments';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import * as minimist from 'minimist';
......@@ -66,9 +67,11 @@ suite('QuickOpen performance (integration)', () => {
const testWorkspacePath = testWorkspaceArg ? path.resolve(testWorkspaceArg) : __dirname;
const telemetryService = new TestTelemetryService();
const experimentService = new TestExperimentService();
const configurationService = new SimpleConfigurationService();
const instantiationService = new InstantiationService(new ServiceCollection(
[ITelemetryService, telemetryService],
[IExperimentService, experimentService],
[IConfigurationService, configurationService],
[IModelService, new ModelServiceImpl(null, configurationService)],
[IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))],
......@@ -177,3 +180,15 @@ class TestTelemetryService implements ITelemetryService {
});
}
};
class TestExperimentService implements IExperimentService {
_serviceBrand: any;
getExperiments(): IExperiments {
return {
ripgrepQuickSearch: true,
deployToAzureQuickLink: false
};
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册