mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
@@ -4,7 +4,7 @@
|
||||
"version": "0.1.20",
|
||||
"license": "MIT",
|
||||
"repositoryURL": "https://github.com/joshaven/string_score",
|
||||
"description": "The file scorer.ts was inspired by the string_score algorithm from Joshaven Potter.",
|
||||
"description": "The file quickOpenScorer.ts was inspired by the string_score algorithm from Joshaven Potter.",
|
||||
"licenseDetail": [
|
||||
"This software is released under the MIT license:",
|
||||
"",
|
||||
|
||||
@@ -122,7 +122,7 @@ function isLower(code: number): boolean {
|
||||
return CharCode.a <= code && code <= CharCode.z;
|
||||
}
|
||||
|
||||
function isUpper(code: number): boolean {
|
||||
export function isUpper(code: number): boolean {
|
||||
return CharCode.A <= code && code <= CharCode.Z;
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ function printTable(table: number[][], pattern: string, patternLen: number, word
|
||||
return ret;
|
||||
}
|
||||
|
||||
function isSeparatorAtPos(value: string, index: number): boolean {
|
||||
export function isSeparatorAtPos(value: string, index: number): boolean {
|
||||
if (index < 0 || index >= value.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,6 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import types = require('vs/base/common/types');
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import filters = require('vs/base/common/filters');
|
||||
import strings = require('vs/base/common/strings');
|
||||
import paths = require('vs/base/common/paths');
|
||||
import { IconLabel, IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
@@ -24,7 +21,7 @@ import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidge
|
||||
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IItemAccessor } from 'vs/base/common/scorer';
|
||||
import { IItemAccessor } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
|
||||
export interface IContext {
|
||||
event: any;
|
||||
@@ -174,112 +171,6 @@ export class QuickOpenEntry {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
// Give matches with label highlights higher priority over
|
||||
// those with only description highlights
|
||||
const labelHighlightsA = elementA.getHighlights()[0] || [];
|
||||
const labelHighlightsB = elementB.getHighlights()[0] || [];
|
||||
if (labelHighlightsA.length && !labelHighlightsB.length) {
|
||||
return -1;
|
||||
} else if (!labelHighlightsA.length && labelHighlightsB.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Fallback to the full path if labels are identical and we have associated resources
|
||||
let nameA = elementA.getLabel();
|
||||
let nameB = elementB.getLabel();
|
||||
if (nameA === nameB) {
|
||||
const resourceA = elementA.getResource();
|
||||
const resourceB = elementB.getResource();
|
||||
|
||||
if (resourceA && resourceB) {
|
||||
nameA = resourceA.fsPath;
|
||||
nameB = resourceB.fsPath;
|
||||
}
|
||||
}
|
||||
|
||||
return compareAnything(nameA, nameB, lookFor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[] = [];
|
||||
const descriptionHighlights: IHighlight[] = [];
|
||||
|
||||
const normalizedLookFor = strings.stripWildcards(lookFor);
|
||||
const label = entry.getLabel();
|
||||
const description = entry.getDescription();
|
||||
|
||||
// Highlight file aware
|
||||
if (entry.getResource()) {
|
||||
|
||||
// Highlight entire label and description if searching for full absolute path
|
||||
const fsPath = entry.getResource().fsPath;
|
||||
if (lookFor.length === fsPath.length && lookFor.toLowerCase() === fsPath.toLowerCase()) {
|
||||
labelHighlights.push({ start: 0, end: label.length });
|
||||
descriptionHighlights.push({ start: 0, end: description.length });
|
||||
}
|
||||
|
||||
// Fuzzy/Full-Path: Highlight is special
|
||||
else if (fuzzyHighlight || lookFor.indexOf(paths.nativeSep) >= 0) {
|
||||
const candidateLabelHighlights = filters.matchesFuzzy(lookFor, label, fuzzyHighlight);
|
||||
if (!candidateLabelHighlights) {
|
||||
const pathPrefix = description ? (description + paths.nativeSep) : '';
|
||||
const pathPrefixLength = pathPrefix.length;
|
||||
|
||||
// If there are no highlights in the label, build a path out of description and highlight and match on both,
|
||||
// then extract the individual label and description highlights back to the original positions
|
||||
let pathHighlights = filters.matchesFuzzy(lookFor, pathPrefix + label, fuzzyHighlight);
|
||||
if (!pathHighlights && lookFor !== normalizedLookFor) {
|
||||
pathHighlights = filters.matchesFuzzy(normalizedLookFor, pathPrefix + label, fuzzyHighlight);
|
||||
}
|
||||
|
||||
if (pathHighlights) {
|
||||
pathHighlights.forEach(h => {
|
||||
|
||||
// Match overlaps label and description part, we need to split it up
|
||||
if (h.start < pathPrefixLength && h.end > pathPrefixLength) {
|
||||
labelHighlights.push({ start: 0, end: h.end - pathPrefixLength });
|
||||
descriptionHighlights.push({ start: h.start, end: pathPrefixLength });
|
||||
}
|
||||
|
||||
// Match on label part
|
||||
else if (h.start >= pathPrefixLength) {
|
||||
labelHighlights.push({ start: h.start - pathPrefixLength, end: h.end - pathPrefixLength });
|
||||
}
|
||||
|
||||
// Match on description part
|
||||
else {
|
||||
descriptionHighlights.push(h);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
labelHighlights = candidateLabelHighlights;
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight only inside label
|
||||
else {
|
||||
labelHighlights = filters.matchesFuzzy(lookFor, label);
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight by label otherwise
|
||||
else {
|
||||
labelHighlights = filters.matchesFuzzy(lookFor, label);
|
||||
}
|
||||
|
||||
return { labelHighlights, descriptionHighlights };
|
||||
}
|
||||
|
||||
public isFile(): boolean {
|
||||
return false; // TODO@Ben debt with editor history merging
|
||||
}
|
||||
@@ -690,3 +581,37 @@ export class QuickOpenModel implements
|
||||
return entry.run(mode, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A good default sort implementation for quick open entries respecting highlight information
|
||||
* as well as associated resources.
|
||||
*/
|
||||
export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number {
|
||||
|
||||
// Give matches with label highlights higher priority over
|
||||
// those with only description highlights
|
||||
const labelHighlightsA = elementA.getHighlights()[0] || [];
|
||||
const labelHighlightsB = elementB.getHighlights()[0] || [];
|
||||
if (labelHighlightsA.length && !labelHighlightsB.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!labelHighlightsA.length && labelHighlightsB.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Fallback to the full path if labels are identical and we have associated resources
|
||||
let nameA = elementA.getLabel();
|
||||
let nameB = elementB.getLabel();
|
||||
if (nameA === nameB) {
|
||||
const resourceA = elementA.getResource();
|
||||
const resourceB = elementB.getResource();
|
||||
|
||||
if (resourceA && resourceB) {
|
||||
nameA = resourceA.fsPath;
|
||||
nameB = resourceB.fsPath;
|
||||
}
|
||||
}
|
||||
|
||||
return compareAnything(nameA, nameB, lookFor);
|
||||
}
|
||||
+43
-21
@@ -6,7 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { matchesPrefix, IMatch, createMatches, matchesCamelCase } from 'vs/base/common/filters';
|
||||
import { matchesPrefix, IMatch, createMatches, matchesCamelCase, isSeparatorAtPos, isUpper } from 'vs/base/common/filters';
|
||||
import { isEqual, nativeSep } from 'vs/base/common/paths';
|
||||
|
||||
export type Score = [number /* score */, number[] /* match positions */];
|
||||
@@ -14,8 +14,6 @@ export type ScorerCache = { [key: string]: IItemScore };
|
||||
|
||||
const NO_SCORE: Score = [0, []];
|
||||
|
||||
const wordPathBoundary = ['-', '_', ' ', '/', '\\', '.'];
|
||||
|
||||
// Based on material from:
|
||||
/*!
|
||||
BEGIN THIRD PARTY
|
||||
@@ -45,7 +43,7 @@ BEGIN THIRD PARTY
|
||||
* Start of word/path bonus: 7
|
||||
* Start of string bonus: 8
|
||||
*/
|
||||
export function _doScore(target: string, query: string, inverse?: boolean): Score {
|
||||
export function _doScore(target: string, query: string, fuzzy: boolean, inverse?: boolean): Score {
|
||||
if (!target || !query) {
|
||||
return NO_SCORE; // return early if target or query are undefined
|
||||
}
|
||||
@@ -72,8 +70,34 @@ export function _doScore(target: string, query: string, inverse?: boolean): Scor
|
||||
startAt = target.length - 1; // inverse: from end of target to beginning
|
||||
}
|
||||
|
||||
// When not searching fuzzy, we require the query to be contained fully
|
||||
// in the target string.
|
||||
if (!fuzzy) {
|
||||
let indexOfQueryInTarget: number;
|
||||
if (!inverse) {
|
||||
indexOfQueryInTarget = targetLower.indexOf(queryLower);
|
||||
} else {
|
||||
indexOfQueryInTarget = targetLower.lastIndexOf(queryLower);
|
||||
}
|
||||
|
||||
if (indexOfQueryInTarget === -1) {
|
||||
// console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`);
|
||||
|
||||
return NO_SCORE;
|
||||
}
|
||||
|
||||
// Adjust the start position with the offset of the query
|
||||
if (!inverse) {
|
||||
startAt = indexOfQueryInTarget;
|
||||
} else {
|
||||
startAt = indexOfQueryInTarget + query.length;
|
||||
}
|
||||
}
|
||||
|
||||
let score = 0;
|
||||
while (inverse ? index >= 0 : index < queryLen) {
|
||||
|
||||
// Check for query character being contained in target
|
||||
let indexOf: number;
|
||||
if (!inverse) {
|
||||
indexOf = targetLower.indexOf(queryLower[index], startAt);
|
||||
@@ -82,10 +106,9 @@ export function _doScore(target: string, query: string, inverse?: boolean): Scor
|
||||
}
|
||||
|
||||
if (indexOf < 0) {
|
||||
|
||||
// console.log(`Character not part of target ${query[index]}`);
|
||||
|
||||
score = 0; // This makes sure that the query is contained in the target
|
||||
score = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -119,7 +142,7 @@ export function _doScore(target: string, query: string, inverse?: boolean): Scor
|
||||
}
|
||||
|
||||
// After separator bonus
|
||||
else if (wordPathBoundary.some(w => w === target[indexOf - 1])) {
|
||||
else if (isSeparatorAtPos(target, indexOf - 1)) {
|
||||
score += 7;
|
||||
|
||||
// console.log('After separtor bonus: +7');
|
||||
@@ -156,9 +179,6 @@ export function _doScore(target: string, query: string, inverse?: boolean): Scor
|
||||
return res;
|
||||
}
|
||||
|
||||
function isUpper(code: number): boolean {
|
||||
return 65 <= code && code <= 90;
|
||||
}
|
||||
/*!
|
||||
END THIRD PARTY
|
||||
*/
|
||||
@@ -209,7 +229,7 @@ const LABEL_PREFIX_SCORE = 1 << 17;
|
||||
const LABEL_CAMELCASE_SCORE = 1 << 16;
|
||||
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
||||
|
||||
export function scoreItem<T>(item: T, query: string, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
||||
export function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
||||
if (!item || !query) {
|
||||
return NO_ITEM_SCORE; // we need an item and query to score on at least
|
||||
}
|
||||
@@ -221,9 +241,11 @@ export function scoreItem<T>(item: T, query: string, accessor: IItemAccessor<T>,
|
||||
|
||||
const description = accessor.getItemDescription(item);
|
||||
|
||||
let cacheHash = label + query;
|
||||
let cacheHash: string;
|
||||
if (description) {
|
||||
cacheHash += description;
|
||||
cacheHash = `${label}${description}${query}${fuzzy}`;
|
||||
} else {
|
||||
cacheHash = `${label}${query}${fuzzy}`;
|
||||
}
|
||||
|
||||
const cached = cache[cacheHash];
|
||||
@@ -231,13 +253,13 @@ export function scoreItem<T>(item: T, query: string, accessor: IItemAccessor<T>,
|
||||
return cached;
|
||||
}
|
||||
|
||||
const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, accessor);
|
||||
const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, fuzzy, accessor);
|
||||
cache[cacheHash] = itemScore;
|
||||
|
||||
return itemScore;
|
||||
}
|
||||
|
||||
function doScoreItem<T>(label: string, description: string, path: string, query: string, accessor: IItemAccessor<T>): IItemScore {
|
||||
function doScoreItem<T>(label: string, description: string, path: string, query: string, fuzzy: boolean, accessor: IItemAccessor<T>): IItemScore {
|
||||
|
||||
// 1.) treat identity matches on full path highest
|
||||
if (path && isEqual(query, path, true)) {
|
||||
@@ -257,7 +279,7 @@ function doScoreItem<T>(label: string, description: string, path: string, query:
|
||||
}
|
||||
|
||||
// 4.) prefer scores on the label if any
|
||||
const [labelScore, labelPositions] = _doScore(label, query);
|
||||
const [labelScore, labelPositions] = _doScore(label, query, fuzzy);
|
||||
if (labelScore) {
|
||||
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
|
||||
}
|
||||
@@ -272,12 +294,12 @@ function doScoreItem<T>(label: string, description: string, path: string, query:
|
||||
const descriptionPrefixLength = descriptionPrefix.length;
|
||||
const descriptionAndLabel = `${descriptionPrefix}${label}`;
|
||||
|
||||
let [labelDescriptionScore, labelDescriptionPositions] = _doScore(descriptionAndLabel, query);
|
||||
let [labelDescriptionScore, labelDescriptionPositions] = _doScore(descriptionAndLabel, query, fuzzy);
|
||||
|
||||
// Optimize for file paths: score from the back to the beginning to catch more specific folder
|
||||
// names that match on the end of the file. This yields better results in most cases.
|
||||
if (!!path) {
|
||||
const [labelDescriptionScoreInverse, labelDescriptionPositionsInverse] = _doScore(descriptionAndLabel, query, true /* inverse */);
|
||||
const [labelDescriptionScoreInverse, labelDescriptionPositionsInverse] = _doScore(descriptionAndLabel, query, fuzzy, true /* inverse */);
|
||||
if (labelDescriptionScoreInverse && labelDescriptionScoreInverse > labelDescriptionScore) {
|
||||
labelDescriptionScore = labelDescriptionScoreInverse;
|
||||
labelDescriptionPositions = labelDescriptionPositionsInverse;
|
||||
@@ -316,9 +338,9 @@ function doScoreItem<T>(label: string, description: string, path: string, query:
|
||||
return NO_ITEM_SCORE;
|
||||
}
|
||||
|
||||
export function compareItemsByScore<T>(itemA: T, itemB: T, query: string, accessor: IItemAccessor<T>, cache: ScorerCache): number {
|
||||
const scoreA = scoreItem(itemA, query, accessor, cache).score;
|
||||
const scoreB = scoreItem(itemB, query, accessor, cache).score;
|
||||
export function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): number {
|
||||
const scoreA = scoreItem(itemA, query, fuzzy, accessor, cache).score;
|
||||
const scoreB = scoreItem(itemB, query, fuzzy, accessor, cache).score;
|
||||
|
||||
// 1.) check for identity matches
|
||||
if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) {
|
||||
+78
-50
@@ -6,7 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as scorer from 'vs/base/common/scorer';
|
||||
import * as scorer from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { basename, dirname } from 'vs/base/common/paths';
|
||||
|
||||
@@ -45,25 +45,25 @@ class NullAccessorClass implements scorer.IItemAccessor<URI> {
|
||||
const NullAccessor = new NullAccessorClass();
|
||||
const cache: scorer.ScorerCache = Object.create(null);
|
||||
|
||||
suite('Scorer', () => {
|
||||
suite('Quick Open Scorer', () => {
|
||||
|
||||
test('score', function () {
|
||||
test('score (fuzzy)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
const scores: scorer.Score[] = [];
|
||||
scores.push(scorer._doScore(target, 'HelLo-World')); // direct case match
|
||||
scores.push(scorer._doScore(target, 'hello-world')); // direct mix-case match
|
||||
scores.push(scorer._doScore(target, 'HW')); // direct case prefix (multiple)
|
||||
scores.push(scorer._doScore(target, 'hw')); // direct mix-case prefix (multiple)
|
||||
scores.push(scorer._doScore(target, 'H')); // direct case prefix
|
||||
scores.push(scorer._doScore(target, 'h')); // direct mix-case prefix
|
||||
scores.push(scorer._doScore(target, 'W')); // direct case word prefix
|
||||
scores.push(scorer._doScore(target, 'w')); // direct mix-case word prefix
|
||||
scores.push(scorer._doScore(target, 'Ld')); // in-string case match (multiple)
|
||||
scores.push(scorer._doScore(target, 'ld')); // in-string mix-case match
|
||||
scores.push(scorer._doScore(target, 'L')); // in-string case match
|
||||
scores.push(scorer._doScore(target, 'l')); // in-string mix-case match
|
||||
scores.push(scorer._doScore(target, '4')); // no match
|
||||
scores.push(scorer._doScore(target, 'HelLo-World', true)); // direct case match
|
||||
scores.push(scorer._doScore(target, 'hello-world', true)); // direct mix-case match
|
||||
scores.push(scorer._doScore(target, 'HW', true)); // direct case prefix (multiple)
|
||||
scores.push(scorer._doScore(target, 'hw', true)); // direct mix-case prefix (multiple)
|
||||
scores.push(scorer._doScore(target, 'H', true)); // direct case prefix
|
||||
scores.push(scorer._doScore(target, 'h', true)); // direct mix-case prefix
|
||||
scores.push(scorer._doScore(target, 'W', true)); // direct case word prefix
|
||||
scores.push(scorer._doScore(target, 'w', true)); // direct mix-case word prefix
|
||||
scores.push(scorer._doScore(target, 'Ld', true)); // in-string case match (multiple)
|
||||
scores.push(scorer._doScore(target, 'ld', true)); // in-string mix-case match
|
||||
scores.push(scorer._doScore(target, 'L', true)); // in-string case match
|
||||
scores.push(scorer._doScore(target, 'l', true)); // in-string mix-case match
|
||||
scores.push(scorer._doScore(target, '4', true)); // no match
|
||||
|
||||
// Assert scoring order
|
||||
let sortedScores = scores.concat().sort((a, b) => b[0] - a[0]);
|
||||
@@ -79,17 +79,45 @@ suite('Scorer', () => {
|
||||
assert.equal(positions[1], 6);
|
||||
});
|
||||
|
||||
test('score (non fuzzy)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
assert.ok(scorer._doScore(target, 'HelLo-World', false)[0] > 0);
|
||||
assert.equal(scorer._doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length);
|
||||
|
||||
assert.ok(scorer._doScore(target, 'hello-world', false)[0] > 0);
|
||||
assert.equal(scorer._doScore(target, 'HW', false)[0], 0);
|
||||
assert.ok(scorer._doScore(target, 'h', false)[0] > 0);
|
||||
assert.ok(scorer._doScore(target, 'ello', false)[0] > 0);
|
||||
assert.ok(scorer._doScore(target, 'ld', false)[0] > 0);
|
||||
assert.equal(scorer._doScore(target, 'eo', false)[0], 0);
|
||||
});
|
||||
|
||||
test('score (non fuzzy, inverse)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
assert.ok(scorer._doScore(target, 'HelLo-World', false, true)[0] > 0);
|
||||
assert.equal(scorer._doScore(target, 'HelLo-World', false, true)[1].length, 'HelLo-World'.length);
|
||||
|
||||
assert.ok(scorer._doScore(target, 'hello-world', false, true)[0] > 0);
|
||||
assert.equal(scorer._doScore(target, 'HW', false, true)[0], 0);
|
||||
assert.ok(scorer._doScore(target, 'h', false, true)[0] > 0);
|
||||
assert.ok(scorer._doScore(target, 'ello', false, true)[0] > 0);
|
||||
assert.ok(scorer._doScore(target, 'ld', false, true)[0] > 0);
|
||||
assert.equal(scorer._doScore(target, 'eo', false, true)[0], 0);
|
||||
});
|
||||
|
||||
test('scoreItem - matches are proper', function () {
|
||||
let res = scorer.scoreItem(null, 'something', ResourceAccessor, cache);
|
||||
let res = scorer.scoreItem(null, 'something', true, ResourceAccessor, cache);
|
||||
assert.ok(!res.score);
|
||||
|
||||
const resource = URI.file('/xyz/some/path/someFile123.txt');
|
||||
|
||||
res = scorer.scoreItem(resource, 'something', NullAccessor, cache);
|
||||
res = scorer.scoreItem(resource, 'something', true, NullAccessor, cache);
|
||||
assert.ok(!res.score);
|
||||
|
||||
// Path Identity
|
||||
const identityRes = scorer.scoreItem(resource, ResourceAccessor.getItemPath(resource), ResourceAccessor, cache);
|
||||
const identityRes = scorer.scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache);
|
||||
assert.ok(identityRes.score);
|
||||
assert.equal(identityRes.descriptionMatch.length, 1);
|
||||
assert.equal(identityRes.labelMatch.length, 1);
|
||||
@@ -99,7 +127,7 @@ suite('Scorer', () => {
|
||||
assert.equal(identityRes.labelMatch[0].end, ResourceAccessor.getItemLabel(resource).length);
|
||||
|
||||
// Basename Prefix
|
||||
const basenamePrefixRes = scorer.scoreItem(resource, 'som', ResourceAccessor, cache);
|
||||
const basenamePrefixRes = scorer.scoreItem(resource, 'som', true, ResourceAccessor, cache);
|
||||
assert.ok(basenamePrefixRes.score);
|
||||
assert.ok(!basenamePrefixRes.descriptionMatch);
|
||||
assert.equal(basenamePrefixRes.labelMatch.length, 1);
|
||||
@@ -107,7 +135,7 @@ suite('Scorer', () => {
|
||||
assert.equal(basenamePrefixRes.labelMatch[0].end, 'som'.length);
|
||||
|
||||
// Basename Camelcase
|
||||
const basenameCamelcaseRes = scorer.scoreItem(resource, 'sF', ResourceAccessor, cache);
|
||||
const basenameCamelcaseRes = scorer.scoreItem(resource, 'sF', true, ResourceAccessor, cache);
|
||||
assert.ok(basenameCamelcaseRes.score);
|
||||
assert.ok(!basenameCamelcaseRes.descriptionMatch);
|
||||
assert.equal(basenameCamelcaseRes.labelMatch.length, 2);
|
||||
@@ -117,7 +145,7 @@ suite('Scorer', () => {
|
||||
assert.equal(basenameCamelcaseRes.labelMatch[1].end, 5);
|
||||
|
||||
// Basename Match
|
||||
const basenameRes = scorer.scoreItem(resource, 'of', ResourceAccessor, cache);
|
||||
const basenameRes = scorer.scoreItem(resource, 'of', true, ResourceAccessor, cache);
|
||||
assert.ok(basenameRes.score);
|
||||
assert.ok(!basenameRes.descriptionMatch);
|
||||
assert.equal(basenameRes.labelMatch.length, 2);
|
||||
@@ -127,7 +155,7 @@ suite('Scorer', () => {
|
||||
assert.equal(basenameRes.labelMatch[1].end, 5);
|
||||
|
||||
// Path Match
|
||||
const pathRes = scorer.scoreItem(resource, 'xyz123', ResourceAccessor, cache);
|
||||
const pathRes = scorer.scoreItem(resource, 'xyz123', true, ResourceAccessor, cache);
|
||||
assert.ok(pathRes.score);
|
||||
assert.ok(pathRes.descriptionMatch);
|
||||
assert.ok(pathRes.labelMatch);
|
||||
@@ -139,7 +167,7 @@ suite('Scorer', () => {
|
||||
assert.equal(pathRes.descriptionMatch[0].end, 4);
|
||||
|
||||
// No Match
|
||||
const noRes = scorer.scoreItem(resource, '987', ResourceAccessor, cache);
|
||||
const noRes = scorer.scoreItem(resource, '987', true, ResourceAccessor, cache);
|
||||
assert.ok(!noRes.score);
|
||||
assert.ok(!noRes.labelMatch);
|
||||
assert.ok(!noRes.descriptionMatch);
|
||||
@@ -157,7 +185,7 @@ suite('Scorer', () => {
|
||||
// xsp is more relevant to the end of the file path even though it matches
|
||||
// fuzzy also in the beginning. we verify the more relevant match at the
|
||||
// end gets returned.
|
||||
const pathRes = scorer.scoreItem(resource, 'xspfile123', ResourceAccessor, cache);
|
||||
const pathRes = scorer.scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache);
|
||||
assert.ok(pathRes.score);
|
||||
assert.ok(pathRes.descriptionMatch);
|
||||
assert.ok(pathRes.labelMatch);
|
||||
@@ -177,12 +205,12 @@ suite('Scorer', () => {
|
||||
// Full resource A path
|
||||
let query = ResourceAccessor.getItemPath(resourceA);
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -190,12 +218,12 @@ suite('Scorer', () => {
|
||||
// Full resource B path
|
||||
query = ResourceAccessor.getItemPath(resourceB);
|
||||
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -209,12 +237,12 @@ suite('Scorer', () => {
|
||||
// Full resource A basename
|
||||
let query = ResourceAccessor.getItemLabel(resourceA);
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -222,12 +250,12 @@ suite('Scorer', () => {
|
||||
// Full resource B basename
|
||||
query = ResourceAccessor.getItemLabel(resourceB);
|
||||
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -241,12 +269,12 @@ suite('Scorer', () => {
|
||||
// resource A camelcase
|
||||
let query = 'fA';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -254,12 +282,12 @@ suite('Scorer', () => {
|
||||
// resource B camelcase
|
||||
query = 'fB';
|
||||
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -273,12 +301,12 @@ suite('Scorer', () => {
|
||||
// Resource A part of basename
|
||||
let query = 'fileA';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -286,12 +314,12 @@ suite('Scorer', () => {
|
||||
// Resource B part of basename
|
||||
query = 'fileB';
|
||||
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -305,12 +333,12 @@ suite('Scorer', () => {
|
||||
// Resource A part of path
|
||||
let query = 'pathfileA';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -318,12 +346,12 @@ suite('Scorer', () => {
|
||||
// Resource B part of path
|
||||
query = 'pathfileB';
|
||||
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -337,12 +365,12 @@ suite('Scorer', () => {
|
||||
// Resource A part of path
|
||||
let query = 'somepath';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -356,12 +384,12 @@ suite('Scorer', () => {
|
||||
// Resource A part of path
|
||||
let query = 'somepath';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceA);
|
||||
assert.equal(res[1], resourceB);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -374,7 +402,7 @@ suite('Scorer', () => {
|
||||
|
||||
let query = 'co/te';
|
||||
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, ResourceAccessor, cache));
|
||||
let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache));
|
||||
assert.equal(res[0], resourceB);
|
||||
assert.equal(res[1], resourceA);
|
||||
assert.equal(res[2], resourceC);
|
||||
@@ -23,7 +23,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { EditorInput, toResource, IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor';
|
||||
import { stripWildcards } from 'vs/base/common/strings';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache } from 'vs/base/common/scorer';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
|
||||
export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
private stacks: IEditorStacksModel;
|
||||
@@ -116,7 +116,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
const itemScore = scoreItem(e, searchValue, QuickOpenItemAccessor, this.scorerCache);
|
||||
const itemScore = scoreItem(e, searchValue, true, QuickOpenItemAccessor, this.scorerCache);
|
||||
if (!itemScore.score) {
|
||||
return false;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
return stacks.positionOfGroup(e1.group) - stacks.positionOfGroup(e2.group);
|
||||
}
|
||||
|
||||
return compareItemsByScore(e1, e2, searchValue, QuickOpenItemAccessor, this.scorerCache);
|
||||
return compareItemsByScore(e1, e2, searchValue, true, QuickOpenItemAccessor, this.scorerCache);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, QuickOpenItemAccessorClass } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { QuickOpenWidget, HideReason } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
|
||||
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
|
||||
import labels = require('vs/base/common/labels');
|
||||
@@ -55,6 +55,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { FileKind, IFileService } from 'vs/platform/files/common/files';
|
||||
import { scoreItem, ScorerCache, compareItemsByScore } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
|
||||
const HELP_PREFIX = '?';
|
||||
|
||||
@@ -458,7 +459,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
return pickA.index - pickB.index; // restore natural order
|
||||
}
|
||||
|
||||
return QuickOpenEntry.compare(pickA, pickB, normalizedSearchValue);
|
||||
return compareEntries(pickA, pickB, normalizedSearchValue);
|
||||
});
|
||||
|
||||
this.pickOpenWidget.refresh(model, value ? { autoFocusFirstEntry: true } : autoFocus);
|
||||
@@ -1164,6 +1165,7 @@ class PickOpenActionProvider implements IActionProvider {
|
||||
}
|
||||
|
||||
class EditorHistoryHandler {
|
||||
private scorerCache: ScorerCache;
|
||||
|
||||
constructor(
|
||||
@IHistoryService private historyService: IHistoryService,
|
||||
@@ -1171,11 +1173,12 @@ class EditorHistoryHandler {
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IFileService private fileService: IFileService
|
||||
) {
|
||||
this.scorerCache = Object.create(null);
|
||||
}
|
||||
|
||||
public getResults(searchValue?: string): QuickOpenEntry[] {
|
||||
if (searchValue) {
|
||||
searchValue = searchValue.replace(/ /g, ''); // get rid of all whitespace
|
||||
searchValue = strings.stripWildcards(searchValue.replace(/ /g, '')); // get rid of all whitespace and wildcards
|
||||
}
|
||||
|
||||
// Just return all if we are not searching
|
||||
@@ -1184,49 +1187,57 @@ class EditorHistoryHandler {
|
||||
return history.map(input => this.instantiationService.createInstance(EditorHistoryEntry, input));
|
||||
}
|
||||
|
||||
const searchInPath = searchValue.indexOf(paths.nativeSep) >= 0;
|
||||
// Otherwise filter by search value and sort by score. Include matches on description
|
||||
// in case the user is explicitly including path separators.
|
||||
const accessor = searchValue.indexOf(paths.nativeSep) >= 0 ? MatchOnDescription : DoNotMatchOnDescription;
|
||||
return history
|
||||
|
||||
const results: QuickOpenEntry[] = [];
|
||||
history.forEach(input => {
|
||||
let resource: URI;
|
||||
if (input instanceof EditorInput) {
|
||||
resource = resourceForEditorHistory(input, this.fileService);
|
||||
} else {
|
||||
resource = (input as IResourceInput).resource;
|
||||
}
|
||||
// For now, only support to match on inputs that provide resource information
|
||||
.filter(input => {
|
||||
let resource: URI;
|
||||
if (input instanceof EditorInput) {
|
||||
resource = resourceForEditorHistory(input, this.fileService);
|
||||
} else {
|
||||
resource = (input as IResourceInput).resource;
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
return; //For now, only support to match on inputs that provide resource information
|
||||
}
|
||||
return !!resource;
|
||||
})
|
||||
|
||||
let searchTargetToMatch: string;
|
||||
if (searchInPath) {
|
||||
searchTargetToMatch = labels.getPathLabel(resource, this.contextService);
|
||||
} else if (input instanceof EditorInput) {
|
||||
searchTargetToMatch = input.getName();
|
||||
} else {
|
||||
searchTargetToMatch = paths.basename((input as IResourceInput).resource.fsPath);
|
||||
}
|
||||
// Conver to quick open entries
|
||||
.map(input => this.instantiationService.createInstance(EditorHistoryEntry, input))
|
||||
|
||||
// Check if this entry is a match for the search value
|
||||
if (!filters.matchesFuzzy(searchValue, searchTargetToMatch)) {
|
||||
return;
|
||||
}
|
||||
// Make sure the search value is matching
|
||||
.filter(e => {
|
||||
const itemScore = scoreItem(e, searchValue, false, accessor, this.scorerCache);
|
||||
if (!itemScore.score) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entry = this.instantiationService.createInstance(EditorHistoryEntry, input);
|
||||
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
|
||||
|
||||
const { labelHighlights, descriptionHighlights } = QuickOpenEntry.highlight(entry, searchValue);
|
||||
entry.setHighlights(labelHighlights, descriptionHighlights);
|
||||
return true;
|
||||
})
|
||||
|
||||
results.push(entry);
|
||||
});
|
||||
|
||||
// Sort
|
||||
const normalizedSearchValue = strings.stripWildcards(searchValue.toLowerCase());
|
||||
return results.sort((elementA: EditorHistoryEntry, elementB: EditorHistoryEntry) => QuickOpenEntry.compare(elementA, elementB, normalizedSearchValue));
|
||||
// Sort by score
|
||||
.sort((e1, e2) => compareItemsByScore(e1, e2, searchValue, false, accessor, this.scorerCache));
|
||||
}
|
||||
}
|
||||
|
||||
class EditorHistoryItemAccessorClass extends QuickOpenItemAccessorClass {
|
||||
|
||||
constructor(private allowMatchOnDescription: boolean) {
|
||||
super();
|
||||
}
|
||||
|
||||
public getItemDescription(entry: QuickOpenEntry): string {
|
||||
return this.allowMatchOnDescription ? entry.getDescription() : void 0;
|
||||
}
|
||||
}
|
||||
|
||||
const MatchOnDescription = new EditorHistoryItemAccessorClass(true);
|
||||
const DoNotMatchOnDescription = new EditorHistoryItemAccessorClass(false);
|
||||
|
||||
export class EditorHistoryEntryGroup extends QuickOpenEntryGroup {
|
||||
// Marker class
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ 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';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache } from 'vs/base/common/scorer';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
|
||||
export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; // OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load
|
||||
|
||||
@@ -218,7 +218,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
|
||||
// Sort
|
||||
const unsortedResultTime = Date.now();
|
||||
const normalizedSearchValue = strings.stripWildcards(searchValue);
|
||||
const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, normalizedSearchValue, QuickOpenItemAccessor, this.scorerCache);
|
||||
const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, normalizedSearchValue, true, QuickOpenItemAccessor, this.scorerCache);
|
||||
const viewResults = arrays.top(mergedResults, compare, OpenAnythingHandler.MAX_DISPLAYED_RESULTS);
|
||||
const sortedResultTime = Date.now();
|
||||
|
||||
@@ -227,7 +227,7 @@ export class OpenAnythingHandler extends QuickOpenHandler {
|
||||
if (entry instanceof FileEntry) {
|
||||
entry.setRange(searchWithRange ? searchWithRange.range : null);
|
||||
|
||||
const itemScore = scoreItem(entry, normalizedSearchValue, QuickOpenItemAccessor, this.scorerCache);
|
||||
const itemScore = scoreItem(entry, normalizedSearchValue, true, QuickOpenItemAccessor, this.scorerCache);
|
||||
entry.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen';
|
||||
import { QuickOpenModel, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { QuickOpenModel, QuickOpenEntry, compareEntries } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import filters = require('vs/base/common/filters');
|
||||
import strings = require('vs/base/common/strings');
|
||||
@@ -118,7 +118,7 @@ class SymbolEntry extends EditorQuickOpenEntry {
|
||||
return elementAType.localeCompare(elementBType);
|
||||
}
|
||||
|
||||
return QuickOpenEntry.compare(elementA, elementB, searchValue);
|
||||
return compareEntries(elementA, elementB, searchValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +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 { compareItemsByScore, IItemAccessor, ScorerCache } from 'vs/base/common/scorer';
|
||||
import { compareItemsByScore, IItemAccessor, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
|
||||
export class SearchService implements IRawSearchService {
|
||||
|
||||
@@ -254,7 +254,7 @@ export class SearchService implements IRawSearchService {
|
||||
// this is very important because we are also limiting the number of results by config.maxResults
|
||||
// and as such we want the top items to be included in this result set if the number of items
|
||||
// exceeds config.maxResults.
|
||||
const compare = (matchA: IRawFileMatch, matchB: IRawFileMatch) => compareItemsByScore(matchA, matchB, strings.stripWildcards(config.filePattern), FileMatchItemAccessor, scorerCache);
|
||||
const compare = (matchA: IRawFileMatch, matchB: IRawFileMatch) => compareItemsByScore(matchA, matchB, strings.stripWildcards(config.filePattern), true, FileMatchItemAccessor, scorerCache);
|
||||
|
||||
return arrays.topAsync(results, compare, config.maxResults, 10000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user