Make the fuzzy option an option for the file picker only

This commit is contained in:
Benjamin Pasero
2015-12-09 16:53:58 +01:00
parent 219b61e7f9
commit f5e97d84f3
9 changed files with 70 additions and 89 deletions
@@ -136,31 +136,12 @@ export class QuickOpenEntry {
/**
* A good default sort implementation for quick open entries respecting highlight information
* as well as associated resources.
*
* Supports fuzzy scoring when the option is enabled.
*/
public static compare(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, enableFuzzyScoring = false): number {
public static compare(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number {
// Normalize
if (lookFor) {
lookFor = Strings.stripWildcards(lookFor);
}
// Fuzzy scoring is special
if (enableFuzzyScoring) {
const labelAScore = Strings.score(elementA.getLabel(), lookFor);
const labelBScore = Strings.score(elementB.getLabel(), lookFor);
if (labelAScore !== labelBScore) {
return labelAScore > labelBScore ? -1 : 1;
}
const descriptionAScore = Strings.score(elementA.getDescription(), lookFor);
const descriptionBScore = Strings.score(elementB.getDescription(), lookFor);
if (descriptionAScore !== descriptionBScore) {
return descriptionAScore > descriptionBScore ? -1 : 1;
}
lookFor = Strings.stripWildcards(lookFor).toLowerCase();
}
// Give matches with label highlights higher priority over
@@ -186,15 +167,13 @@ export class QuickOpenEntry {
}
}
return compareAnything(nameA, nameB, lookFor ? lookFor.toLowerCase() : lookFor);
return compareAnything(nameA, nameB, lookFor);
}
/**
* A good default highlight implementation for an entry with label and description.
*
* Supports fuzzy matching when the option is enabled.
*/
public static highlight(entry: QuickOpenEntry, lookFor: string, enableFuzzyMatching = false): { labelHighlights: IHighlight[], descriptionHighlights: IHighlight[] } {
public static highlight(entry: QuickOpenEntry, lookFor: string, enableSeparateSubstringMatching = false): { labelHighlights: IHighlight[], descriptionHighlights: IHighlight[] } {
let labelHighlights: IHighlight[] = [];
let descriptionHighlights: IHighlight[] = [];
@@ -203,24 +182,24 @@ export class QuickOpenEntry {
// Highlight only inside label
if (lookFor.indexOf(Paths.nativeSep) < 0) {
labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel(), enableFuzzyMatching);
labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel(), enableSeparateSubstringMatching);
}
// Highlight in label and description
else {
descriptionHighlights = Filters.matchesFuzzy(Strings.trim(lookFor, Paths.nativeSep), entry.getDescription(), enableFuzzyMatching);
descriptionHighlights = Filters.matchesFuzzy(Strings.trim(lookFor, Paths.nativeSep), entry.getDescription(), enableSeparateSubstringMatching);
// If we have no highlights, assume that the match is split among name and parent folder
if (!descriptionHighlights || !descriptionHighlights.length) {
labelHighlights = Filters.matchesFuzzy(Paths.basename(lookFor), entry.getLabel(), enableFuzzyMatching);
descriptionHighlights = Filters.matchesFuzzy(Strings.trim(Paths.dirname(lookFor), Paths.nativeSep), entry.getDescription(), enableFuzzyMatching);
labelHighlights = Filters.matchesFuzzy(Paths.basename(lookFor), entry.getLabel(), enableSeparateSubstringMatching);
descriptionHighlights = Filters.matchesFuzzy(Strings.trim(Paths.dirname(lookFor), Paths.nativeSep), entry.getDescription(), enableSeparateSubstringMatching);
}
}
}
// Highlight by label otherwise
else {
labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel(), enableFuzzyMatching);
labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel(), enableSeparateSubstringMatching);
}
return { labelHighlights, descriptionHighlights };
+1
View File
@@ -92,5 +92,6 @@ export class LineMatch implements ILineMatch {
export interface ISearchConfiguration extends IFilesConfiguration {
search: {
exclude: glob.IExpression;
fuzzyFilePicker: boolean;
};
}
@@ -13,7 +13,6 @@ import strings = require('vs/base/common/strings');
import filters = require('vs/base/common/filters');
import uuid = require('vs/base/common/uuid');
import types = require('vs/base/common/types');
import {ListenerUnbind} from 'vs/base/common/eventEmitter';
import {Mode, IContext, IAutoFocus, IQuickNavigateConfiguration, IModel} from 'vs/base/parts/quickopen/browser/quickOpen';
import {QuickOpenEntryItem, QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import {QuickOpenWidget} from 'vs/base/parts/quickopen/browser/quickOpenWidget';
@@ -79,8 +78,6 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe
private actionProvider = new ContributableActionProvider();
private previousValue = '';
private visibilityChangeTimeoutHandle: number;
private fuzzyMatchingEnabled: boolean;
private configurationListenerUnbind: ListenerUnbind;
constructor(
private eventService: IEventService,
@@ -102,22 +99,8 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe
this.inQuickOpenMode = keybindingService.createKey(QUICK_OPEN_MODE, false);
this.updateFuzzyMatching(contextService.getOptions().globalSettings.settings);
this._onShow = new EventSource<() => void>();
this._onHide = new EventSource<() => void>();
this.registerListeners();
}
private registerListeners(): void {
// Listen to configuration changes
this.configurationListenerUnbind = this.configurationService.addListener(ConfigurationServiceEventTypes.UPDATED, (e: IConfigurationServiceEvent) => this.updateFuzzyMatching(e.config));
}
private updateFuzzyMatching(configuration: any): void {
this.fuzzyMatchingEnabled = configuration.picker && configuration.picker.enableFuzzy;
}
public get onShow(): EventProvider<() => void> {
@@ -136,10 +119,6 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe
return this.editorHistoryModel;
}
public isFuzzyMatchingEnabled(): boolean {
return this.fuzzyMatchingEnabled;
}
public create(): void {
// Listen on Editor Input Changes to show in MRU List
@@ -852,10 +831,6 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe
this.pickOpenWidget.dispose();
}
if (this.configurationListenerUnbind) {
this.configurationListenerUnbind();
}
super.dispose();
}
}
@@ -73,19 +73,4 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('updateChannel', "Configure the update channel to receive updates from. Requires a restart after change.")
}
}
});
// Picker Configuration
configurationRegistry.registerConfiguration({
'id': 'picker',
'order': 11,
'title': nls.localize('filterConfigurationTitle', "Picker configuration"),
'type': 'object',
'properties': {
'picker.enableFuzzy': {
'type': 'boolean',
'default': false,
'description': nls.localize('enableFuzzy', "Enable or disable fuzzy matching and sorting in the picker.")
}
}
});
@@ -14,6 +14,7 @@ import paths = require('vs/base/common/paths');
import filters = require('vs/base/common/filters');
import labels = require('vs/base/common/labels');
import {IRange} from 'vs/editor/common/editorCommon';
import {ListenerUnbind} from 'vs/base/common/eventEmitter';
import {IAutoFocus} from 'vs/base/parts/quickopen/browser/quickOpen';
import {QuickOpenEntry, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import {QuickOpenHandler} from 'vs/workbench/browser/quickopen';
@@ -21,8 +22,10 @@ import {FileEntry, OpenFileHandler} from 'vs/workbench/parts/search/browser/open
import {OpenSymbolHandler as _OpenSymbolHandler} from 'vs/workbench/parts/search/browser/openSymbolHandler';
import {IMessageService, Severity} from 'vs/platform/message/common/message';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {IWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService';
import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService';
import {ISearchConfiguration} from 'vs/platform/search/common/search';
import {IConfigurationService, IConfigurationServiceEvent, ConfigurationServiceEventTypes} from 'vs/platform/configuration/common/configuration';
// OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load
export const OpenSymbolHandler = _OpenSymbolHandler
@@ -42,12 +45,15 @@ export class OpenAnythingHandler extends QuickOpenHandler {
private delayer: ThrottledDelayer<QuickOpenModel>;
private pendingSearch: TPromise<QuickOpenModel>;
private isClosed: boolean;
private fuzzyMatchingEnabled: boolean;
private configurationListenerUnbind: ListenerUnbind;
constructor(
@IMessageService private messageService: IMessageService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IInstantiationService instantiationService: IInstantiationService,
@IQuickOpenService private quickOpenService: IQuickOpenService
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IConfigurationService private configurationService: IConfigurationService
) {
super();
@@ -60,6 +66,19 @@ export class OpenAnythingHandler extends QuickOpenHandler {
this.resultsToSearchCache = 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.search && configuration.search.fuzzyFilePicker;
this.openFileHandler.setFuzzyMatchingEnabled(this.fuzzyMatchingEnabled);
}
public getResults(searchValue: string): TPromise<QuickOpenModel> {
@@ -144,7 +163,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
let result = [...results[0].entries, ...results[1].entries];
// Sort
result.sort((elementA, elementB) => QuickOpenEntry.compare(elementA, elementB, searchValue, this.quickOpenService.isFuzzyMatchingEnabled()));
result.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue, this.fuzzyMatchingEnabled));
// Apply Range
result.forEach((element) => {
@@ -237,7 +256,6 @@ export class OpenAnythingHandler extends QuickOpenHandler {
// Pattern match on results and adjust highlights
let results: QuickOpenEntry[] = [];
const searchInPath = searchValue.indexOf(paths.nativeSep) >= 0;
const enableFuzzy = this.quickOpenService.isFuzzyMatchingEnabled();
for (let i = 0; i < cachedEntries.length; i++) {
let entry = cachedEntries[i];
@@ -248,19 +266,19 @@ export class OpenAnythingHandler extends QuickOpenHandler {
// Check if this entry is a match for the search value
let targetToMatch = searchInPath ? labels.getPathLabel(entry.getResource(), this.contextService) : entry.getLabel();
if (!filters.matchesFuzzy(searchValue, targetToMatch, enableFuzzy)) {
if (!filters.matchesFuzzy(searchValue, targetToMatch, this.fuzzyMatchingEnabled)) {
continue;
}
// Apply highlights
const {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, enableFuzzy);
const {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, this.fuzzyMatchingEnabled);
entry.setHighlights(labelHighlights, descriptionHighlights);
results.push(entry);
}
// Sort
results.sort((elementA, elementB) => QuickOpenEntry.compare(elementA, elementB, searchValue, enableFuzzy));
results.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue, this.fuzzyMatchingEnabled));
// Apply Range
results.forEach((element) => {
@@ -272,6 +290,28 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return results;
}
private sort(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, enableFuzzyScoring): number {
// Fuzzy scoring is special
if (enableFuzzyScoring) {
const labelAScore = strings.score(elementA.getLabel(), lookFor);
const labelBScore = strings.score(elementB.getLabel(), lookFor);
if (labelAScore !== labelBScore) {
return labelAScore > labelBScore ? -1 : 1;
}
const descriptionAScore = strings.score(elementA.getDescription(), lookFor);
const descriptionBScore = strings.score(elementB.getDescription(), lookFor);
if (descriptionAScore !== descriptionBScore) {
return descriptionAScore > descriptionBScore ? -1 : 1;
}
}
return QuickOpenEntry.compare(elementA, elementB, lookFor);
}
public getGroupLabel(): string {
return nls.localize('fileAndTypeResults', "file and symbol results");
}
@@ -90,6 +90,7 @@ export class OpenFileHandler extends QuickOpenHandler {
private queryBuilder: QueryBuilder;
private delayer: ThrottledDelayer<QuickOpenEntry[]>;
private isStandalone: boolean;
private fuzzyMatchingEnabled: boolean;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -113,6 +114,10 @@ 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,7 +140,7 @@ export class OpenFileHandler extends QuickOpenHandler {
rootResources.push(this.contextService.getWorkspace().resource);
}
let query: IQueryOptions = { filePattern: searchValue, matchFuzzy: this.quickOpenService.isFuzzyMatchingEnabled(), rootResources: rootResources };
let query: IQueryOptions = { filePattern: searchValue, matchFuzzy: this.fuzzyMatchingEnabled, rootResources: rootResources };
return this.queryBuilder.file(query).then((query) => this.searchService.search(query)).then((complete) => {
@@ -150,7 +155,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.quickOpenService.isFuzzyMatchingEnabled());
let {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, this.fuzzyMatchingEnabled);
entry.setHighlights(labelHighlights, descriptionHighlights);
results.push(entry);
@@ -158,7 +163,7 @@ export class OpenFileHandler extends QuickOpenHandler {
// Sort (standalone only)
if (this.isStandalone) {
results = results.sort((elementA, elementB) => QuickOpenEntry.compare(elementA, elementB, searchValue, this.quickOpenService.isFuzzyMatchingEnabled()));
results = results.sort((elementA, elementB) => QuickOpenEntry.compare(elementA, elementB, searchValue));
}
return results;
@@ -197,6 +197,11 @@ configurationRegistry.registerConfiguration({
}
]
}
},
'search.fuzzyFilePicker': {
'type': 'boolean',
'default': false,
'description': nls.localize('enableFuzzy', "Enable or disable fuzzy matching and sorting in the file picker.")
}
}
});
@@ -125,9 +125,4 @@ export interface IQuickOpenService {
* Allows to register on the event that quick open is hiding
*/
onHide: EventProvider<() => void>;
/**
* A boolean to indicate if fuzzy matching is enabled or not.
*/
isFuzzyMatchingEnabled(): boolean;
}
@@ -505,10 +505,6 @@ export class TestQuickOpenService implements QuickOpenService.IQuickOpenService
return null;
}
public isFuzzyMatchingEnabled(): boolean {
return false;
}
public removeEditorHistoryEntry(input: WorkbenchEditorCommon.EditorInput): void {}
public dispose() {}
public quickNavigate(): void {}