diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index 67a95a10c86..ac79d8d6c20 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import scorer = require('vs/base/common/scorer'); -import strings = require('vs/base/common/strings'); +import * as strings from 'vs/base/common/strings'; import * as paths from 'vs/base/common/paths'; let intlFileNameCollator: Intl.Collator; @@ -186,56 +185,4 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu } return 0; -} - -export interface IScorableResourceAccessor { - getLabel(t: T): string; - getResourcePath(t: T): string; -} - -export function compareByScore(elementA: T, elementB: T, accessor: IScorableResourceAccessor, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number { - const labelA = accessor.getLabel(elementA); - const labelB = accessor.getLabel(elementB); - - // 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, scorerCache); - const labelBScore = scorer.score(labelB, lookFor, scorerCache); - - if (labelAScore !== labelBScore) { - return labelAScore > labelBScore ? -1 : 1; - } - - // Score on full resource path comes next (if available) - let resourcePathA = accessor.getResourcePath(elementA); - let resourcePathB = accessor.getResourcePath(elementB); - if (resourcePathA && resourcePathB) { - const resourceAScore = scorer.score(resourcePathA, lookFor, scorerCache); - const resourceBScore = scorer.score(resourcePathB, lookFor, scorerCache); - - 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 (resourcePathA && resourcePathB && resourcePathA.length !== resourcePathB.length) { - return resourcePathA.length < resourcePathB.length ? -1 : 1; - } - - // Finally compare by label or resource path - if (labelA === labelB && resourcePathA && resourcePathB) { - return compareAnything(resourcePathA, resourcePathB, lookForNormalizedLower); - } - - return compareAnything(labelA, labelB, lookForNormalizedLower); -} +} \ No newline at end of file diff --git a/src/vs/base/common/scorer.ts b/src/vs/base/common/scorer.ts index 174900cf63c..deaa2d76037 100644 --- a/src/vs/base/common/scorer.ts +++ b/src/vs/base/common/scorer.ts @@ -5,6 +5,8 @@ 'use strict'; +import { compareByPrefix, compareAnything } from 'vs/base/common/comparers'; + // Based on material from: /*! BEGIN THIRD PARTY @@ -151,4 +153,56 @@ function isUpper(code: number): boolean { } /*! END THIRD PARTY -*/ \ No newline at end of file +*/ + +export interface IScorableResourceAccessor { + getResourceLabel(t: T): string; + getResourcePath(t: T): string; +} + +export function compareResourcesByScore(resourceA: T, resourceB: T, accessor: IScorableResourceAccessor, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number { + const resourceLabelA = accessor.getResourceLabel(resourceA); + const resourceLabelB = accessor.getResourceLabel(resourceB); + + // treat prefix matches highest in any case + const prefixCompare = compareByPrefix(resourceLabelA, resourceLabelB, lookFor); + if (prefixCompare) { + return prefixCompare; + } + + // Give higher importance to label score + const resourceLabelScoreA = score(resourceLabelA, lookFor, scorerCache); + const resourceLabelScoreB = score(resourceLabelB, lookFor, scorerCache); + + if (resourceLabelScoreA !== resourceLabelScoreB) { + return resourceLabelScoreA > resourceLabelScoreB ? -1 : 1; + } + + // Score on full resource path comes next (if available) + let resourcePathA = accessor.getResourcePath(resourceA); + let resourcePathB = accessor.getResourcePath(resourceB); + if (resourcePathA && resourcePathB) { + const resourceAScore = score(resourcePathA, lookFor, scorerCache); + const resourceBScore = score(resourcePathB, lookFor, scorerCache); + + 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 (resourceLabelA.length !== resourceLabelB.length) { + return resourceLabelA.length < resourceLabelB.length ? -1 : 1; + } + + if (resourcePathA && resourcePathB && resourcePathA.length !== resourcePathB.length) { + return resourcePathA.length < resourcePathB.length ? -1 : 1; + } + + // Finally compare by label or resource path + if (resourceLabelA === resourceLabelB && resourcePathA && resourcePathB) { + return compareAnything(resourcePathA, resourcePathB, lookForNormalizedLower); + } + + return compareAnything(resourceLabelA, resourceLabelB, lookForNormalizedLower); +} \ No newline at end of file diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 90f3d9d6133..d919f26fdf6 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -37,14 +37,15 @@ export interface IHighlight { let IDS = 0; -export class EntryAccessor { +export class ResourceAccessor { - public static getLabel(entry: QuickOpenEntry) { + public static getResourceLabel(entry: QuickOpenEntry) { return entry.getLabel(); } public static getResourcePath(entry: QuickOpenEntry) { const resource = entry.getResource(); + return resource && resource.fsPath; } } diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index e420d020b75..bb3640b1a71 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -12,7 +12,7 @@ import URI from 'vs/base/common/uri'; import errors = require('vs/base/common/errors'); import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; -import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, EntryAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, ResourceAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IModeService } from 'vs/editor/common/services/modeService'; import { getIconClasses } from 'vs/workbench/browser/labels'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -23,8 +23,8 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EditorInput, toResource, IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor'; -import { compareByScore } from 'vs/base/common/comparers'; import { stripWildcards, fuzzyContains } from 'vs/base/common/strings'; +import { compareResourcesByScore } from 'vs/base/common/scorer'; export class EditorPickerEntry extends QuickOpenEntryGroup { private stacks: IEditorStacksModel; @@ -137,7 +137,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler { return stacks.positionOfGroup(e1.group) - stacks.positionOfGroup(e2.group); } - return compareByScore(e1, e2, EntryAccessor, searchValue, normalizedSearchValueLowercase, this.scorerCache); + return compareResourcesByScore(e1, e2, ResourceAccessor, searchValue, normalizedSearchValueLowercase, this.scorerCache); }); } diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 20a8dd1c3c7..ab4f3536836 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -14,7 +14,7 @@ import types = require('vs/base/common/types'); import { isWindows } from 'vs/base/common/platform'; import strings = require('vs/base/common/strings'); import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; -import { QuickOpenEntry, QuickOpenModel, EntryAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenEntry, QuickOpenModel, ResourceAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; import { FileEntry, OpenFileHandler, FileQuickOpenModel } from 'vs/workbench/parts/search/browser/openFileHandler'; import * as openSymbolHandler from 'vs/workbench/parts/search/browser/openSymbolHandler'; @@ -24,10 +24,10 @@ import { ISearchStats, ICachedSearchStats, IUncachedSearchStats } from 'vs/platf import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchSearchConfiguration } from 'vs/workbench/parts/search/common/search'; -// OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load -export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; import { IRange } from 'vs/editor/common/core/range'; -import { compareByScore } from 'vs/base/common/comparers'; +import { compareResourcesByScore } from 'vs/base/common/scorer'; + +export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; // OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load const objects_assign: (destination: T, source: U) => T & U = objects.assign; @@ -216,7 +216,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { // Sort const unsortedResultTime = Date.now(); const normalizedSearchValue = strings.stripWildcards(searchValue).toLowerCase(); - const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareByScore(elementA, elementB, EntryAccessor, searchValue, normalizedSearchValue, this.scorerCache); + const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareResourcesByScore(elementA, elementB, ResourceAccessor, searchValue, normalizedSearchValue, this.scorerCache); const viewResults = arrays.top(mergedResults, compare, OpenAnythingHandler.MAX_DISPLAYED_RESULTS); const sortedResultTime = Date.now(); diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 11eb7e911a0..f0a5fca8fad 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -12,7 +12,6 @@ import gracefulFs = require('graceful-fs'); gracefulFs.gracefulify(fs); import arrays = require('vs/base/common/arrays'); -import { compareByScore } from 'vs/base/common/comparers'; import objects = require('vs/base/common/objects'); import strings = require('vs/base/common/strings'); import { PPromise, TPromise } from 'vs/base/common/winjs.base'; @@ -24,6 +23,7 @@ import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/text import { IRawSearchService, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchProgressItem, ISerializedSearchComplete, ISearchEngine, IFileSearchProgressItem, ITelemetryEvent } from './search'; import { ICachedSearchStats, IProgress } from 'vs/platform/search/common/search'; import { fuzzyContains } from 'vs/base/common/strings'; +import { compareResourcesByScore } from 'vs/base/common/scorer'; export class SearchService implements IRawSearchService { @@ -246,7 +246,7 @@ export class SearchService implements IRawSearchService { private sortResults(config: IRawSearch, results: IRawFileMatch[], scorerCache: ScorerCache): IRawFileMatch[] { const filePattern = config.filePattern; const normalizedSearchValue = strings.stripWildcards(filePattern).toLowerCase(); - const compare = (elementA: IRawFileMatch, elementB: IRawFileMatch) => compareByScore(elementA, elementB, FileMatchAccessor, filePattern, normalizedSearchValue, scorerCache); + const compare = (elementA: IRawFileMatch, elementB: IRawFileMatch) => compareResourcesByScore(elementA, elementB, FileMatchResourceAccessor, filePattern, normalizedSearchValue, scorerCache); return arrays.top(results, compare, config.maxResults); } @@ -409,9 +409,9 @@ interface ScorerCache { [key: string]: number; } -class FileMatchAccessor { +class FileMatchResourceAccessor { - public static getLabel(match: IRawFileMatch): string { + public static getResourceLabel(match: IRawFileMatch): string { return match.basename; }