提交 ee9f1e7e 编写于 作者: B Benjamin Pasero

Quickpick items search reveals octicons used in the items (fixes #21243)

上级 a475e27b
......@@ -7,6 +7,7 @@
import strings = require('vs/base/common/strings');
import { LRUCache } from 'vs/base/common/map';
import { CharCode } from 'vs/base/common/charCode';
import { ltrim } from 'vs/base/common/strings';
export interface IFilter {
// Returns null if word doesn't match.
......@@ -341,6 +342,108 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep
return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst);
}
const octiconStartMarker = '$(';
export function matchesFuzzyOcticonAware(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] {
// Return early if there are no octicon markers in the word to match against
const firstOcticonIndex = wordToMatchAgainst.indexOf(octiconStartMarker);
if (firstOcticonIndex === -1) {
return matchesFuzzy(word, wordToMatchAgainst, enableSeparateSubstringMatching);
}
const octiconOffsets: number[] = [];
let wordToMatchAgainstWithoutOcticons: string = '';
function appendChars(chars: string) {
if (chars) {
wordToMatchAgainstWithoutOcticons += chars;
for (let i = 0; i < chars.length; i++) {
octiconOffsets.push(octiconsOffset); // make sure to fill in octicon offsets
}
}
}
let currentOcticonStart = -1;
let currentOcticonValue: string = '';
let octiconsOffset = 0;
let char: string;
let nextChar: string;
let offset = firstOcticonIndex;
const length = wordToMatchAgainst.length;
// Append all characters until the first octicon
appendChars(wordToMatchAgainst.substr(0, firstOcticonIndex));
// example: $(file-symlink-file) my cool $(other-octicon) entry
while (offset < length) {
char = wordToMatchAgainst[offset];
nextChar = wordToMatchAgainst[offset + 1];
// beginning of octicon: some value $( <--
if (char === octiconStartMarker[0] && nextChar === octiconStartMarker[1]) {
currentOcticonStart = offset;
// if we had a previous potential octicon value without
// the closing ')', it was actually not an octicon and
// so we have to add it to the actual value
appendChars(currentOcticonValue);
currentOcticonValue = octiconStartMarker;
offset++; // jump over '('
}
// end of octicon: some value $(some-octicon) <--
else if (char === ')' && currentOcticonStart !== -1) {
const currentOcticonLength = offset - currentOcticonStart + 1; // +1 to include the closing ')'
octiconsOffset += currentOcticonLength;
currentOcticonStart = -1;
currentOcticonValue = '';
}
// within octicon
else if (currentOcticonStart !== -1) {
currentOcticonValue += char;
}
// any value outside of octicons
else {
appendChars(char);
}
offset++;
}
// if we had a previous potential octicon value without
// the closing ')', it was actually not an octicon and
// so we have to add it to the actual value
appendChars(currentOcticonValue);
// Trim the word to match against because it could have leading
// whitespace now if the word started with an octicon
const wordToMatchAgainstWithoutOcticonsTrimmed = ltrim(wordToMatchAgainstWithoutOcticons, ' ');
const leadingWhitespaceOffset = wordToMatchAgainstWithoutOcticons.length - wordToMatchAgainstWithoutOcticonsTrimmed.length;
// match on value without octicons
const matches = matchesFuzzy(word, wordToMatchAgainstWithoutOcticonsTrimmed, enableSeparateSubstringMatching);
// Map matches back to offsets with octicons and trimming
if (matches) {
for (let i = 0; i < matches.length; i++) {
const octiconOffset = octiconOffsets[matches[i].start] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
matches[i].start += octiconOffset;
matches[i].end += octiconOffset;
}
}
return matches;
}
export function skipScore(pattern: string, word: string, patternMaxWhitespaceIgnore?: number): [number, number[]] {
pattern = pattern.toLowerCase();
word = word.toLowerCase();
......
......@@ -5,7 +5,7 @@
'use strict';
import * as assert from 'assert';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive } from 'vs/base/common/filters';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, matchesFuzzy, matchesFuzzyOcticonAware } from 'vs/base/common/filters';
function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) {
let r = filter(word, wordToMatchAgainst);
......@@ -442,4 +442,53 @@ suite('Filters', () => {
assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGraceful);
assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGracefulAggressive);
});
test('matchesFuzzzyOcticonAware', function () {
// Camel Case
filterOk(matchesFuzzy, 'ccr', 'CamelCaseRocks', [
{ start: 0, end: 1 },
{ start: 5, end: 6 },
{ start: 9, end: 10 }
]);
filterOk(matchesFuzzyOcticonAware, 'ccr', '$(octicon)CamelCaseRocks$(octicon)', [
{ start: 10, end: 11 },
{ start: 15, end: 16 },
{ start: 19, end: 20 }
]);
filterOk(matchesFuzzyOcticonAware, 'ccr', '$(octicon) CamelCaseRocks $(octicon)', [
{ start: 11, end: 12 },
{ start: 16, end: 17 },
{ start: 20, end: 21 }
]);
filterOk(matchesFuzzyOcticonAware, 'iut', '$(octicon) Indent $(octico) Using $(octic) Tpaces', [
{ start: 11, end: 12 },
{ start: 28, end: 29 },
{ start: 43, end: 44 },
]);
// Prefix
filterOk(matchesFuzzy, 'using', 'Indent Using Spaces', [
{ start: 7, end: 12 }
]);
filterOk(matchesFuzzyOcticonAware, 'using', '$(octicon) Indent Using Spaces', [
{ start: 18, end: 23 },
]);
// Broken Octicon
filterOk(matchesFuzzyOcticonAware, 'octicon', 'This $(octicon Indent Using Spaces', [
{ start: 7, end: 14 },
]);
filterOk(matchesFuzzyOcticonAware, 'indent', 'This $octicon Indent Using Spaces', [
{ start: 14, end: 20 },
]);
});
});
......@@ -11,7 +11,6 @@ import nls = require('vs/nls');
import * as browser from 'vs/base/browser/browser';
import { Dimension, withElementById } from 'vs/base/browser/builder';
import strings = require('vs/base/common/strings');
import filters = require('vs/base/common/filters');
import DOM = require('vs/base/browser/dom');
import URI from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
......@@ -55,6 +54,7 @@ import { FileKind, IFileService } from 'vs/platform/files/common/files';
import { scoreItem, ScorerCache, compareItemsByScore, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { getBaseLabel } from 'vs/base/common/labels';
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
import { matchesFuzzyOcticonAware } from 'vs/base/common/filters';
const HELP_PREFIX = '?';
......@@ -431,12 +431,12 @@ export class QuickOpenController extends Component implements IQuickOpenService
});
}
// Filter by value
// Filter by value (since we support octicons, use octicon aware fuzzy matching)
else {
entries.forEach(entry => {
const labelHighlights = filters.matchesFuzzy(value, entry.getLabel());
const descriptionHighlights = options.matchOnDescription && filters.matchesFuzzy(value, entry.getDescription());
const detailHighlights = options.matchOnDetail && entry.getDetail() && filters.matchesFuzzy(value, entry.getDetail());
const labelHighlights = matchesFuzzyOcticonAware(value, entry.getLabel());
const descriptionHighlights = options.matchOnDescription && matchesFuzzyOcticonAware(value, entry.getDescription());
const detailHighlights = options.matchOnDetail && entry.getDetail() && matchesFuzzyOcticonAware(value, entry.getDetail());
if (entry.shouldAlwaysShow() || labelHighlights || descriptionHighlights || detailHighlights) {
entry.setHighlights(labelHighlights, descriptionHighlights, detailHighlights);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册