enable fuzzy matching for file packer by default

This commit is contained in:
Benjamin Pasero 2016-01-13 16:19:21 +01:00
parent 68e5439a08
commit b82b39af71
10 changed files with 49 additions and 91 deletions

View file

@ -12,6 +12,5 @@
".build/**": true,
"out*/**": true,
"extensions/**/out/**": true
},
"filePicker.alternateFileNameMatching": true
}
}

View file

@ -29,7 +29,6 @@ export interface IQueryOptions {
includePattern?: glob.IExpression;
maxResults?: number;
fileEncoding?: string;
matchFuzzy?: boolean;
}
export interface ISearchQuery extends IQueryOptions {
@ -94,8 +93,4 @@ export interface ISearchConfiguration extends IFilesConfiguration {
search: {
exclude: glob.IExpression;
};
filePicker: {
alternateFileNameMatching: boolean;
};
}

View file

@ -51,8 +51,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
private delayer: ThrottledDelayer<QuickOpenModel>;
private pendingSearch: TPromise<QuickOpenModel>;
private isClosed: boolean;
private scorerCache: {[key: string]: number};
private fuzzyMatchingEnabled: boolean;
private scorerCache: { [key: string]: number };
private configurationListenerUnbind: ListenerUnbind;
constructor(
@ -73,19 +72,6 @@ export class OpenAnythingHandler extends QuickOpenHandler {
this.resultsToSearchCache = Object.create(null);
this.scorerCache = Object.create(null);
this.delayer = new ThrottledDelayer<QuickOpenModel>(OpenAnythingHandler.SEARCH_DELAY);
this.updateFuzzyMatching(contextService.getOptions().globalSettings.settings);
this.registerListeners();
}
private registerListeners(): void {
this.configurationListenerUnbind = this.configurationService.addListener(ConfigurationServiceEventTypes.UPDATED, (e: IConfigurationServiceEvent) => this.updateFuzzyMatching(e.config));
}
private updateFuzzyMatching(configuration: ISearchConfiguration): void {
this.fuzzyMatchingEnabled = configuration.filePicker && configuration.filePicker.alternateFileNameMatching;
this.openFileHandler.setFuzzyMatchingEnabled(this.fuzzyMatchingEnabled);
}
public getResults(searchValue: string): TPromise<QuickOpenModel> {
@ -172,7 +158,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
let result = [...results[0].entries, ...results[1].entries];
// Sort
result.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue, this.fuzzyMatchingEnabled));
result.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue));
// Apply Range
result.forEach((element) => {
@ -271,7 +257,6 @@ export class OpenAnythingHandler extends QuickOpenHandler {
// Pattern match on results and adjust highlights
let results: QuickOpenEntry[] = [];
const searchInPath = this.fuzzyMatchingEnabled || searchValue.indexOf(paths.nativeSep) >= 0;
for (let i = 0; i < cachedEntries.length; i++) {
let entry = cachedEntries[i];
@ -282,20 +267,20 @@ export class OpenAnythingHandler extends QuickOpenHandler {
// Check if this entry is a match for the search value
const resource = entry.getResource(); // can be null for symbol results!
let targetToMatch = searchInPath && resource ? labels.getPathLabel(resource, this.contextService) : entry.getLabel();
if (!filters.matchesFuzzy(searchValue, targetToMatch, this.fuzzyMatchingEnabled)) {
let targetToMatch = resource ? labels.getPathLabel(resource, this.contextService) : entry.getLabel();
if (!filters.matchesFuzzy(searchValue, targetToMatch, true /* separate substring matching */)) {
continue;
}
// Apply highlights
const {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, this.fuzzyMatchingEnabled);
const {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, true /* fuzzy highlight */);
entry.setHighlights(labelHighlights, descriptionHighlights);
results.push(entry);
}
// Sort
results.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue, this.fuzzyMatchingEnabled));
results.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue));
// Apply Range
results.forEach((element) => {
@ -310,55 +295,51 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return viewResults;
}
private sort(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, enableFuzzyScoring: boolean): number {
private sort(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number {
const labelA = elementA.getLabel();
const labelB = elementB.getLabel();
// Fuzzy scoring is special
if (enableFuzzyScoring) {
const labelA = elementA.getLabel();
const labelB = elementB.getLabel();
// treat prefix matches highest in any case
const prefixCompare = compareByPrefix(labelA, labelB, lookFor);
if (prefixCompare) {
return prefixCompare;
}
// treat prefix matches highest in any case
const prefixCompare = compareByPrefix(labelA, labelB, lookFor);
if (prefixCompare) {
return prefixCompare;
}
// Give higher importance to label score
const labelAScore = scorer.score(labelA, lookFor, this.scorerCache);
const labelBScore = scorer.score(labelB, lookFor, this.scorerCache);
// Give higher importance to label score
const labelAScore = scorer.score(labelA, lookFor, this.scorerCache);
const labelBScore = scorer.score(labelB, lookFor, this.scorerCache);
// Useful for understanding the scoring
// elementA.setPrefix(labelAScore + ' ');
// elementB.setPrefix(labelBScore + ' ');
if (labelAScore !== labelBScore) {
return labelAScore > labelBScore ? -1 : 1;
}
// Score on full resource path comes next (can be null for symbols!)
let resourceA = elementA.getResource();
let resourceB = elementB.getResource();
if (resourceA && resourceB) {
const resourceAScore = scorer.score(resourceA.fsPath, lookFor, this.scorerCache);
const resourceBScore = scorer.score(resourceB.fsPath, lookFor, this.scorerCache);
// Useful for understanding the scoring
// elementA.setPrefix(labelAScore + ' ');
// elementB.setPrefix(labelBScore + ' ');
// elementA.setPrefix(elementA.getPrefix() + ' ' + resourceAScore + ': ');
// elementB.setPrefix(elementB.getPrefix() + ' ' + resourceBScore + ': ');
if (labelAScore !== labelBScore) {
return labelAScore > labelBScore ? -1 : 1;
if (resourceAScore !== resourceBScore) {
return resourceAScore > resourceBScore ? -1 : 1;
}
}
// Score on full resource path comes next (can be null for symbols!)
let resourceA = elementA.getResource();
let resourceB = elementB.getResource();
if (resourceA && resourceB) {
const resourceAScore = scorer.score(resourceA.fsPath, lookFor, this.scorerCache);
const resourceBScore = scorer.score(resourceB.fsPath, lookFor, this.scorerCache);
// At this place, the scores are identical so we check for string lengths and favor shorter ones
if (labelA.length !== labelB.length) {
return labelA.length < labelB.length ? -1 : 1;
}
// Useful for understanding the scoring
// elementA.setPrefix(elementA.getPrefix() + ' ' + resourceAScore + ': ');
// elementB.setPrefix(elementB.getPrefix() + ' ' + resourceBScore + ': ');
if (resourceAScore !== resourceBScore) {
return resourceAScore > resourceBScore ? -1 : 1;
}
}
// At this place, the scores are identical so we check for string lengths and favor shorter ones
if (labelA.length !== labelB.length) {
return labelA.length < labelB.length ? -1 : 1;
}
if (resourceA && resourceB && resourceA.fsPath.length !== resourceB.fsPath.length) {
return resourceA.fsPath.length < resourceB.fsPath.length ? -1 : 1;
}
if (resourceA && resourceB && resourceA.fsPath.length !== resourceB.fsPath.length) {
return resourceA.fsPath.length < resourceB.fsPath.length ? -1 : 1;
}
return QuickOpenEntry.compare(elementA, elementB, lookFor);

View file

@ -88,7 +88,6 @@ export class OpenFileHandler extends QuickOpenHandler {
private queryBuilder: QueryBuilder;
private delayer: ThrottledDelayer<QuickOpenEntry[]>;
private isStandalone: boolean;
private fuzzyMatchingEnabled: boolean;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ -111,10 +110,6 @@ export class OpenFileHandler extends QuickOpenHandler {
this.isStandalone = standalone;
}
public setFuzzyMatchingEnabled(enabled: boolean): void {
this.fuzzyMatchingEnabled = enabled;
}
public getResults(searchValue: string): TPromise<QuickOpenModel> {
searchValue = searchValue.trim();
let promise: TPromise<QuickOpenEntry[]>;
@ -135,8 +130,7 @@ export class OpenFileHandler extends QuickOpenHandler {
let query: IQueryOptions = {
folderResources: this.contextService.getWorkspace() ? [this.contextService.getWorkspace().resource] : [],
extraFileResources: this.textFileService.getWorkingFilesModel().getOutOfWorkspaceContextEntries().map(e => e.resource),
filePattern: searchValue,
matchFuzzy: this.fuzzyMatchingEnabled
filePattern: searchValue
};
return this.queryBuilder.file(query).then((query) => this.searchService.search(query)).then((complete) => {
@ -152,7 +146,7 @@ export class OpenFileHandler extends QuickOpenHandler {
let entry = this.instantiationService.createInstance(FileEntry, label, description, fileMatch.resource);
// Apply highlights
let {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, this.fuzzyMatchingEnabled);
let {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, true /* fuzzy highlight */);
entry.setHighlights(labelHighlights, descriptionHighlights);
results.push(entry);

View file

@ -196,11 +196,6 @@ configurationRegistry.registerConfiguration({
}
]
}
},
'filePicker.alternateFileNameMatching': {
'type': 'boolean',
'default': false,
'description': nls.localize('enableFuzzy', "Experimental support for fuzzy matching of file names in the file picker.")
}
}
});

View file

@ -60,8 +60,7 @@ export class QueryBuilder {
includePattern: options.includePattern,
maxResults: options.maxResults,
fileEncoding: options.fileEncoding,
contentPattern: contentPattern,
matchFuzzy: options.matchFuzzy
contentPattern: contentPattern
};
});
}

View file

@ -28,14 +28,12 @@ export class FileWalker {
private resultCount: number;
private isCanceled: boolean;
private searchInPath: boolean;
private matchFuzzy: boolean;
private walkedPaths: { [path: string]: boolean; };
constructor(config: IRawSearch) {
this.config = config;
this.filePattern = config.filePattern;
this.matchFuzzy = config.matchFuzzy;
this.excludePattern = config.excludePattern;
this.includePattern = config.includePattern;
this.maxResults = config.maxResults || null;
@ -231,7 +229,7 @@ export class FileWalker {
// Check for search pattern
if (this.filePattern) {
const res = filters.matchesFuzzy(this.filePattern, this.matchFuzzy || this.searchInPath ? path : name, this.matchFuzzy);
const res = filters.matchesFuzzy(this.filePattern, path, true /* separate substring matching */);
return !!res && res.length > 0;
}

View file

@ -20,7 +20,6 @@ export interface IRawSearch {
rootFolders: string[];
extraFiles?: string[];
filePattern?: string;
matchFuzzy?: boolean;
excludePattern?: glob.IExpression;
includePattern?: glob.IExpression;
contentPattern?: IPatternInfo;

View file

@ -218,8 +218,7 @@ class DiskSearch {
filePattern: query.filePattern,
excludePattern: query.excludePattern,
includePattern: query.includePattern,
maxResults: query.maxResults,
matchFuzzy: query.matchFuzzy
maxResults: query.maxResults
};
if (query.type === QueryType.Text) {

View file

@ -73,8 +73,7 @@ suite('Search', () => {
test('Files: examples (fuzzy)', function(done: () => void) {
let engine = new FileSearchEngine({
rootFolders: rootfolders(),
filePattern: 'xl',
matchFuzzy: true
filePattern: 'xl'
});
let count = 0;