From 88f37e579cc495feb6ee7a2f6d593695bf8bd038 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 26 Aug 2014 17:24:11 -0700 Subject: [PATCH] Support for switch/case/default/break in getOccs --- src/services/services.ts | 86 +++++++++++++++++-- .../getOccurrencesSwitchCaseDefault.ts | 29 +++++++ .../getOccurrencesSwitchCaseDefault2.ts | 29 +++++++ .../getOccurrencesSwitchCaseDefaultBroken.ts | 47 ++++++++++ ...etOccurrencesSwitchCaseDefaultNegatives.ts | 25 ++++++ 5 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts create mode 100644 tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts create mode 100644 tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts create mode 100644 tests/cases/fourslash/getOccurrencesSwitchCaseDefaultNegatives.ts diff --git a/src/services/services.ts b/src/services/services.ts index a8935c463ab..0be021a32ac 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2162,22 +2162,35 @@ module ts { return undefined; } - if (node.kind === SyntaxKind.Identifier || - isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || - isNameOfExternalModuleImportOrDeclaration(node)) { + if (node.kind === SyntaxKind.Identifier || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) { return getReferencesForNode(node, [sourceFile]); } + var result: ReferenceEntry[]; + + // Each of these helper functions bails out if the node is undefined, + // which is why you'll see much of this'node.parent && node.parent.parent' pattern. switch (node.kind) { case SyntaxKind.TryKeyword: case SyntaxKind.CatchKeyword: case SyntaxKind.FinallyKeyword: - return getTryCatchFinallyOccurrences((node.parent && node.parent.parent)); + result = getTryCatchFinallyOccurrences((node.parent && node.parent.parent)); + break; + case SyntaxKind.SwitchKeyword: + result = getSwitchCaseDefaultOccurrences(node.parent); + break; + case SyntaxKind.CaseKeyword: + case SyntaxKind.DefaultKeyword: + result = getSwitchCaseDefaultOccurrences((node.parent && node.parent.parent)); + break; + case SyntaxKind.BreakKeyword: + result = getBreakStatementOccurences(node.parent); + } - return undefined; + return result; - function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[]{ + function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] { if (!tryStatement || tryStatement.kind !== SyntaxKind.TryStatement) { return undefined; } @@ -2197,6 +2210,67 @@ module ts { return keywordsToReferenceEntries(keywords); } + function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) { + if (!switchStatement || switchStatement.kind !== SyntaxKind.SwitchStatement) { + return undefined; + } + + var keywords: Node[] = []; + + pushIfKeyword(keywords, switchStatement.getFirstToken()); + + // Go through each clause in the switch statement, collecting the clause keywords. + switchStatement.clauses.forEach(clause => { + pushIfKeyword(keywords, clause.getFirstToken()); + + // For each clause, also recursively traverse the statements where we can find analogous breaks. + forEachChild(clause, function aggregateBreakKeywords(node: Node): void { + switch (node.kind) { + case SyntaxKind.BreakStatement: + // If the break statement has a label, cannot be part of + if (!(node).label) { + pushIfKeyword(keywords, node.getFirstToken()); + } + // Fall through + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.SwitchStatement: + return; + } + + forEachChild(node, aggregateBreakKeywords); + }); + }); + + return keywordsToReferenceEntries(keywords); + } + + function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{ + if (!breakStatement || breakStatement.kind !== SyntaxKind.BreakStatement) { + return undefined; + } + + // TODO (drosen): Deal with labeled statements. + if (breakStatement.label) { + return undefined; + } + + for (var owner = node.parent; owner; owner = owner.parent) { + switch (owner.kind) { + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + // TODO (drosen): Handle loops! + return undefined; + case SyntaxKind.SwitchStatement: + return getSwitchCaseDefaultOccurrences(owner); + } + } + } + function pushIfKeyword(keywordList: Node[], token: Node) { if (token && isKeyword(token.kind)) { keywordList.push(token); diff --git a/tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts b/tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts new file mode 100644 index 00000000000..a3d51e642d1 --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts @@ -0,0 +1,29 @@ +/// + +////[|sw/*1*/itch|] (10) { +//// [|/*2*/case|] 1: +//// [|cas/*3*/e|] 2: +//// [|c/*4*/ase|] 4: +//// [|c/*5*/ase|] 8: +//// foo: switch (20) { +//// case 1: +//// case 2: +//// break; +//// default: +//// break foo; +//// } +//// [|cas/*6*/e|] 0xBEEF: +//// [|defa/*7*/ult|]: +//// [|bre/*9*/ak|]; +//// [|/*8*/case|] 16: +////} + + +for (var i = 1; i <= test.markers().length; i++) { + goTo.marker("" + i); + verify.occurrencesAtPositionCount(9); + + test.ranges().forEach(range => { + verify.occurrencesAtPositionContains(range, false); + }); +} diff --git a/tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts b/tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts new file mode 100644 index 00000000000..a8c91f530ca --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts @@ -0,0 +1,29 @@ +/// + +////switch (10) { +//// case 1: +//// case 2: +//// case 4: +//// case 8: +//// foo: [|swi/*1*/tch|] (20) { +//// [|/*2*/case|] 1: +//// [|cas/*3*/e|] 2: +//// [|b/*4*/reak|]; +//// [|defaul/*5*/t|]: +//// break foo; +//// } +//// case 0xBEEF: +//// default: +//// break; +//// case 16: +////} + + +for (var i = 1; i <= test.markers().length; i++) { + goTo.marker("" + i); + verify.occurrencesAtPositionCount(5); + + test.ranges().forEach(range => { + verify.occurrencesAtPositionContains(range, false); + }); +} diff --git a/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts new file mode 100644 index 00000000000..9ce6d1ffb81 --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts @@ -0,0 +1,47 @@ +/// + +////swi/*1*/tch(10) { +//// case 1: +//// case 2: +//// c/*2*/ase 4: +//// case 8: +//// case 0xBEEF: +//// de/*4*/fult: +//// break; +//// /*5*/cas 16: +//// c/*3*/ase 12: +////} + +////sw/*6*/itch (10) { +//// de/*7*/fault +//// case 1: +//// case 2 + +//// c/*8*/ose 4: +//// case 8: +//// case 0xBEEF: +//// bre/*9*/ak; +//// case 16: +////} + +for (var i = 1; i <= test.markers().length; i++) { + goTo.marker("" + i); + + switch (i) { + case 1: + case 2: + case 3: + verify.occurrencesAtPositionCount(8); + break; + case 4: + case 5: + case 8: + verify.occurrencesAtPositionCount(1); + break; + case 6: + case 7: + case 9: + verify.occurrencesAtPositionCount(8); + break; + } +} \ No newline at end of file diff --git a/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultNegatives.ts b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultNegatives.ts new file mode 100644 index 00000000000..cfb70b0c11a --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultNegatives.ts @@ -0,0 +1,25 @@ +/// + +////switch/*1*/ (10) { +//// case/*2*/ 1: +//// case/*3*/ 2: +//// case/*4*/ 4: +//// case/*5*/ 8: +//// foo: switch/*6*/ (20) { +//// case/*7*/ 1: +//// case/*8*/ 2: +//// break/*9*/; +//// default/*10*/: +//// break foo; +//// } +//// case/*11*/ 0xBEEF: +//// default/*12*/: +//// break/*13*/; +//// case 16/*14*/: +////} + + +for (var i = 1; i <= test.markers().length; i++) { + goTo.marker("" + i); + verify.occurrencesAtPositionCount(0); +}