提交 5c4e24f0 编写于 作者: B Benjamin Pasero

Merge pull request #1102 from Microsoft/ben/fuzzy-quickopen

Alternative fuzzy searching
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "string_scorer",
"version": "10 March 2015",
"license": "MIT License",
"repositoryURL": "https://github.com/joshaven/string_score",
"description": "The file scorer.ts was inspired by the string_score algorithm from Joshaven Potter."
}]
...@@ -251,12 +251,7 @@ const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContigu ...@@ -251,12 +251,7 @@ const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContigu
const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString); const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString);
const fuzzyRegExpCache: { [key: string]: RegExp; } = {}; const fuzzyRegExpCache: { [key: string]: RegExp; } = {};
let defaultFuzzyMatching = SubstringMatching.Contiguous; export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] {
export function setDefaultFuzzyMatching(matcher: SubstringMatching): void {
defaultFuzzyMatching = matcher;
}
export function matchesFuzzy(word: string, wordToMatchAgainst: string, matcher = defaultFuzzyMatching): IMatch[] {
// Form RegExp for wildcard matches // Form RegExp for wildcard matches
let regexp = fuzzyRegExpCache[word]; let regexp = fuzzyRegExpCache[word];
...@@ -272,5 +267,5 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, matcher = ...@@ -272,5 +267,5 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, matcher =
} }
// Default Filter // Default Filter
return matcher === SubstringMatching.Contiguous ? fuzzyContiguousFilter(word, wordToMatchAgainst) : fuzzySeparateFilter(word, wordToMatchAgainst); return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst);
} }
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// Based on material from:
/*!
* string_score.js: String Scoring Algorithm 0.1.22
*
* http://joshaven.com/string_score
* https://github.com/joshaven/string_score
*
* Copyright (C) 2009-2014 Joshaven Potter <yourtech@gmail.com>
* Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
* MIT License: http://opensource.org/licenses/MIT
*
* Date: Tue Mar 1 2011
* Updated: Tue Mar 10 2015
*/
/**
* Compute a score for the given string and the given query.
*
* Rules:
* Character score: 1
* Same case bonus: 1
* Upper case bonus: 1
* Start of word/path bonus: 7
* Start of string bonus: 8
*/
export function score(target: string, query: string): number {
let score = 0;
if (!target || !query) {
return score; // return early if target or query are undefined
}
const queryLen = query.length;
const targetLower = target.toLowerCase();
const queryLower = query.toLowerCase();
const wordPathBoundary = ['-', '_', ' ', '/', '\\'];
let index = 0;
while (index < queryLen) {
var indexOf = targetLower.indexOf(queryLower[index]);
if (indexOf < 0) {
index++;
continue; // no match
}
// Character Match Bonus
score += 1;
// Same Case Bonous
if (target[indexOf] === query[indexOf]) {
score += 1;
}
// Upper Case Bonus
if (isUpper(target.charCodeAt(indexOf))) {
score += 1;
}
// Prefix Bonus
if (indexOf === 0) {
score += 8;
}
// Start of Word/Path Bonous
if (wordPathBoundary.some(w => w === target[indexOf - 1])) {
score += 7;
}
index++;
}
return score;
}
function isUpper(code: number): boolean {
return 65 <= code && code <= 90;
}
\ No newline at end of file
...@@ -134,7 +134,8 @@ export class QuickOpenEntry { ...@@ -134,7 +134,8 @@ export class QuickOpenEntry {
} }
/** /**
* A good default sort implementation for quick open entries * A good default sort implementation for quick open entries respecting highlight information
* as well as associated resources.
*/ */
public static compare(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number { public static compare(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number {
...@@ -153,7 +154,7 @@ export class QuickOpenEntry { ...@@ -153,7 +154,7 @@ export class QuickOpenEntry {
return 1; return 1;
} }
// Sort by name/path // Fallback to the full path if labels are identical and we have associated resources
let nameA = elementA.getLabel(); let nameA = elementA.getLabel();
let nameB = elementB.getLabel(); let nameB = elementB.getLabel();
if (nameA === nameB) { if (nameA === nameB) {
...@@ -169,20 +170,24 @@ export class QuickOpenEntry { ...@@ -169,20 +170,24 @@ export class QuickOpenEntry {
return compareAnything(nameA, nameB, lookFor); return compareAnything(nameA, nameB, lookFor);
} }
public static highlight(entry: QuickOpenEntry, lookFor: string): { labelHighlights: IHighlight[], descriptionHighlights: IHighlight[] } { /**
* A good default highlight implementation for an entry with label and description.
*/
public static highlight(entry: QuickOpenEntry, lookFor: string, fuzzyHighlight = false): { labelHighlights: IHighlight[], descriptionHighlights: IHighlight[] } {
let labelHighlights: IHighlight[] = []; let labelHighlights: IHighlight[] = [];
let descriptionHighlights: IHighlight[] = []; let descriptionHighlights: IHighlight[] = [];
// Highlight file aware // Highlight file aware
if (entry.getResource()) { if (entry.getResource()) {
// Highlight only inside label // Fuzzy: Highlight in label and description
if (lookFor.indexOf(Paths.nativeSep) < 0) { if (fuzzyHighlight) {
labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel()); labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel(), true);
descriptionHighlights = Filters.matchesFuzzy(lookFor, entry.getDescription(), true);
} }
// Highlight in label and description // Path search: Highlight in label and description
else { else if (lookFor.indexOf(Paths.nativeSep) >= 0) {
descriptionHighlights = Filters.matchesFuzzy(Strings.trim(lookFor, Paths.nativeSep), entry.getDescription()); descriptionHighlights = Filters.matchesFuzzy(Strings.trim(lookFor, Paths.nativeSep), entry.getDescription());
// If we have no highlights, assume that the match is split among name and parent folder // If we have no highlights, assume that the match is split among name and parent folder
...@@ -191,6 +196,11 @@ export class QuickOpenEntry { ...@@ -191,6 +196,11 @@ export class QuickOpenEntry {
descriptionHighlights = Filters.matchesFuzzy(Strings.trim(Paths.dirname(lookFor), Paths.nativeSep), entry.getDescription()); descriptionHighlights = Filters.matchesFuzzy(Strings.trim(Paths.dirname(lookFor), Paths.nativeSep), entry.getDescription());
} }
} }
// Highlight only inside label
else {
labelHighlights = Filters.matchesFuzzy(lookFor, entry.getLabel());
}
} }
// Highlight by label otherwise // Highlight by label otherwise
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import scorer = require('vs/base/common/scorer');
suite('Scorer', () => {
test("score", function() {
const target = 'HelLo-World';
const scores = [];
scores.push(scorer.score(target, 'HelLo-World')); // direct case match
scores.push(scorer.score(target, 'hello-world')); // direct mix-case match
scores.push(scorer.score(target, 'HW')); // direct case prefix (multiple)
scores.push(scorer.score(target, 'H')); // direct case prefix
scores.push(scorer.score(target, 'hw')); // direct mix-case prefix (multiple)
scores.push(scorer.score(target, 'h')); // direct mix-case prefix
scores.push(scorer.score(target, 'W')); // direct case word prefix
scores.push(scorer.score(target, 'w')); // direct mix-case word prefix
scores.push(scorer.score(target, 'Ld')); // in-string case match (multiple)
scores.push(scorer.score(target, 'L')); // in-string case match
scores.push(scorer.score(target, 'ld')); // in-string mix-case match
scores.push(scorer.score(target, 'l')); // in-string mix-case match
scores.push(scorer.score(target, '4')); // no match
// Assert scoring order
let sortedScores = scores.sort();
assert.deepEqual(scores.reverse(), sortedScores);
});
});
\ No newline at end of file
...@@ -28,6 +28,7 @@ export interface IQueryOptions { ...@@ -28,6 +28,7 @@ export interface IQueryOptions {
includePattern?: glob.IExpression; includePattern?: glob.IExpression;
maxResults?: number; maxResults?: number;
fileEncoding?: string; fileEncoding?: string;
matchFuzzy?: boolean;
} }
export interface ISearchQuery extends IQueryOptions { export interface ISearchQuery extends IQueryOptions {
...@@ -91,5 +92,6 @@ export class LineMatch implements ILineMatch { ...@@ -91,5 +92,6 @@ export class LineMatch implements ILineMatch {
export interface ISearchConfiguration extends IFilesConfiguration { export interface ISearchConfiguration extends IFilesConfiguration {
search: { search: {
exclude: glob.IExpression; exclude: glob.IExpression;
fuzzyFilePicker: boolean;
}; };
} }
\ No newline at end of file
...@@ -38,7 +38,7 @@ import {IEventService} from 'vs/platform/event/common/event'; ...@@ -38,7 +38,7 @@ import {IEventService} from 'vs/platform/event/common/event';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IMessageService, Severity} from 'vs/platform/message/common/message'; import {IMessageService, Severity} from 'vs/platform/message/common/message';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {IWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService'; import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
const ID = 'workbench.component.quickopen'; const ID = 'workbench.component.quickopen';
......
...@@ -32,13 +32,14 @@ import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils'; ...@@ -32,13 +32,14 @@ import {KeybindingsUtils} from 'vs/platform/keybinding/common/keybindingsUtils';
import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService'; import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
const ACTION_ID = 'workbench.action.showCommands'; export const ALL_COMMANDS_PREFIX = '>';
const ACTION_LABEL = nls.localize('showTriggerActions', "Show All Commands"); export const EDITOR_COMMANDS_PREFIX = '$';
const ALL_COMMANDS_PREFIX = '>';
const EDITOR_COMMANDS_PREFIX = '$';
export class ShowAllCommandsAction extends QuickOpenAction { export class ShowAllCommandsAction extends QuickOpenAction {
public static ID = 'workbench.action.showCommands';
public static LABEL = nls.localize('showTriggerActions', "Show All Commands");
constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) { constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) {
super(actionId, actionLabel, ALL_COMMANDS_PREFIX, quickOpenService); super(actionId, actionLabel, ALL_COMMANDS_PREFIX, quickOpenService);
} }
...@@ -320,7 +321,6 @@ export class CommandsHandler extends QuickOpenHandler { ...@@ -320,7 +321,6 @@ export class CommandsHandler extends QuickOpenHandler {
public getEmptyLabel(searchString: string): string { public getEmptyLabel(searchString: string): string {
return nls.localize('noCommandsMatching', "No commands matching"); return nls.localize('noCommandsMatching', "No commands matching");
} }
} }
export class EditorCommandsHandler extends CommandsHandler { export class EditorCommandsHandler extends CommandsHandler {
...@@ -359,20 +359,3 @@ export class QuickCommandsEditorAction extends EditorAction { ...@@ -359,20 +359,3 @@ export class QuickCommandsEditorAction extends EditorAction {
return super.run(); return super.run();
} }
} }
\ No newline at end of file
// Register Action
let registry = <IWorkbenchActionRegistry>Registry.as(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllCommandsAction, ACTION_ID, ACTION_LABEL, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P,
secondary: [KeyCode.F1]
}));
// Register Quick Open Handler
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/commandsHandler',
'CommandsHandler',
ALL_COMMANDS_PREFIX,
nls.localize('commandsHandlerDescriptionDefault', "Show and Run Commands")
)
);
\ No newline at end of file
...@@ -24,11 +24,13 @@ import {Position} from 'vs/platform/editor/common/editor'; ...@@ -24,11 +24,13 @@ import {Position} from 'vs/platform/editor/common/editor';
import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService'; import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
const ACTION_ID = 'workbench.action.gotoLine'; export const GOTO_LINE_PREFIX = ':';
const ACTION_LABEL = nls.localize('gotoLine', "Go to Line...");
const GOTO_LINE_PREFIX = ':';
export class GotoLineAction extends QuickOpenAction { export class GotoLineAction extends QuickOpenAction {
public static ID = 'workbench.action.gotoLine';
public static LABEL = nls.localize('gotoLine', "Go to Line...");
constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) { constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) {
super(actionId, actionLabel, GOTO_LINE_PREFIX, quickOpenService); super(actionId, actionLabel, GOTO_LINE_PREFIX, quickOpenService);
} }
...@@ -282,26 +284,3 @@ export class GotoLineHandler extends QuickOpenHandler { ...@@ -282,26 +284,3 @@ export class GotoLineHandler extends QuickOpenHandler {
}; };
} }
} }
\ No newline at end of file
// Register Action
let registry = <IWorkbenchActionRegistry>Registry.as(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(GotoLineAction, ACTION_ID, ACTION_LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_G,
mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G }
}));
// Register Quick Open Handler
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/gotoLineHandler',
'GotoLineHandler',
GOTO_LINE_PREFIX,
[
{
prefix: GOTO_LINE_PREFIX,
needsEditor: true,
description: env.isMacintosh ? nls.localize('gotoLineDescriptionMac', "Go to Line") : nls.localize('gotoLineDescriptionWin', "Go to Line")
},
]
)
);
\ No newline at end of file
...@@ -33,13 +33,14 @@ import {Position} from 'vs/platform/editor/common/editor'; ...@@ -33,13 +33,14 @@ import {Position} from 'vs/platform/editor/common/editor';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {OutlineRegistry, getOutlineEntries} from 'vs/editor/contrib/quickOpen/common/quickOpen'; import {OutlineRegistry, getOutlineEntries} from 'vs/editor/contrib/quickOpen/common/quickOpen';
const ACTION_ID = 'workbench.action.gotoSymbol'; export const GOTO_SYMBOL_PREFIX = '@';
const ACTION_LABEL = nls.localize('gotoSymbol', "Go to Symbol..."); export const SCOPE_PREFIX = ':';
const GOTO_SYMBOL_PREFIX = '@';
const SCOPE_PREFIX = ':';
export class GotoSymbolAction extends QuickOpenAction { export class GotoSymbolAction extends QuickOpenAction {
public static ID = 'workbench.action.gotoSymbol';
public static LABEL = nls.localize('gotoSymbol', "Go to Symbol...");
constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) { constructor(actionId: string, actionLabel: string, @IQuickOpenService quickOpenService: IQuickOpenService) {
super(actionId, actionLabel, GOTO_SYMBOL_PREFIX, quickOpenService); super(actionId, actionLabel, GOTO_SYMBOL_PREFIX, quickOpenService);
} }
...@@ -432,7 +433,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { ...@@ -432,7 +433,7 @@ export class GotoSymbolHandler extends QuickOpenHandler {
if (model && types.isFunction((<ITokenizedModel>model).getMode)) { if (model && types.isFunction((<ITokenizedModel>model).getMode)) {
canRun = OutlineRegistry.has(<IModel> model); canRun = OutlineRegistry.has(<IModel>model);
} }
} }
...@@ -527,7 +528,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { ...@@ -527,7 +528,7 @@ export class GotoSymbolHandler extends QuickOpenHandler {
return TPromise.as(this.outlineToModelCache[modelId]); return TPromise.as(this.outlineToModelCache[modelId]);
} }
return getOutlineEntries(<IModel> model).then(outline => { return getOutlineEntries(<IModel>model).then(outline => {
let model = new OutlineModel(outline, this.toQuickOpenEntries(outline)); let model = new OutlineModel(outline, this.toQuickOpenEntries(outline));
...@@ -628,28 +629,3 @@ export class GotoSymbolHandler extends QuickOpenHandler { ...@@ -628,28 +629,3 @@ export class GotoSymbolHandler extends QuickOpenHandler {
this.activeOutlineRequest = null; this.activeOutlineRequest = null;
} }
} }
\ No newline at end of file
// Register Action
let registry = <IWorkbenchActionRegistry>Registry.as(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, ACTION_ID, ACTION_LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O }));
// Register Quick Outline Handler
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/gotoSymbolHandler',
'GotoSymbolHandler',
GOTO_SYMBOL_PREFIX,
[
{
prefix: GOTO_SYMBOL_PREFIX,
needsEditor: true,
description: env.isMacintosh ? nls.localize('gotoSymbolDescriptionNormalMac', "Go to Symbol") : nls.localize('gotoSymbolDescriptionNormalWin', "Go to Symbol")
},
{
prefix: GOTO_SYMBOL_PREFIX + SCOPE_PREFIX,
needsEditor: true,
description: nls.localize('gotoSymbolDescriptionScoped', "Go to Symbol by Category")
}
]
)
);
\ No newline at end of file
...@@ -15,7 +15,7 @@ import {ITree, IElementCallback} from 'vs/base/parts/tree/common/tree'; ...@@ -15,7 +15,7 @@ import {ITree, IElementCallback} from 'vs/base/parts/tree/common/tree';
import {QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, QuickOpenHandler} from 'vs/workbench/browser/quickopen'; import {QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, QuickOpenHandler} from 'vs/workbench/browser/quickopen';
import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService'; import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService';
const HELP_PREFIX = '?'; export const HELP_PREFIX = '?';
class HelpEntry extends QuickOpenEntryItem { class HelpEntry extends QuickOpenEntryItem {
private prefix: string; private prefix: string;
...@@ -183,13 +183,3 @@ export class HelpHandler extends QuickOpenHandler { ...@@ -183,13 +183,3 @@ export class HelpHandler extends QuickOpenHandler {
}; };
} }
} }
\ No newline at end of file
// Register Quick Open Handler
(<IQuickOpenRegistry>Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/helpHandler',
'HelpHandler',
HELP_PREFIX,
nls.localize('helpDescription', "Show Help")
)
);
\ No newline at end of file
...@@ -67,9 +67,8 @@ class MarkerEntry extends QuickOpenEntryItem { ...@@ -67,9 +67,8 @@ class MarkerEntry extends QuickOpenEntryItem {
return null; return null;
} }
public run(mode:Mode, context:IContext):boolean { public run(mode: Mode, context: IContext): boolean {
if (mode !== Mode.OPEN) {
if(mode !== Mode.OPEN) {
return false; return false;
} }
...@@ -107,7 +106,7 @@ export class MarkersHandler extends QuickOpenHandler { ...@@ -107,7 +106,7 @@ export class MarkersHandler extends QuickOpenHandler {
this._contextService = contextService; this._contextService = contextService;
} }
public getResults(searchValue:string):TPromise<QuickOpenModel> { public getResults(searchValue: string): TPromise<QuickOpenModel> {
searchValue = searchValue.trim(); searchValue = searchValue.trim();
let markers = this._markerService.read({ take: 500 }); let markers = this._markerService.read({ take: 500 });
...@@ -121,7 +120,6 @@ export class MarkersHandler extends QuickOpenHandler { ...@@ -121,7 +120,6 @@ export class MarkersHandler extends QuickOpenHandler {
} }
private static _sort(a: IMarker, b: IMarker): number { private static _sort(a: IMarker, b: IMarker): number {
let ret: number; let ret: number;
// 1st: severity matters first // 1st: severity matters first
...@@ -144,7 +142,7 @@ export class MarkersHandler extends QuickOpenHandler { ...@@ -144,7 +142,7 @@ export class MarkersHandler extends QuickOpenHandler {
// 4th: start column matters // 4th: start column matters
ret = a.startColumn - b.startColumn; ret = a.startColumn - b.startColumn;
if(ret !== 0) { if (ret !== 0) {
return ret; return ret;
} }
...@@ -153,7 +151,7 @@ export class MarkersHandler extends QuickOpenHandler { ...@@ -153,7 +151,7 @@ export class MarkersHandler extends QuickOpenHandler {
private _filter(marker: IMarker, query: string): boolean { private _filter(marker: IMarker, query: string): boolean {
if(marker.resource.scheme === network.schemas.inMemory) { if (marker.resource.scheme === network.schemas.inMemory) {
// ignore inmemory-models // ignore inmemory-models
return false; return false;
} }
...@@ -172,7 +170,7 @@ export class MarkersHandler extends QuickOpenHandler { ...@@ -172,7 +170,7 @@ export class MarkersHandler extends QuickOpenHandler {
return 'marker-handler'; return 'marker-handler';
} }
public getAutoFocus(searchValue:string):IAutoFocus { public getAutoFocus(searchValue: string): IAutoFocus {
return { return {
autoFocusFirstEntry: !!searchValue autoFocusFirstEntry: !!searchValue
}; };
...@@ -187,7 +185,7 @@ export class MarkersHandler extends QuickOpenHandler { ...@@ -187,7 +185,7 @@ export class MarkersHandler extends QuickOpenHandler {
} }
class GotoMarkerAction extends QuickOpenAction { export class GotoMarkerAction extends QuickOpenAction {
static Prefix = '!'; static Prefix = '!';
static Id = 'workbench.action.showErrorsWarnings'; static Id = 'workbench.action.showErrorsWarnings';
...@@ -197,23 +195,3 @@ class GotoMarkerAction extends QuickOpenAction { ...@@ -197,23 +195,3 @@ class GotoMarkerAction extends QuickOpenAction {
super(actionId, actionLabel, GotoMarkerAction.Prefix, quickOpenService); super(actionId, actionLabel, GotoMarkerAction.Prefix, quickOpenService);
} }
} }
\ No newline at end of file
// Register Action
let registry = <IWorkbenchActionRegistry> Registry.as(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(GotoMarkerAction, GotoMarkerAction.Id, GotoMarkerAction.Label, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M }));
// Register Quick Open Handler
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/markersHandler',
'MarkersHandler',
GotoMarkerAction.Prefix,
[
{
prefix: GotoMarkerAction.Prefix,
needsEditor: false,
description: env.isMacintosh ? nls.localize('desc.mac', "Show Errors or Warnings") : nls.localize('desc.win', "Show Errors and Warnings")
},
]
)
);
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import env = require('vs/base/common/platform');
import nls = require('vs/nls');
import {QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandler} from 'vs/workbench/browser/quickopen';
import {Registry} from 'vs/platform/platform';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/browser/actionRegistry';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {IConfigurationRegistry, Extensions as ConfigurationExtensions} from 'vs/platform/configuration/common/configurationRegistry';
import {GotoSymbolHandler, GotoSymbolAction, GOTO_SYMBOL_PREFIX, SCOPE_PREFIX} from 'vs/workbench/parts/quickopen/browser/gotoSymbolHandler';
import {CommandsHandler, ShowAllCommandsAction, ALL_COMMANDS_PREFIX} from 'vs/workbench/parts/quickopen/browser/commandsHandler';
import {GotoLineAction, GotoLineHandler, GOTO_LINE_PREFIX} from 'vs/workbench/parts/quickopen/browser/gotoLineHandler';
import {HelpHandler, HELP_PREFIX} from 'vs/workbench/parts/quickopen/browser/helpHandler';
import {MarkersHandler, GotoMarkerAction} from 'vs/workbench/parts/quickopen/browser/markersHandler';
// Register Actions
let registry = <IWorkbenchActionRegistry>Registry.as(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(GotoMarkerAction, GotoMarkerAction.Id, GotoMarkerAction.Label, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M
}));
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllCommandsAction, ShowAllCommandsAction.ID, ShowAllCommandsAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P,
secondary: [KeyCode.F1]
}));
registry.registerWorkbenchAction(new SyncActionDescriptor(GotoLineAction, GotoLineAction.ID, GotoLineAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_G,
mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G }
}));
registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, GotoSymbolAction.ID, GotoSymbolAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O
}));
// Register Quick Open Handler
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/markersHandler',
'MarkersHandler',
GotoMarkerAction.Prefix,
[
{
prefix: GotoMarkerAction.Prefix,
needsEditor: false,
description: env.isMacintosh ? nls.localize('desc.mac', "Show Errors or Warnings") : nls.localize('desc.win', "Show Errors and Warnings")
},
]
)
);
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/commandsHandler',
'CommandsHandler',
ALL_COMMANDS_PREFIX,
nls.localize('commandsHandlerDescriptionDefault', "Show and Run Commands")
)
);
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/gotoLineHandler',
'GotoLineHandler',
GOTO_LINE_PREFIX,
[
{
prefix: GOTO_LINE_PREFIX,
needsEditor: true,
description: env.isMacintosh ? nls.localize('gotoLineDescriptionMac', "Go to Line") : nls.localize('gotoLineDescriptionWin', "Go to Line")
},
]
)
);
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/gotoSymbolHandler',
'GotoSymbolHandler',
GOTO_SYMBOL_PREFIX,
[
{
prefix: GOTO_SYMBOL_PREFIX,
needsEditor: true,
description: env.isMacintosh ? nls.localize('gotoSymbolDescriptionNormalMac', "Go to Symbol") : nls.localize('gotoSymbolDescriptionNormalWin', "Go to Symbol")
},
{
prefix: GOTO_SYMBOL_PREFIX + SCOPE_PREFIX,
needsEditor: true,
description: nls.localize('gotoSymbolDescriptionScoped', "Go to Symbol by Category")
}
]
)
);
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/quickopen/browser/helpHandler',
'HelpHandler',
HELP_PREFIX,
nls.localize('helpDescription', "Show Help")
)
);
\ No newline at end of file
...@@ -10,11 +10,12 @@ import nls = require('vs/nls'); ...@@ -10,11 +10,12 @@ import nls = require('vs/nls');
import {ThrottledDelayer} from 'vs/base/common/async'; import {ThrottledDelayer} from 'vs/base/common/async';
import types = require('vs/base/common/types'); import types = require('vs/base/common/types');
import strings = require('vs/base/common/strings'); import strings = require('vs/base/common/strings');
import scorer = require('vs/base/common/scorer');
import paths = require('vs/base/common/paths'); import paths = require('vs/base/common/paths');
import filters = require('vs/base/common/filters'); import filters = require('vs/base/common/filters');
import labels = require('vs/base/common/labels'); import labels = require('vs/base/common/labels');
import {IRange} from 'vs/editor/common/editorCommon'; import {IRange} from 'vs/editor/common/editorCommon';
import {compareAnything} from 'vs/base/common/comparers'; import {ListenerUnbind} from 'vs/base/common/eventEmitter';
import {IAutoFocus} from 'vs/base/parts/quickopen/browser/quickOpen'; import {IAutoFocus} from 'vs/base/parts/quickopen/browser/quickOpen';
import {QuickOpenEntry, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel'; import {QuickOpenEntry, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import {QuickOpenHandler} from 'vs/workbench/browser/quickopen'; import {QuickOpenHandler} from 'vs/workbench/browser/quickopen';
...@@ -22,7 +23,9 @@ import {FileEntry, OpenFileHandler} from 'vs/workbench/parts/search/browser/open ...@@ -22,7 +23,9 @@ import {FileEntry, OpenFileHandler} from 'vs/workbench/parts/search/browser/open
import {OpenSymbolHandler as _OpenSymbolHandler} from 'vs/workbench/parts/search/browser/openSymbolHandler'; import {OpenSymbolHandler as _OpenSymbolHandler} from 'vs/workbench/parts/search/browser/openSymbolHandler';
import {IMessageService, Severity} from 'vs/platform/message/common/message'; import {IMessageService, Severity} from 'vs/platform/message/common/message';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; 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 {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 // OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load
export const OpenSymbolHandler = _OpenSymbolHandler export const OpenSymbolHandler = _OpenSymbolHandler
...@@ -42,11 +45,14 @@ export class OpenAnythingHandler extends QuickOpenHandler { ...@@ -42,11 +45,14 @@ export class OpenAnythingHandler extends QuickOpenHandler {
private delayer: ThrottledDelayer<QuickOpenModel>; private delayer: ThrottledDelayer<QuickOpenModel>;
private pendingSearch: TPromise<QuickOpenModel>; private pendingSearch: TPromise<QuickOpenModel>;
private isClosed: boolean; private isClosed: boolean;
private fuzzyMatchingEnabled: boolean;
private configurationListenerUnbind: ListenerUnbind;
constructor( constructor(
@IMessageService private messageService: IMessageService, @IMessageService private messageService: IMessageService,
@IWorkspaceContextService private contextService: IWorkspaceContextService, @IWorkspaceContextService private contextService: IWorkspaceContextService,
@IInstantiationService instantiationService: IInstantiationService @IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService
) { ) {
super(); super();
...@@ -59,6 +65,19 @@ export class OpenAnythingHandler extends QuickOpenHandler { ...@@ -59,6 +65,19 @@ export class OpenAnythingHandler extends QuickOpenHandler {
this.resultsToSearchCache = Object.create(null); this.resultsToSearchCache = Object.create(null);
this.delayer = new ThrottledDelayer<QuickOpenModel>(OpenAnythingHandler.SEARCH_DELAY); 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> { public getResults(searchValue: string): TPromise<QuickOpenModel> {
...@@ -143,7 +162,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { ...@@ -143,7 +162,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
let result = [...results[0].entries, ...results[1].entries]; let result = [...results[0].entries, ...results[1].entries];
// Sort // Sort
result.sort((elementA, elementB) => QuickOpenEntry.compare(elementA, elementB, searchValue)); result.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue, this.fuzzyMatchingEnabled));
// Apply Range // Apply Range
result.forEach((element) => { result.forEach((element) => {
...@@ -246,19 +265,19 @@ export class OpenAnythingHandler extends QuickOpenHandler { ...@@ -246,19 +265,19 @@ export class OpenAnythingHandler extends QuickOpenHandler {
// Check if this entry is a match for the search value // Check if this entry is a match for the search value
let targetToMatch = searchInPath ? labels.getPathLabel(entry.getResource(), this.contextService) : entry.getLabel(); let targetToMatch = searchInPath ? labels.getPathLabel(entry.getResource(), this.contextService) : entry.getLabel();
if (!filters.matchesFuzzy(searchValue, targetToMatch)) { if (!filters.matchesFuzzy(searchValue, targetToMatch, this.fuzzyMatchingEnabled)) {
continue; continue;
} }
// Apply highlights // Apply highlights
const {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue); const {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, this.fuzzyMatchingEnabled);
entry.setHighlights(labelHighlights, descriptionHighlights); entry.setHighlights(labelHighlights, descriptionHighlights);
results.push(entry); results.push(entry);
} }
// Sort // Sort
results.sort((elementA, elementB) => QuickOpenEntry.compare(elementA, elementB, searchValue)); results.sort((elementA, elementB) => this.sort(elementA, elementB, searchValue, this.fuzzyMatchingEnabled));
// Apply Range // Apply Range
results.forEach((element) => { results.forEach((element) => {
...@@ -270,6 +289,28 @@ export class OpenAnythingHandler extends QuickOpenHandler { ...@@ -270,6 +289,28 @@ export class OpenAnythingHandler extends QuickOpenHandler {
return results; return results;
} }
private sort(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string, enableFuzzyScoring): number {
// Fuzzy scoring is special
if (enableFuzzyScoring) {
const labelAScore = scorer.score(elementA.getLabel(), lookFor);
const labelBScore = scorer.score(elementB.getLabel(), lookFor);
if (labelAScore !== labelBScore) {
return labelAScore > labelBScore ? -1 : 1;
}
const descriptionAScore = scorer.score(elementA.getDescription(), lookFor);
const descriptionBScore = scorer.score(elementB.getDescription(), lookFor);
if (descriptionAScore !== descriptionBScore) {
return descriptionAScore > descriptionBScore ? -1 : 1;
}
}
return QuickOpenEntry.compare(elementA, elementB, lookFor);
}
public getGroupLabel(): string { public getGroupLabel(): string {
return nls.localize('fileAndTypeResults', "file and symbol results"); return nls.localize('fileAndTypeResults', "file and symbol results");
} }
......
...@@ -89,6 +89,7 @@ export class OpenFileHandler extends QuickOpenHandler { ...@@ -89,6 +89,7 @@ export class OpenFileHandler extends QuickOpenHandler {
private queryBuilder: QueryBuilder; private queryBuilder: QueryBuilder;
private delayer: ThrottledDelayer<QuickOpenEntry[]>; private delayer: ThrottledDelayer<QuickOpenEntry[]>;
private isStandalone: boolean; private isStandalone: boolean;
private fuzzyMatchingEnabled: boolean;
constructor( constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService,
...@@ -111,6 +112,10 @@ export class OpenFileHandler extends QuickOpenHandler { ...@@ -111,6 +112,10 @@ export class OpenFileHandler extends QuickOpenHandler {
this.isStandalone = standalone; this.isStandalone = standalone;
} }
public setFuzzyMatchingEnabled(enabled: boolean): void {
this.fuzzyMatchingEnabled = enabled;
}
public getResults(searchValue: string): TPromise<QuickOpenModel> { public getResults(searchValue: string): TPromise<QuickOpenModel> {
searchValue = searchValue.trim(); searchValue = searchValue.trim();
let promise: TPromise<QuickOpenEntry[]>; let promise: TPromise<QuickOpenEntry[]>;
...@@ -133,7 +138,7 @@ export class OpenFileHandler extends QuickOpenHandler { ...@@ -133,7 +138,7 @@ export class OpenFileHandler extends QuickOpenHandler {
rootResources.push(this.contextService.getWorkspace().resource); rootResources.push(this.contextService.getWorkspace().resource);
} }
let query: IQueryOptions = { filePattern: searchValue, 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) => { return this.queryBuilder.file(query).then((query) => this.searchService.search(query)).then((complete) => {
...@@ -148,7 +153,7 @@ export class OpenFileHandler extends QuickOpenHandler { ...@@ -148,7 +153,7 @@ export class OpenFileHandler extends QuickOpenHandler {
let entry = this.instantiationService.createInstance(FileEntry, label, description, fileMatch.resource); let entry = this.instantiationService.createInstance(FileEntry, label, description, fileMatch.resource);
// Apply highlights // Apply highlights
let {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue); let {labelHighlights, descriptionHighlights} = QuickOpenEntry.highlight(entry, searchValue, this.fuzzyMatchingEnabled);
entry.setHighlights(labelHighlights, descriptionHighlights); entry.setHighlights(labelHighlights, descriptionHighlights);
results.push(entry); results.push(entry);
......
...@@ -192,23 +192,19 @@ export class OpenSymbolHandler extends QuickOpenHandler { ...@@ -192,23 +192,19 @@ export class OpenSymbolHandler extends QuickOpenHandler {
} }
private sort(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { private sort(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number {
let elementAName = elementA.getName().toLowerCase();
let elementBName = elementB.getName().toLowerCase();
// Compare by name
let r = strings.localeCompare(elementAName, elementBName);
if (r !== 0) {
return r;
}
// Sort by Type if name is identical // Sort by Type if name is identical
let elementAName = elementA.getName().toLowerCase();
let elementBName = elementB.getName().toLowerCase();
if (elementAName === elementBName) {
let elementAType = elementA.getType(); let elementAType = elementA.getType();
let elementBType = elementB.getType(); let elementBType = elementB.getType();
if (elementAType !== elementBType) { if (elementAType !== elementBType) {
return OpenSymbolHandler.SUPPORTED_OPEN_TYPES.indexOf(elementAType) < OpenSymbolHandler.SUPPORTED_OPEN_TYPES.indexOf(elementBType) ? -1 : 1; return OpenSymbolHandler.SUPPORTED_OPEN_TYPES.indexOf(elementAType) < OpenSymbolHandler.SUPPORTED_OPEN_TYPES.indexOf(elementBType) ? -1 : 1;
} }
}
return 0; // Keep default sorting order otherwise return QuickOpenEntry.compare(elementA, elementB, searchValue);
} }
public getGroupLabel(): string { public getGroupLabel(): string {
......
...@@ -197,6 +197,11 @@ configurationRegistry.registerConfiguration({ ...@@ -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.")
} }
} }
}); });
\ No newline at end of file
...@@ -63,7 +63,8 @@ export class QueryBuilder { ...@@ -63,7 +63,8 @@ export class QueryBuilder {
includePattern: options.includePattern, includePattern: options.includePattern,
maxResults: options.maxResults, maxResults: options.maxResults,
fileEncoding: options.fileEncoding, fileEncoding: options.fileEncoding,
contentPattern: contentPattern contentPattern: contentPattern,
matchFuzzy: options.matchFuzzy
}; };
}); });
} }
......
...@@ -32,12 +32,14 @@ export class FileWalker { ...@@ -32,12 +32,14 @@ export class FileWalker {
private resultCount: number; private resultCount: number;
private isCanceled: boolean; private isCanceled: boolean;
private searchInPath: boolean; private searchInPath: boolean;
private matchFuzzy: boolean;
private walkedPaths: { [path: string]: boolean; }; private walkedPaths: { [path: string]: boolean; };
constructor(config: IRawSearch) { constructor(config: IRawSearch) {
this.config = config; this.config = config;
this.filePattern = config.filePattern; this.filePattern = config.filePattern;
this.matchFuzzy = config.matchFuzzy;
this.excludePattern = config.excludePattern; this.excludePattern = config.excludePattern;
this.includePattern = config.includePattern; this.includePattern = config.includePattern;
this.maxResults = config.maxResults || null; this.maxResults = config.maxResults || null;
...@@ -194,7 +196,7 @@ export class FileWalker { ...@@ -194,7 +196,7 @@ export class FileWalker {
// Check for search pattern // Check for search pattern
if (this.filePattern) { if (this.filePattern) {
const res = filters.matchesFuzzy(this.filePattern, this.searchInPath ? path : name); const res = filters.matchesFuzzy(this.filePattern, this.matchFuzzy || this.searchInPath ? path : name, this.matchFuzzy);
return !!res && res.length > 0; return !!res && res.length > 0;
} }
......
...@@ -19,6 +19,7 @@ import {Engine as TextSearchEngine} from 'vs/workbench/services/search/node/text ...@@ -19,6 +19,7 @@ import {Engine as TextSearchEngine} from 'vs/workbench/services/search/node/text
export interface IRawSearch { export interface IRawSearch {
rootPaths: string[]; rootPaths: string[];
filePattern?: string; filePattern?: string;
matchFuzzy?: boolean;
excludePattern?: glob.IExpression; excludePattern?: glob.IExpression;
includePattern?: glob.IExpression; includePattern?: glob.IExpression;
contentPattern?: IPatternInfo; contentPattern?: IPatternInfo;
......
...@@ -223,7 +223,8 @@ class DiskSearch { ...@@ -223,7 +223,8 @@ class DiskSearch {
filePattern: query.filePattern, filePattern: query.filePattern,
excludePattern: query.excludePattern, excludePattern: query.excludePattern,
includePattern: query.includePattern, includePattern: query.includePattern,
maxResults: query.maxResults maxResults: query.maxResults,
matchFuzzy: query.matchFuzzy
}; };
if (query.type === QueryType.Text) { if (query.type === QueryType.Text) {
......
...@@ -66,6 +66,25 @@ suite('Search', () => { ...@@ -66,6 +66,25 @@ suite('Search', () => {
}); });
}); });
test('Files: examples (fuzzy)', function(done: () => void) {
let engine = new FileSearchEngine({
rootPaths: [require.toUrl('./fixtures')],
filePattern: 'xl',
matchFuzzy: true
});
let count = 0;
engine.search((result) => {
if (result) {
count++;
}
}, () => { }, (error) => {
assert.ok(!error);
assert.equal(count, 5);
done();
});
});
test('Files: *.js (Files as roots)', function(done: () => void) { test('Files: *.js (Files as roots)', function(done: () => void) {
let engine = new FileSearchEngine({ let engine = new FileSearchEngine({
rootPaths: [require.toUrl('./fixtures/examples/company.js'), require.toUrl('./fixtures/examples/small.js')], rootPaths: [require.toUrl('./fixtures/examples/company.js'), require.toUrl('./fixtures/examples/small.js')],
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
'use strict'; 'use strict';
import * as assert from 'assert'; import * as assert from 'assert';
import {TestKeybindingService, TestContextService, TestStorageService, TestEventService, TestEditorService, TestQuickOpenService} from 'vs/workbench/test/browser/servicesTestUtils'; import {TestKeybindingService, TestConfigurationService, TestContextService, TestStorageService, TestEventService, TestEditorService, TestQuickOpenService} from 'vs/workbench/test/browser/servicesTestUtils';
import {Registry} from 'vs/platform/platform'; import {Registry} from 'vs/platform/platform';
import {EditorHistoryModel, EditorHistoryEntry} from 'vs/workbench/browser/parts/quickopen/editorHistoryModel'; import {EditorHistoryModel, EditorHistoryEntry} from 'vs/workbench/browser/parts/quickopen/editorHistoryModel';
import {QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions} from 'vs/workbench/browser/quickopen'; import {QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions} from 'vs/workbench/browser/quickopen';
......
...@@ -27,6 +27,7 @@ import Severity from 'vs/base/common/severity'; ...@@ -27,6 +27,7 @@ import Severity from 'vs/base/common/severity';
import Arrays = require('vs/base/common/arrays'); import Arrays = require('vs/base/common/arrays');
import Errors = require('vs/base/common/errors'); import Errors = require('vs/base/common/errors');
import http = require('vs/base/common/http'); import http = require('vs/base/common/http');
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage'; import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
import UntitledEditorService = require('vs/workbench/services/untitled/browser/untitledEditorService'); import UntitledEditorService = require('vs/workbench/services/untitled/browser/untitledEditorService');
import WorkbenchEditorService = require('vs/workbench/services/editor/common/editorService'); import WorkbenchEditorService = require('vs/workbench/services/editor/common/editorService');
...@@ -71,7 +72,11 @@ export class TestContextService implements WorkspaceContextService.IWorkspaceCon ...@@ -71,7 +72,11 @@ export class TestContextService implements WorkspaceContextService.IWorkspaceCon
constructor(workspace: any = TestWorkspace, configuration: any = TestConfiguration, options: any = null) { constructor(workspace: any = TestWorkspace, configuration: any = TestConfiguration, options: any = null) {
this.workspace = workspace; this.workspace = workspace;
this.configuration = configuration; this.configuration = configuration;
this.options = options; this.options = options || {
globalSettings: {
settings: {}
}
};
} }
public getWorkspace(): IWorkspace { public getWorkspace(): IWorkspace {
...@@ -466,7 +471,7 @@ export class TestQuickOpenService implements QuickOpenService.IQuickOpenService ...@@ -466,7 +471,7 @@ export class TestQuickOpenService implements QuickOpenService.IQuickOpenService
private callback: (prefix: string) => void; private callback: (prefix: string) => void;
constructor(callback: (prefix: string) => void) { constructor(callback?: (prefix: string) => void) {
this.callback = callback; this.callback = callback;
} }
...@@ -483,7 +488,7 @@ export class TestQuickOpenService implements QuickOpenService.IQuickOpenService ...@@ -483,7 +488,7 @@ export class TestQuickOpenService implements QuickOpenService.IQuickOpenService
} }
show(prefix?: string, quickNavigateConfiguration?: any): Promise { show(prefix?: string, quickNavigateConfiguration?: any): Promise {
this.callback(prefix); this.callback && this.callback(prefix);
return Promise.as(true); return Promise.as(true);
} }
...@@ -531,3 +536,15 @@ export const TestFileService = { ...@@ -531,3 +536,15 @@ export const TestFileService = {
}); });
} }
} }
export class TestConfigurationService extends EventEmitter.EventEmitter implements IConfigurationService {
public serviceId = IConfigurationService;
public loadConfiguration(section?:string):TPromise<any> {
return TPromise.as({});
}
public hasWorkspaceConfiguration():boolean {
return false;
}
}
\ No newline at end of file
...@@ -32,11 +32,7 @@ define([ ...@@ -32,11 +32,7 @@ define([
'vs/workbench/browser/actions/showPerformanceBox', 'vs/workbench/browser/actions/showPerformanceBox',
'vs/workbench/browser/actions/openSettings', 'vs/workbench/browser/actions/openSettings',
'vs/workbench/parts/quickopen/browser/gotoSymbolHandler', 'vs/workbench/parts/quickopen/browser/quickopen.contribution',
'vs/workbench/parts/quickopen/browser/commandsHandler',
'vs/workbench/parts/quickopen/browser/gotoLineHandler',
'vs/workbench/parts/quickopen/browser/helpHandler',
'vs/workbench/parts/quickopen/browser/markersHandler',
'vs/workbench/parts/files/browser/explorerViewlet', 'vs/workbench/parts/files/browser/explorerViewlet',
'vs/workbench/parts/files/browser/fileActions.contribution', 'vs/workbench/parts/files/browser/fileActions.contribution',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册