diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index 11edbe95920..b3f72d83cb3 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -26,8 +26,12 @@ module ts.NavigateTo { // It was a match! If the pattern has dots in it, then also see if hte // declaration container matches as well. if (patternMatcher.patternContainsDots) { - var containerName = getContainerName(getContainerNode(declaration)); - matches = patternMatcher.getMatches(name, containerName); + var containers = getContainers(declaration); + if (!containers) { + return undefined; + } + + matches = patternMatcher.getMatches(containers, name); if (!matches) { continue; @@ -36,7 +40,7 @@ module ts.NavigateTo { var fileName = sourceFile.fileName; var matchKind = bestMatchKind(matches); - rawItems.push({ name, fileName, matchKind, isCaseSensitive: isCaseSensitive(matches), declaration }); + rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration }); } } }); @@ -50,7 +54,7 @@ module ts.NavigateTo { return items; - function isCaseSensitive(matches: PatternMatch[]): boolean { + function allMatchesAreCaseSensitive(matches: PatternMatch[]): boolean { Debug.assert(matches.length > 0); // This is a case sensitive match, only if all the submatches were case sensitive. @@ -64,26 +68,99 @@ module ts.NavigateTo { } function getDeclarationName(declaration: Declaration): string { - if (declaration.name.kind === SyntaxKind.Identifier || - declaration.name.kind === SyntaxKind.StringLiteral || - declaration.name.kind === SyntaxKind.NumericLiteral) { + var result = getTextOfIdentifierOrLiteral(declaration.name); + if (result !== undefined) { + return result; + } - return (declaration.name).text; + if (declaration.name.kind === SyntaxKind.ComputedPropertyName) { + var expr = (declaration.name).expression; + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + return (expr).name.text; + } + + return getTextOfIdentifierOrLiteral(expr); } return undefined; } - function getContainerName(declaration: Declaration): string { - var name = getDeclarationName(declaration); - if (name === undefined) { - return undefined; + function getTextOfIdentifierOrLiteral(node: Node) { + if (node.kind === SyntaxKind.Identifier || + node.kind === SyntaxKind.StringLiteral || + node.kind === SyntaxKind.NumericLiteral) { + + return (node).text; } - var container = getContainerNode(declaration); - return container && container.name - ? getContainerName(container) + "." + name - : name; + return undefined; + } + + function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) { + if (declaration && declaration.name) { + var text = getTextOfIdentifierOrLiteral(declaration.name); + if (text !== undefined) { + containers.unshift(text); + } + else if (declaration.name.kind === SyntaxKind.ComputedPropertyName) { + return tryAddComputedPropertyName((declaration.name).expression, containers, /*includeLastPortion:*/ true); + } + else { + // Don't know how to add this. + return false + } + } + + return true; + } + + // Only added the names of computed properties if they're simple dotted expressions, like: + // + // [X.Y.Z]() { } + function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean { + var text = getTextOfIdentifierOrLiteral(expression); + if (text !== undefined) { + if (includeLastPortion) { + containers.unshift(text); + } + return true; + } + + if (expression.kind === SyntaxKind.PropertyAccessExpression) { + var propertyAccess = expression; + if (includeLastPortion) { + containers.unshift(propertyAccess.name.text); + } + + return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion:*/ true); + } + + return false; + } + + function getContainers(declaration: Declaration) { + var containers: string[] = []; + + // First, if we started with a computed property name, then add all but the last + // portion into the container array. + if (declaration.name.kind === SyntaxKind.ComputedPropertyName) { + if (!tryAddComputedPropertyName((declaration.name).expression, containers, /*includeLastPortion:*/ false)) { + return undefined; + } + } + + // Now, walk up our containers, adding all their names to the container array. + declaration = getContainerNode(declaration); + + while (declaration) { + if (!tryAddSingleDeclarationName(declaration, containers)) { + return undefined; + } + + declaration = getContainerNode(declaration); + } + + return containers; } function bestMatchKind(matches: PatternMatch[]) { diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index b19ee8fcd2c..9bcd3e1d000 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -46,7 +46,7 @@ module ts { // 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(candidate: string, dottedContainer: string): PatternMatch[]; + getMatches(candidateContainers: string[], candidate: string): PatternMatch[]; // 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. @@ -139,7 +139,7 @@ module ts { return matchSegment(candidate, lastOrUndefined(dotSeparatedSegments)); } - function getMatches(candidate: string, dottedContainer: string): PatternMatch[] { + function getMatches(candidateContainers: string[], candidate: string): PatternMatch[] { if (skipMatch(candidate)) { return undefined; } @@ -152,12 +152,11 @@ module ts { return undefined; } - dottedContainer = dottedContainer || ""; - var containerParts = dottedContainer.split("."); + candidateContainers = candidateContainers || []; // -1 because the last part was checked against the name, and only the rest // of the parts are checked against the container. - if (dotSeparatedSegments.length - 1 > containerParts.length) { + if (dotSeparatedSegments.length - 1 > candidateContainers.length) { // There weren't enough container parts to match against the pattern parts. // So this definitely doesn't match. return undefined; @@ -167,12 +166,12 @@ module ts { // the dotted parts match up correctly. var totalMatch = candidateMatch; - for (var i = dotSeparatedSegments.length - 2, j = containerParts.length - 1; + for (var i = dotSeparatedSegments.length - 2, j = candidateContainers.length - 1; i >= 0; i--, j--) { var segment = dotSeparatedSegments[i]; - var containerName = containerParts[j]; + var containerName = candidateContainers[j]; var containerMatch = matchSegment(containerName, segment); if (!containerMatch) { diff --git a/tests/cases/unittests/services/patternMatcher.ts b/tests/cases/unittests/services/patternMatcher.ts index 1af64545db8..21f62507f4a 100644 --- a/tests/cases/unittests/services/patternMatcher.ts +++ b/tests/cases/unittests/services/patternMatcher.ts @@ -96,7 +96,6 @@ describe('PatternMatcher', function () { describe("SingleWordPattern", () => { it("PreferCaseSensitiveExact", () => { - debugger; var match = getFirstMatch("Foo", "Foo"); assert.equal(ts.PatternMatchKind.exact, match.kind); @@ -125,7 +124,6 @@ describe('PatternMatcher', function () { }); it("PreferCaseSensitiveCamelCaseMatchSimple", () => { - debugger; var match = getFirstMatch("FogBar", "FB"); assert.equal(ts.PatternMatchKind.camelCase, match.kind); @@ -172,7 +170,6 @@ describe('PatternMatcher', function () { }); it("PreferCaseSensitiveLowercasePattern", () => { - debugger; var match = getFirstMatch("FogBar", "b"); assert.equal(ts.PatternMatchKind.substring, match.kind); @@ -266,7 +263,6 @@ describe('PatternMatcher', function () { }); it("AllLowerPattern1", () => { - debugger; var match = getFirstMatch("FogBarChangedEventArgs", "changedeventargs"); assert.isTrue(undefined !== match); @@ -473,7 +469,6 @@ describe('PatternMatcher', function () { }); it("DottedPattern7", () => { - debugger; var match = getFirstMatch("UIElement", "UIElement"); var match = getFirstMatch("GetKeyword", "UIElement"); assert.isTrue(match === undefined); @@ -490,7 +485,7 @@ describe('PatternMatcher', function () { } function getFirstMatchForDottedPattern(dottedContainer: string, candidate: string, pattern: string): ts.PatternMatch { - var matches = ts.createPatternMatcher(pattern).getMatches(candidate, dottedContainer); + var matches = ts.createPatternMatcher(pattern).getMatches(dottedContainer.split("."), candidate); return matches ? matches[0] : undefined; }