///
///
describe("PatternMatcher", function () {
describe("BreakIntoCharacterSpans", function () {
it("EmptyIdentifier", () => {
verifyBreakIntoCharacterSpans("");
});
it("SimpleIdentifier", () => {
verifyBreakIntoCharacterSpans("foo", "foo");
});
it("PrefixUnderscoredIdentifier", () => {
verifyBreakIntoCharacterSpans("_foo", "_", "foo");
});
it("UnderscoredIdentifier", () => {
verifyBreakIntoCharacterSpans("f_oo", "f", "_", "oo");
});
it("PostfixUnderscoredIdentifier", () => {
verifyBreakIntoCharacterSpans("foo_", "foo", "_");
});
it("PrefixUnderscoredIdentifierWithCapital", () => {
verifyBreakIntoCharacterSpans("_Foo", "_", "Foo");
});
it("MUnderscorePrefixed", () => {
verifyBreakIntoCharacterSpans("m_foo", "m", "_", "foo");
});
it("CamelCaseIdentifier", () => {
verifyBreakIntoCharacterSpans("FogBar", "Fog", "Bar");
});
it("MixedCaseIdentifier", () => {
verifyBreakIntoCharacterSpans("fogBar", "fog", "Bar");
});
it("TwoCharacterCapitalIdentifier", () => {
verifyBreakIntoCharacterSpans("UIElement", "U", "I", "Element");
});
it("NumberSuffixedIdentifier", () => {
verifyBreakIntoCharacterSpans("Foo42", "Foo", "42");
});
it("NumberContainingIdentifier", () => {
verifyBreakIntoCharacterSpans("Fog42Bar", "Fog", "42", "Bar");
});
it("NumberPrefixedIdentifier", () => {
verifyBreakIntoCharacterSpans("42Bar", "42", "Bar");
});
});
describe("BreakIntoWordSpans", function () {
it("VarbatimIdentifier", () => {
verifyBreakIntoWordSpans("@int:", "int");
});
it("AllCapsConstant", () => {
verifyBreakIntoWordSpans("C_STYLE_CONSTANT", "C", "_", "STYLE", "_", "CONSTANT");
});
it("SingleLetterPrefix1", () => {
verifyBreakIntoWordSpans("UInteger", "U", "Integer");
});
it("SingleLetterPrefix2", () => {
verifyBreakIntoWordSpans("IDisposable", "I", "Disposable");
});
it("TwoCharacterCapitalIdentifier", () => {
verifyBreakIntoWordSpans("UIElement", "UI", "Element");
});
it("XDocument", () => {
verifyBreakIntoWordSpans("XDocument", "X", "Document");
});
it("XMLDocument1", () => {
verifyBreakIntoWordSpans("XMLDocument", "XML", "Document");
});
it("XMLDocument2", () => {
verifyBreakIntoWordSpans("XmlDocument", "Xml", "Document");
});
it("TwoUppercaseCharacters", () => {
verifyBreakIntoWordSpans("SimpleUIElement", "Simple", "UI", "Element");
});
});
describe("SingleWordPattern", () => {
it("PreferCaseSensitiveExact", () => {
const match = getFirstMatch("Foo", "Foo");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveExactInsensitive", () => {
const match = getFirstMatch("foo", "Foo");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitivePrefix", () => {
const match = getFirstMatch("Foo", "Fo");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitivePrefixCaseInsensitive", () => {
const match = getFirstMatch("Foo", "fo");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitiveCamelCaseMatchSimple", () => {
const match = getFirstMatch("FogBar", "FB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertInRange(match.camelCaseWeight, 1, 1 << 30);
});
it("PreferCaseSensitiveCamelCaseMatchPartialPattern", () => {
const match = getFirstMatch("FogBar", "FoB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveCamelCaseMatchToLongPattern1", () => {
const match = getFirstMatch("FogBar", "FBB");
assert.isTrue(match === undefined);
});
it("PreferCaseSensitiveCamelCaseMatchToLongPattern2", () => {
const match = getFirstMatch("FogBar", "FoooB");
assert.isTrue(match === undefined);
});
it("CamelCaseMatchPartiallyUnmatched", () => {
const match = getFirstMatch("FogBarBaz", "FZ");
assert.isTrue(match === undefined);
});
it("CamelCaseMatchCompletelyUnmatched", () => {
const match = getFirstMatch("FogBarBaz", "ZZ");
assert.isTrue(match === undefined);
});
it("TwoUppercaseCharacters", () => {
const match = getFirstMatch("SimpleUIElement", "SiUI");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveLowercasePattern", () => {
const match = getFirstMatch("FogBar", "b");
assert.equal(ts.PatternMatchKind.substring, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitiveLowercasePattern2", () => {
const match = getFirstMatch("FogBar", "fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitiveTryUnderscoredName", () => {
const match = getFirstMatch("_fogBar", "_fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveTryUnderscoredName2", () => {
const match = getFirstMatch("_fogBar", "fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveTryUnderscoredNameInsensitive", () => {
const match = getFirstMatch("_FogBar", "_fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitiveMiddleUnderscore", () => {
const match = getFirstMatch("Fog_Bar", "FB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveMiddleUnderscore2", () => {
const match = getFirstMatch("Fog_Bar", "F_B");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("PreferCaseSensitiveMiddleUnderscore3", () => {
const match = getFirstMatch("Fog_Bar", "F__B");
assert.isTrue(undefined === match);
});
it("PreferCaseSensitiveMiddleUnderscore4", () => {
const match = getFirstMatch("Fog_Bar", "f_B");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitiveMiddleUnderscore5", () => {
const match = getFirstMatch("Fog_Bar", "F_b");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
});
it("PreferCaseSensitiveRelativeWeights1", () => {
const match1 = getFirstMatch("FogBarBaz", "FB");
const match2 = getFirstMatch("FooFlobBaz", "FB");
// We should prefer something that starts at the beginning if possible
assertInRange(match1.camelCaseWeight, match2.camelCaseWeight + 1, 1 << 30);
});
it("PreferCaseSensitiveRelativeWeights2", () => {
const match1 = getFirstMatch("BazBarFooFooFoo", "FFF");
const match2 = getFirstMatch("BazFogBarFooFoo", "FFF");
// Contiguous things should also be preferred
assertInRange(match1.camelCaseWeight, match2.camelCaseWeight + 1, 1 << 30);
});
it("PreferCaseSensitiveRelativeWeights3", () => {
const match1 = getFirstMatch("FogBarFooFoo", "FFF");
const match2 = getFirstMatch("BarFooFooFoo", "FFF");
// The weight of being first should be greater than the weight of being contiguous
assertInRange(match1.camelCaseWeight, match2.camelCaseWeight + 1, 1 << 30);
});
it("AllLowerPattern1", () => {
const match = getFirstMatch("FogBarChangedEventArgs", "changedeventargs");
assert.isTrue(undefined !== match);
});
it("AllLowerPattern2", () => {
const match = getFirstMatch("FogBarChangedEventArgs", "changedeventarrrgh");
assert.isTrue(undefined === match);
});
it("AllLowerPattern3", () => {
const match = getFirstMatch("ABCDEFGH", "bcd");
assert.isTrue(undefined !== match);
});
it("AllLowerPattern4", () => {
const match = getFirstMatch("AbcdefghijEfgHij", "efghij");
assert.isTrue(undefined === match);
});
});
describe("MultiWordPattern", () => {
it("ExactWithLowercase", () => {
const matches = getAllMatches("AddMetadataReference", "addmetadatareference");
assertContainsKind(ts.PatternMatchKind.exact, matches);
});
it("SingleLowercasedSearchWord1", () => {
const matches = getAllMatches("AddMetadataReference", "add");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
});
it("SingleLowercasedSearchWord2", () => {
const matches = getAllMatches("AddMetadataReference", "metadata");
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("SingleUppercaseSearchWord1", () => {
const matches = getAllMatches("AddMetadataReference", "Add");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
});
it("SingleUppercaseSearchWord2", () => {
const matches = getAllMatches("AddMetadataReference", "Metadata");
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("SingleUppercaseSearchLetter1", () => {
const matches = getAllMatches("AddMetadataReference", "A");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
});
it("SingleUppercaseSearchLetter2", () => {
const matches = getAllMatches("AddMetadataReference", "M");
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "add metadata");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "A M");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "AM");
assertContainsKind(ts.PatternMatchKind.camelCase, matches);
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "ref Metadata");
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "ref M");
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
});
it("MixedCamelCase", () => {
const matches = getAllMatches("AddMetadataReference", "AMRe");
assertContainsKind(ts.PatternMatchKind.camelCase, matches);
});
it("BlankPattern", () => {
const matches = getAllMatches("AddMetadataReference", "");
assert.isTrue(matches === undefined);
});
it("WhitespaceOnlyPattern", () => {
const matches = getAllMatches("AddMetadataReference", " ");
assert.isTrue(matches === undefined);
});
it("EachWordSeparately1", () => {
const matches = getAllMatches("AddMetadataReference", "add Meta");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("EachWordSeparately2", () => {
const matches = getAllMatches("AddMetadataReference", "Add meta");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("EachWordSeparately3", () => {
const matches = getAllMatches("AddMetadataReference", "Add Meta");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
});
it("MixedCasing", () => {
const matches = getAllMatches("AddMetadataReference", "mEta");
assert.isTrue(matches === undefined);
});
it("MixedCasing2", () => {
const matches = getAllMatches("AddMetadataReference", "Data");
assert.isTrue(matches === undefined);
});
it("AsteriskSplit", () => {
const matches = getAllMatches("GetKeyWord", "K*W");
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
});
it("LowercaseSubstring1", () => {
const matches = getAllMatches("Operator", "a");
assert.isTrue(matches === undefined);
});
it("LowercaseSubstring2", () => {
const matches = getAllMatches("FooAttribute", "a");
assertContainsKind(ts.PatternMatchKind.substring, matches);
assert.isFalse(matches[0].isCaseSensitive);
});
});
describe("DottedPattern", () => {
it("DottedPattern1", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "B.Q");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("DottedPattern2", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "C.Q");
assert.isTrue(match === undefined);
});
it("DottedPattern3", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "B.B.Q");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("DottedPattern4", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "Baz.Quux");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("DottedPattern5", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "F.B.B.Quux");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(true, match.isCaseSensitive);
});
it("DottedPattern6", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "F.F.B.B.Quux");
assert.isTrue(match === undefined);
});
it("DottedPattern7", () => {
let match = getFirstMatch("UIElement", "UIElement");
match = getFirstMatch("GetKeyword", "UIElement");
assert.isTrue(match === undefined);
});
});
function getFirstMatch(candidate: string, pattern: string): ts.PatternMatch {
const matches = ts.createPatternMatcher(pattern).getMatchesForLastSegmentOfPattern(candidate);
return matches ? matches[0] : undefined;
}
function getAllMatches(candidate: string, pattern: string): ts.PatternMatch[] {
return ts.createPatternMatcher(pattern).getMatchesForLastSegmentOfPattern(candidate);
}
function getFirstMatchForDottedPattern(dottedContainer: string, candidate: string, pattern: string): ts.PatternMatch {
const matches = ts.createPatternMatcher(pattern).getMatches(dottedContainer.split("."), candidate);
return matches ? matches[0] : undefined;
}
function spanListToSubstrings(identifier: string, spans: ts.TextSpan[]) {
return ts.map(spans, s => identifier.substr(s.start, s.length));
}
function breakIntoCharacterSpans(identifier: string) {
return spanListToSubstrings(identifier, ts.breakIntoCharacterSpans(identifier));
}
function breakIntoWordSpans(identifier: string) {
return spanListToSubstrings(identifier, ts.breakIntoWordSpans(identifier));
}
function assertArrayEquals(array1: T[], array2: T[]) {
assert.equal(array1.length, array2.length);
for (let i = 0, n = array1.length; i < n; i++) {
assert.equal(array1[i], array2[i]);
}
}
function assertInRange(val: number, low: number, high: number) {
assert.isTrue(val >= low);
assert.isTrue(val <= high);
}
function verifyBreakIntoCharacterSpans(original: string, ...parts: string[]): void {
assertArrayEquals(parts, breakIntoCharacterSpans(original));
}
function verifyBreakIntoWordSpans(original: string, ...parts: string[]): void {
assertArrayEquals(parts, breakIntoWordSpans(original));
}
function assertContainsKind(kind: ts.PatternMatchKind, results: ts.PatternMatch[]) {
assert.isTrue(ts.forEach(results, r => r.kind === kind));
}
});