mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-24 04:30:53 -06:00
patternMatcher: Return single best match instead of list (#23166)
This commit is contained in:
parent
f6b206a75a
commit
28455c65b3
@ -28,13 +28,11 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
function shouldIgnoreCalledExpression(expression: ts.Expression): boolean {
|
||||
if (expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
||||
const methodName = (expression as ts.PropertyAccessExpression).name.text;
|
||||
if (methodName.indexOf("set") === 0) {
|
||||
if (methodName.startsWith("set") || methodName.startsWith("assert")) {
|
||||
return true;
|
||||
}
|
||||
switch (methodName) {
|
||||
case "apply":
|
||||
case "assert":
|
||||
case "assertEqual":
|
||||
case "call":
|
||||
case "equal":
|
||||
case "fail":
|
||||
@ -46,11 +44,10 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
}
|
||||
else if (expression.kind === ts.SyntaxKind.Identifier) {
|
||||
const functionName = (expression as ts.Identifier).text;
|
||||
if (functionName.indexOf("set") === 0) {
|
||||
if (functionName.startsWith("set") || functionName.startsWith("assert")) {
|
||||
return true;
|
||||
}
|
||||
switch (functionName) {
|
||||
case "assert":
|
||||
case "contains":
|
||||
case "createAnonymousType":
|
||||
case "createImportSpecifier":
|
||||
|
||||
@ -1724,6 +1724,10 @@ namespace ts {
|
||||
return compareComparableValues(a, b);
|
||||
}
|
||||
|
||||
export function min<T>(a: T, b: T, compare: Comparer<T>): T {
|
||||
return compare(a, b) === Comparison.LessThan ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two strings using a case-insensitive ordinal comparison.
|
||||
*
|
||||
|
||||
@ -95,251 +95,157 @@ describe("PatternMatcher", () => {
|
||||
|
||||
describe("SingleWordPattern", () => {
|
||||
it("PreferCaseSensitiveExact", () => {
|
||||
const match = getFirstMatch("Foo", "Foo");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.exact, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("Foo", "Foo", { kind: ts.PatternMatchKind.exact, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveExactInsensitive", () => {
|
||||
const match = getFirstMatch("foo", "Foo");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.exact, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("foo", "Foo", { kind: ts.PatternMatchKind.exact, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitivePrefix", () => {
|
||||
const match = getFirstMatch("Foo", "Fo");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.prefix, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("Foo", "Fo", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitivePrefixCaseInsensitive", () => {
|
||||
const match = getFirstMatch("Foo", "fo");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.prefix, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("Foo", "fo", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveCamelCaseMatchSimple", () => {
|
||||
const match = getFirstMatch("FogBar", "FB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("FogBar", "FB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveCamelCaseMatchPartialPattern", () => {
|
||||
const match = getFirstMatch("FogBar", "FoB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("FogBar", "FoB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveCamelCaseMatchToLongPattern1", () => {
|
||||
const match = getFirstMatch("FogBar", "FBB");
|
||||
|
||||
assert.isTrue(match === undefined);
|
||||
assertSegmentMatch("FogBar", "FBB", undefined);
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveCamelCaseMatchToLongPattern2", () => {
|
||||
const match = getFirstMatch("FogBar", "FoooB");
|
||||
|
||||
assert.isTrue(match === undefined);
|
||||
assertSegmentMatch("FogBar", "FoooB", undefined);
|
||||
});
|
||||
|
||||
it("CamelCaseMatchPartiallyUnmatched", () => {
|
||||
const match = getFirstMatch("FogBarBaz", "FZ");
|
||||
|
||||
assert.isTrue(match === undefined);
|
||||
assertSegmentMatch("FogBarBaz", "FZ", undefined);
|
||||
});
|
||||
|
||||
it("CamelCaseMatchCompletelyUnmatched", () => {
|
||||
const match = getFirstMatch("FogBarBaz", "ZZ");
|
||||
|
||||
assert.isTrue(match === undefined);
|
||||
assertSegmentMatch("FogBarBaz", "ZZ", undefined);
|
||||
});
|
||||
|
||||
it("TwoUppercaseCharacters", () => {
|
||||
const match = getFirstMatch("SimpleUIElement", "SiUI");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("SimpleUIElement", "SiUI", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveLowercasePattern", () => {
|
||||
const match = getFirstMatch("FogBar", "b");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.substring, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("FogBar", "b", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveLowercasePattern2", () => {
|
||||
const match = getFirstMatch("FogBar", "fB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("FogBar", "fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveTryUnderscoredName", () => {
|
||||
const match = getFirstMatch("_fogBar", "_fB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("_fogBar", "_fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveTryUnderscoredName2", () => {
|
||||
const match = getFirstMatch("_fogBar", "fB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("_fogBar", "fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveTryUnderscoredNameInsensitive", () => {
|
||||
const match = getFirstMatch("_FogBar", "_fB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("_FogBar", "_fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveMiddleUnderscore", () => {
|
||||
const match = getFirstMatch("Fog_Bar", "FB");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("Fog_Bar", "FB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveMiddleUnderscore2", () => {
|
||||
const match = getFirstMatch("Fog_Bar", "F_B");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertSegmentMatch("Fog_Bar", "F_B", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveMiddleUnderscore3", () => {
|
||||
const match = getFirstMatch("Fog_Bar", "F__B");
|
||||
|
||||
assert.isTrue(undefined === match);
|
||||
assertSegmentMatch("Fog_Bar", "F__B", undefined);
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveMiddleUnderscore4", () => {
|
||||
const match = getFirstMatch("Fog_Bar", "f_B");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("Fog_Bar", "f_B", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("PreferCaseSensitiveMiddleUnderscore5", () => {
|
||||
const match = getFirstMatch("Fog_Bar", "F_b");
|
||||
|
||||
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
|
||||
assert.equal(false, match.isCaseSensitive);
|
||||
assertSegmentMatch("Fog_Bar", "F_b", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("AllLowerPattern1", () => {
|
||||
const match = getFirstMatch("FogBarChangedEventArgs", "changedeventargs");
|
||||
|
||||
assert.isTrue(undefined !== match);
|
||||
assertSegmentMatch("FogBarChangedEventArgs", "changedeventargs", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("AllLowerPattern2", () => {
|
||||
const match = getFirstMatch("FogBarChangedEventArgs", "changedeventarrrgh");
|
||||
|
||||
assert.isTrue(undefined === match);
|
||||
assertSegmentMatch("FogBarChangedEventArgs", "changedeventarrrgh", undefined);
|
||||
});
|
||||
|
||||
it("AllLowerPattern3", () => {
|
||||
const match = getFirstMatch("ABCDEFGH", "bcd");
|
||||
|
||||
assert.isTrue(undefined !== match);
|
||||
assertSegmentMatch("ABCDEFGH", "bcd", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("AllLowerPattern4", () => {
|
||||
const match = getFirstMatch("AbcdefghijEfgHij", "efghij");
|
||||
|
||||
assert.isTrue(undefined === match);
|
||||
assertSegmentMatch("AbcdefghijEfgHij", "efghij", undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("MultiWordPattern", () => {
|
||||
it("ExactWithLowercase", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "addmetadatareference");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.exact, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "addmetadatareference", { kind: ts.PatternMatchKind.exact, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("SingleLowercasedSearchWord1", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "add");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "add", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("SingleLowercasedSearchWord2", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "metadata");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "metadata", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("SingleUppercaseSearchWord1", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "Add");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "Add", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("SingleUppercaseSearchWord2", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "Metadata");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "Metadata", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("SingleUppercaseSearchLetter1", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "A");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "A", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("SingleUppercaseSearchLetter2", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "M");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "M", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("TwoLowercaseWords", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "add metadata");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
it("TwoLowercaseWords0", () => {
|
||||
assertSegmentMatch("AddMetadataReference", "add metadata", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("TwoLowercaseWords", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "A M");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
it("TwoLowercaseWords1", () => {
|
||||
assertSegmentMatch("AddMetadataReference", "A M", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("TwoLowercaseWords", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "AM");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.camelCase, matches);
|
||||
it("TwoLowercaseWords2", () => {
|
||||
assertSegmentMatch("AddMetadataReference", "AM", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("TwoLowercaseWords", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "ref Metadata");
|
||||
|
||||
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
|
||||
it("TwoLowercaseWords3", () => {
|
||||
assertSegmentMatch("AddMetadataReference", "ref Metadata", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("TwoLowercaseWords", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "ref M");
|
||||
|
||||
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
|
||||
it("TwoLowercaseWords4", () => {
|
||||
assertSegmentMatch("AddMetadataReference", "ref M", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("MixedCamelCase", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "AMRe");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.camelCase, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "AMRe", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("BlankPattern", () => {
|
||||
@ -351,120 +257,83 @@ describe("PatternMatcher", () => {
|
||||
});
|
||||
|
||||
it("EachWordSeparately1", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "add Meta");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "add Meta", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
|
||||
});
|
||||
|
||||
it("EachWordSeparately2", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "Add meta");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "Add meta", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("EachWordSeparately3", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "Add Meta");
|
||||
|
||||
assertContainsKind(ts.PatternMatchKind.prefix, matches);
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assertSegmentMatch("AddMetadataReference", "Add Meta", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("MixedCasing", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "mEta");
|
||||
|
||||
assert.isTrue(matches === undefined);
|
||||
assertSegmentMatch("AddMetadataReference", "mEta", undefined);
|
||||
});
|
||||
|
||||
it("MixedCasing2", () => {
|
||||
const matches = getAllMatches("AddMetadataReference", "Data");
|
||||
|
||||
assert.isTrue(matches === undefined);
|
||||
assertSegmentMatch("AddMetadataReference", "Data", undefined);
|
||||
});
|
||||
|
||||
it("AsteriskSplit", () => {
|
||||
const matches = getAllMatches("GetKeyWord", "K*W");
|
||||
|
||||
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
|
||||
assertSegmentMatch("GetKeyWord", "K*W", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("LowercaseSubstring1", () => {
|
||||
const matches = getAllMatches("Operator", "a");
|
||||
|
||||
assert.isTrue(matches === undefined);
|
||||
assertSegmentMatch("Operator", "a", undefined);
|
||||
});
|
||||
|
||||
it("LowercaseSubstring2", () => {
|
||||
const matches = getAllMatches("FooAttribute", "a");
|
||||
assertContainsKind(ts.PatternMatchKind.substring, matches);
|
||||
assert.isFalse(matches[0].isCaseSensitive);
|
||||
assertSegmentMatch("FooAttribute", "a", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
assertFullMatch("Foo.Bar.Baz", "Quux", "B.Q", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("DottedPattern2", () => {
|
||||
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "C.Q");
|
||||
assert.isTrue(match === undefined);
|
||||
assertFullMatch("Foo.Bar.Baz", "Quux", "C.Q", 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);
|
||||
assertFullMatch("Foo.Bar.Baz", "Quux", "B.B.Q", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("DottedPattern4", () => {
|
||||
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "Baz.Quux");
|
||||
assert.equal(ts.PatternMatchKind.exact, match.kind);
|
||||
assert.equal(true, match.isCaseSensitive);
|
||||
assertFullMatch("Foo.Bar.Baz", "Quux", "Baz.Quux", { kind: ts.PatternMatchKind.exact, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
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);
|
||||
assertFullMatch("Foo.Bar.Baz", "Quux", "F.B.B.Quux", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
|
||||
});
|
||||
|
||||
it("DottedPattern6", () => {
|
||||
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "F.F.B.B.Quux");
|
||||
assert.isTrue(match === undefined);
|
||||
assertFullMatch("Foo.Bar.Baz", "Quux", "F.F.B.B.Quux", undefined);
|
||||
});
|
||||
|
||||
it("DottedPattern7", () => {
|
||||
let match = getFirstMatch("UIElement", "UIElement");
|
||||
match = getFirstMatch("GetKeyword", "UIElement");
|
||||
assert.isTrue(match === undefined);
|
||||
assertSegmentMatch("UIElement", "UIElement", { kind: ts.PatternMatchKind.exact, isCaseSensitive: true });
|
||||
assertSegmentMatch("GetKeyword", "UIElement", undefined);
|
||||
});
|
||||
});
|
||||
|
||||
function assertSegmentMatch(candidate: string, pattern: string, expected: ts.PatternMatch | undefined): void {
|
||||
assert.deepEqual(ts.createPatternMatcher(pattern).getMatchForLastSegmentOfPattern(candidate), expected);
|
||||
}
|
||||
|
||||
function assertInvalidPattern(pattern: string) {
|
||||
assert.equal(ts.createPatternMatcher(pattern), 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 assertFullMatch(dottedContainer: string, candidate: string, pattern: string, expected: ts.PatternMatch | undefined): void {
|
||||
assert.deepEqual(ts.createPatternMatcher(pattern).getFullMatch(dottedContainer.split("."), candidate), expected);
|
||||
}
|
||||
|
||||
function spanListToSubstrings(identifier: string, spans: ts.TextSpan[]) {
|
||||
return ts.map(spans, s => identifier.substr(s.start, s.length));
|
||||
return spans.map(s => identifier.substr(s.start, s.length));
|
||||
}
|
||||
|
||||
function breakIntoCharacterSpans(identifier: string) {
|
||||
@ -474,23 +343,12 @@ describe("PatternMatcher", () => {
|
||||
function breakIntoWordSpans(identifier: string) {
|
||||
return spanListToSubstrings(identifier, ts.breakIntoWordSpans(identifier));
|
||||
}
|
||||
function assertArrayEquals<T>(array1: T[], array2: T[]) {
|
||||
assert.equal(array1.length, array2.length);
|
||||
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
assert.equal(array1[i], array2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyBreakIntoCharacterSpans(original: string, ...parts: string[]): void {
|
||||
assertArrayEquals(parts, breakIntoCharacterSpans(original));
|
||||
assert.deepEqual(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));
|
||||
assert.deepEqual(parts, breakIntoWordSpans(original));
|
||||
}
|
||||
});
|
||||
|
||||
@ -36,28 +36,24 @@ namespace ts.NavigateTo {
|
||||
function getItemsFromNamedDeclaration(patternMatcher: PatternMatcher, name: string, declarations: ReadonlyArray<Declaration>, checker: TypeChecker, fileName: string, rawItems: Push<RawNavigateToItem>): void {
|
||||
// First do a quick check to see if the name of the declaration matches the
|
||||
// last portion of the (possibly) dotted name they're searching for.
|
||||
const matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
|
||||
|
||||
if (!matches) {
|
||||
const match = patternMatcher.getMatchForLastSegmentOfPattern(name);
|
||||
if (!match) {
|
||||
return; // continue to next named declarations
|
||||
}
|
||||
|
||||
for (const declaration of declarations) {
|
||||
if (!shouldKeepItem(declaration, checker)) {
|
||||
continue;
|
||||
}
|
||||
if (!shouldKeepItem(declaration, checker)) continue;
|
||||
|
||||
// It was a match! If the pattern has dots in it, then also see if the
|
||||
// declaration container matches as well.
|
||||
let containerMatches = matches;
|
||||
if (patternMatcher.patternContainsDots) {
|
||||
containerMatches = patternMatcher.getMatches(getContainers(declaration), name);
|
||||
if (!containerMatches) {
|
||||
continue;
|
||||
const fullMatch = patternMatcher.getFullMatch(getContainers(declaration), name);
|
||||
if (fullMatch) {
|
||||
rawItems.push({ name, fileName, matchKind: fullMatch.kind, isCaseSensitive: fullMatch.isCaseSensitive, declaration });
|
||||
}
|
||||
}
|
||||
|
||||
rawItems.push({ name, fileName, matchKind: Math.min(...matches.map(m => m.kind)), isCaseSensitive: matches.every(m => m.isCaseSensitive), declaration });
|
||||
else {
|
||||
// If the pattern has dots in it, then also see if the declaration container matches as well.
|
||||
rawItems.push({ name, fileName, matchKind: match.kind, isCaseSensitive: match.isCaseSensitive, declaration });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,12 +33,12 @@ namespace ts {
|
||||
// this will return a successful match, having only tested "SK" against "SyntaxKind". At
|
||||
// that point a call can be made to 'getMatches("SyntaxKind", "ts.compiler")', with the
|
||||
// work to create 'ts.compiler' only being done once the first match succeeded.
|
||||
getMatchesForLastSegmentOfPattern(candidate: string): PatternMatch[];
|
||||
getMatchForLastSegmentOfPattern(candidate: string): PatternMatch | undefined;
|
||||
|
||||
// Fully checks a candidate, with an dotted container, against the search pattern.
|
||||
// The candidate must match the last part of the search pattern, and the dotted container
|
||||
// must match the preceding segments of the pattern.
|
||||
getMatches(candidateContainers: string[], candidate: string): PatternMatch[] | undefined;
|
||||
getFullMatch(candidateContainers: ReadonlyArray<string>, candidate: string): PatternMatch | undefined;
|
||||
|
||||
// Whether or not the pattern contained dots or not. Clients can use this to determine
|
||||
// If they should call getMatches, or if getMatchesForLastSegmentOfPattern is sufficient.
|
||||
@ -109,13 +109,13 @@ namespace ts {
|
||||
if (dotSeparatedSegments.some(segment => !segment.subWordTextChunks.length)) return undefined;
|
||||
|
||||
return {
|
||||
getMatches: (containers, candidate) => getMatches(containers, candidate, dotSeparatedSegments, stringToWordSpans),
|
||||
getMatchesForLastSegmentOfPattern: candidate => matchSegment(candidate, last(dotSeparatedSegments), stringToWordSpans),
|
||||
getFullMatch: (containers, candidate) => getFullMatch(containers, candidate, dotSeparatedSegments, stringToWordSpans),
|
||||
getMatchForLastSegmentOfPattern: candidate => matchSegment(candidate, last(dotSeparatedSegments), stringToWordSpans),
|
||||
patternContainsDots: dotSeparatedSegments.length > 1
|
||||
};
|
||||
}
|
||||
|
||||
function getMatches(candidateContainers: ReadonlyArray<string>, candidate: string, dotSeparatedSegments: ReadonlyArray<Segment>, stringToWordSpans: Map<TextSpan[]>): PatternMatch[] | undefined {
|
||||
function getFullMatch(candidateContainers: ReadonlyArray<string>, candidate: string, dotSeparatedSegments: ReadonlyArray<Segment>, stringToWordSpans: Map<TextSpan[]>): PatternMatch | undefined {
|
||||
// First, check that the last part of the dot separated pattern matches the name of the
|
||||
// candidate. If not, then there's no point in proceeding and doing the more
|
||||
// expensive work.
|
||||
@ -134,29 +134,13 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// So far so good. Now break up the container for the candidate and check if all
|
||||
// the dotted parts match up correctly.
|
||||
const totalMatch = candidateMatch;
|
||||
|
||||
let bestMatch: PatternMatch | undefined;
|
||||
for (let i = dotSeparatedSegments.length - 2, j = candidateContainers.length - 1;
|
||||
i >= 0;
|
||||
i -= 1, j -= 1) {
|
||||
|
||||
const segment = dotSeparatedSegments[i];
|
||||
const containerName = candidateContainers[j];
|
||||
|
||||
const containerMatch = matchSegment(containerName, segment, stringToWordSpans);
|
||||
if (!containerMatch) {
|
||||
// This container didn't match the pattern piece. So there's no match at all.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
addRange(totalMatch, containerMatch);
|
||||
bestMatch = betterMatch(bestMatch, matchSegment(candidateContainers[j], dotSeparatedSegments[i], stringToWordSpans));
|
||||
}
|
||||
|
||||
// Success, this symbol's full name matched against the dotted name the user was asking
|
||||
// about.
|
||||
return totalMatch;
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
function getWordSpans(word: string, stringToWordSpans: Map<TextSpan[]>): TextSpan[] {
|
||||
@ -219,7 +203,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function matchSegment(candidate: string, segment: Segment, stringToWordSpans: Map<TextSpan[]>): PatternMatch[] {
|
||||
function matchSegment(candidate: string, segment: Segment, stringToWordSpans: Map<TextSpan[]>): PatternMatch {
|
||||
// First check if the segment matches as is. This is also useful if the segment contains
|
||||
// characters we would normally strip when splitting into parts that we also may want to
|
||||
// match in the candidate. For example if the segment is "@int" and the candidate is
|
||||
@ -229,9 +213,7 @@ namespace ts {
|
||||
// multi-word segment.
|
||||
if (every(segment.totalTextChunk.text, ch => ch !== CharacterCodes.space && ch !== CharacterCodes.asterisk)) {
|
||||
const match = matchTextChunk(candidate, segment.totalTextChunk, stringToWordSpans);
|
||||
if (match) {
|
||||
return [match];
|
||||
}
|
||||
if (match) return match;
|
||||
}
|
||||
|
||||
// The logic for pattern matching is now as follows:
|
||||
@ -271,20 +253,19 @@ namespace ts {
|
||||
// Only if all words have some sort of match is the pattern considered matched.
|
||||
|
||||
const subWordTextChunks = segment.subWordTextChunks;
|
||||
let matches: PatternMatch[];
|
||||
|
||||
let bestMatch: PatternMatch | undefined;
|
||||
for (const subWordTextChunk of subWordTextChunks) {
|
||||
// Try to match the candidate with this word
|
||||
const result = matchTextChunk(candidate, subWordTextChunk, stringToWordSpans);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
matches = matches || [];
|
||||
matches.push(result);
|
||||
bestMatch = betterMatch(bestMatch, matchTextChunk(candidate, subWordTextChunk, stringToWordSpans));
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
return matches;
|
||||
function betterMatch(a: PatternMatch | undefined, b: PatternMatch | undefined): PatternMatch {
|
||||
return min(a, b, compareMatches);
|
||||
}
|
||||
function compareMatches(a: PatternMatch | undefined, b: PatternMatch | undefined): Comparison {
|
||||
return a === undefined ? Comparison.GreaterThan : b === undefined ? Comparison.LessThan
|
||||
: compareValues(a.kind, b.kind) || compareBooleans(!a.isCaseSensitive, !b.isCaseSensitive);
|
||||
}
|
||||
|
||||
function partStartsWith(candidate: string, candidateSpan: TextSpan, pattern: string, ignoreCase: boolean, patternSpan: TextSpan = { start: 0, length: pattern.length }): boolean {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user