Skip to content

Commit 41d8d6c

Browse files
Merge pull request #549 from Microsoft/getOccurrences_switchCaseDefault
Get occurrences for switch/case/default/break keywords.
2 parents 5b7da99 + 813f28d commit 41d8d6c

11 files changed

+458
-27
lines changed

src/compiler/parser.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,22 @@ module ts {
483483
nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext;
484484
}
485485

486+
export function isKeyword(token: SyntaxKind): boolean {
487+
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
488+
}
489+
490+
export function isModifier(token: SyntaxKind): boolean {
491+
switch (token) {
492+
case SyntaxKind.PublicKeyword:
493+
case SyntaxKind.PrivateKeyword:
494+
case SyntaxKind.StaticKeyword:
495+
case SyntaxKind.ExportKeyword:
496+
case SyntaxKind.DeclareKeyword:
497+
return true;
498+
}
499+
return false;
500+
}
501+
486502
export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget, version: string, isOpen: boolean = false): SourceFile {
487503
var file: SourceFile;
488504
var scanner: Scanner;
@@ -853,23 +869,6 @@ module ts {
853869
return parseIdentifierName();
854870
}
855871

856-
857-
function isKeyword(token: SyntaxKind): boolean {
858-
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
859-
}
860-
861-
function isModifier(token: SyntaxKind): boolean {
862-
switch (token) {
863-
case SyntaxKind.PublicKeyword:
864-
case SyntaxKind.PrivateKeyword:
865-
case SyntaxKind.StaticKeyword:
866-
case SyntaxKind.ExportKeyword:
867-
case SyntaxKind.DeclareKeyword:
868-
return true;
869-
}
870-
return false;
871-
}
872-
873872
function parseContextualModifier(t: SyntaxKind): boolean {
874873
return token === t && tryParse(() => {
875874
nextToken();

src/services/services.ts

Lines changed: 143 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,12 +1314,13 @@ module ts {
13141314

13151315
function isAnyFunction(node: Node): boolean {
13161316
switch (node.kind) {
1317+
case SyntaxKind.FunctionExpression:
13171318
case SyntaxKind.FunctionDeclaration:
1319+
case SyntaxKind.ArrowFunction:
13181320
case SyntaxKind.Method:
1319-
case SyntaxKind.FunctionExpression:
13201321
case SyntaxKind.GetAccessor:
13211322
case SyntaxKind.SetAccessor:
1322-
case SyntaxKind.ArrowFunction:
1323+
case SyntaxKind.Constructor:
13231324
return true;
13241325
}
13251326
return false;
@@ -1933,7 +1934,9 @@ module ts {
19331934
current = child;
19341935
continue outer;
19351936
}
1936-
if (child.end > position) break;
1937+
if (child.end > position) {
1938+
break;
1939+
}
19371940
}
19381941
return current;
19391942
}
@@ -2160,13 +2163,143 @@ module ts {
21602163
return undefined;
21612164
}
21622165

2163-
if (node.kind !== SyntaxKind.Identifier &&
2164-
!isLiteralNameOfPropertyDeclarationOrIndexAccess(node) &&
2165-
!isNameOfExternalModuleImportOrDeclaration(node)) {
2166+
if (node.kind === SyntaxKind.Identifier || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) {
2167+
return getReferencesForNode(node, [sourceFile]);
2168+
}
2169+
2170+
switch (node.kind) {
2171+
case SyntaxKind.TryKeyword:
2172+
case SyntaxKind.CatchKeyword:
2173+
case SyntaxKind.FinallyKeyword:
2174+
if (hasKind(parent(parent(node)), SyntaxKind.TryStatement)) {
2175+
return getTryCatchFinallyOccurrences(<TryStatement>node.parent.parent);
2176+
}
2177+
break;
2178+
case SyntaxKind.SwitchKeyword:
2179+
if (hasKind(node.parent, SyntaxKind.SwitchStatement)) {
2180+
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent);
2181+
}
2182+
break;
2183+
case SyntaxKind.CaseKeyword:
2184+
case SyntaxKind.DefaultKeyword:
2185+
if (hasKind(parent(parent(node)), SyntaxKind.SwitchStatement)) {
2186+
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent.parent);
2187+
}
2188+
break;
2189+
case SyntaxKind.BreakKeyword:
2190+
if (hasKind(node.parent, SyntaxKind.BreakStatement)) {
2191+
return getBreakStatementOccurences(<BreakOrContinueStatement>node.parent);
2192+
}
2193+
break;
2194+
}
2195+
2196+
return undefined;
2197+
2198+
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
2199+
var keywords: Node[] = [];
2200+
2201+
pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword);
2202+
2203+
if (tryStatement.catchBlock) {
2204+
pushKeywordIf(keywords, tryStatement.catchBlock.getFirstToken(), SyntaxKind.CatchKeyword);
2205+
}
2206+
2207+
if (tryStatement.finallyBlock) {
2208+
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
2209+
}
2210+
2211+
return keywordsToReferenceEntries(keywords);
2212+
}
2213+
2214+
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
2215+
var keywords: Node[] = [];
2216+
2217+
pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword);
2218+
2219+
// Go through each clause in the switch statement, collecting the clause keywords.
2220+
forEach(switchStatement.clauses, clause => {
2221+
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);
2222+
2223+
// For each clause, also recursively traverse the statements where we can find analogous breaks.
2224+
forEachChild(clause, function aggregateBreakKeywords(node: Node): void {
2225+
switch (node.kind) {
2226+
case SyntaxKind.BreakStatement:
2227+
// If the break statement has a label, it cannot be part of a switch block.
2228+
if (!(<BreakOrContinueStatement>node).label) {
2229+
pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.BreakKeyword);
2230+
}
2231+
// Fall through
2232+
case SyntaxKind.ForStatement:
2233+
case SyntaxKind.ForInStatement:
2234+
case SyntaxKind.DoStatement:
2235+
case SyntaxKind.WhileStatement:
2236+
case SyntaxKind.SwitchStatement:
2237+
return;
2238+
}
2239+
2240+
// Do not cross function boundaries.
2241+
if (!isAnyFunction(node)) {
2242+
forEachChild(node, aggregateBreakKeywords);
2243+
}
2244+
});
2245+
});
2246+
2247+
return keywordsToReferenceEntries(keywords);
2248+
}
2249+
2250+
function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
2251+
// TODO (drosen): Deal with labeled statements.
2252+
if (breakStatement.label) {
2253+
return undefined;
2254+
}
2255+
2256+
for (var owner = node.parent; owner; owner = owner.parent) {
2257+
switch (owner.kind) {
2258+
case SyntaxKind.ForStatement:
2259+
case SyntaxKind.ForInStatement:
2260+
case SyntaxKind.DoStatement:
2261+
case SyntaxKind.WhileStatement:
2262+
// TODO (drosen): Handle loops!
2263+
return undefined;
2264+
2265+
case SyntaxKind.SwitchStatement:
2266+
return getSwitchCaseDefaultOccurrences(<SwitchStatement>owner);
2267+
2268+
default:
2269+
if (isAnyFunction(owner)) {
2270+
return undefined;
2271+
}
2272+
}
2273+
}
2274+
21662275
return undefined;
21672276
}
21682277

2169-
return getReferencesForNode(node, [sourceFile]);
2278+
// returns true if 'node' is defined and has a matching 'kind'.
2279+
function hasKind(node: Node, kind: SyntaxKind) {
2280+
return !!(node && node.kind === kind);
2281+
}
2282+
2283+
// Null-propagating 'parent' function.
2284+
function parent(node: Node): Node {
2285+
return node && node.parent;
2286+
}
2287+
2288+
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): void {
2289+
if (!token) {
2290+
return;
2291+
}
2292+
2293+
if (contains(<SyntaxKind[]>expected, token.kind)) {
2294+
keywordList.push(token);
2295+
}
2296+
}
2297+
2298+
function keywordsToReferenceEntries(keywords: Node[]): ReferenceEntry[]{
2299+
return map(keywords, keyword =>
2300+
new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false)
2301+
);
2302+
}
21702303
}
21712304

21722305
function getReferencesAtPosition(filename: string, position: number): ReferenceEntry[] {
@@ -2284,13 +2417,13 @@ module ts {
22842417
var container = getContainerNode(declarations[i]);
22852418

22862419
if (scope && scope !== container) {
2287-
// Diffrent declarations have diffrent containers, bail out
2420+
// Different declarations have different containers, bail out
22882421
return undefined;
22892422
}
22902423

22912424
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
22922425
// This is a global variable and not an external module, any declaration defined
2293-
// withen this scope is visible outside the file
2426+
// within this scope is visible outside the file
22942427
return undefined;
22952428
}
22962429

@@ -2957,7 +3090,7 @@ module ts {
29573090
// ["// hack 1", "// ", "hack 1", undefined, "hack"]
29583091
//
29593092
// Here are the relevant capture groups:
2960-
// 0) The full match for hte entire regex.
3093+
// 0) The full match for the entire regex.
29613094
// 1) The preamble to the message portion.
29623095
// 2) The message portion.
29633096
// 3...N) The descriptor that was matched - by index. 'undefined' for each
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////[|sw/*1*/itch|] (10) {
4+
//// [|/*2*/case|] 1:
5+
//// [|cas/*3*/e|] 2:
6+
//// [|c/*4*/ase|] 4:
7+
//// [|c/*5*/ase|] 8:
8+
//// foo: switch (20) {
9+
//// case 1:
10+
//// case 2:
11+
//// break;
12+
//// default:
13+
//// break foo;
14+
//// }
15+
//// [|cas/*6*/e|] 0xBEEF:
16+
//// [|defa/*7*/ult|]:
17+
//// [|bre/*9*/ak|];
18+
//// [|/*8*/case|] 16:
19+
////}
20+
21+
22+
for (var i = 1; i <= test.markers().length; i++) {
23+
goTo.marker("" + i);
24+
verify.occurrencesAtPositionCount(9);
25+
26+
test.ranges().forEach(range => {
27+
verify.occurrencesAtPositionContains(range, false);
28+
});
29+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////switch (10) {
4+
//// case 1:
5+
//// case 2:
6+
//// case 4:
7+
//// case 8:
8+
//// foo: [|swi/*1*/tch|] (20) {
9+
//// [|/*2*/case|] 1:
10+
//// [|cas/*3*/e|] 2:
11+
//// [|b/*4*/reak|];
12+
//// [|defaul/*5*/t|]:
13+
//// break foo;
14+
//// }
15+
//// case 0xBEEF:
16+
//// default:
17+
//// break;
18+
//// case 16:
19+
////}
20+
21+
22+
for (var i = 1; i <= test.markers().length; i++) {
23+
goTo.marker("" + i);
24+
verify.occurrencesAtPositionCount(5);
25+
26+
test.ranges().forEach(range => {
27+
verify.occurrencesAtPositionContains(range, false);
28+
});
29+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////swi/*1*/tch(10) {
4+
//// case 1:
5+
//// case 2:
6+
//// c/*2*/ase 4:
7+
//// case 8:
8+
//// case 0xBEEF:
9+
//// de/*4*/fult:
10+
//// break;
11+
//// /*5*/cas 16:
12+
//// c/*3*/ase 12:
13+
//// function f() {
14+
//// br/*11*/eak;
15+
//// /*12*/break;
16+
//// }
17+
////}
18+
////
19+
////sw/*6*/itch (10) {
20+
//// de/*7*/fault
21+
//// case 1:
22+
//// case 2
23+
////
24+
//// c/*8*/ose 4:
25+
//// case 8:
26+
//// case 0xBEEF:
27+
//// bre/*9*/ak;
28+
//// case 16:
29+
//// () => bre/*10*/ak;
30+
////}
31+
32+
for (var i = 1; i <= test.markers().length; i++) {
33+
goTo.marker("" + i);
34+
35+
switch (i) {
36+
case 1:
37+
case 2:
38+
case 3:
39+
verify.occurrencesAtPositionCount(8);
40+
break;
41+
case 4:
42+
case 5:
43+
case 8:
44+
verify.occurrencesAtPositionCount(1);
45+
break;
46+
case 6:
47+
case 7:
48+
case 9:
49+
verify.occurrencesAtPositionCount(8);
50+
break;
51+
case 10:
52+
case 11:
53+
case 12:
54+
verify.occurrencesAtPositionCount(0);
55+
break;
56+
}
57+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////switch/*1*/ (10) {
4+
//// case/*2*/ 1:
5+
//// case/*3*/ 2:
6+
//// case/*4*/ 4:
7+
//// case/*5*/ 8:
8+
//// foo: switch/*6*/ (20) {
9+
//// case/*7*/ 1:
10+
//// case/*8*/ 2:
11+
//// break/*9*/;
12+
//// default/*10*/:
13+
//// break foo;
14+
//// }
15+
//// case/*11*/ 0xBEEF:
16+
//// default/*12*/:
17+
//// break/*13*/;
18+
//// case 16/*14*/:
19+
////}
20+
21+
22+
for (var i = 1; i <= test.markers().length; i++) {
23+
goTo.marker("" + i);
24+
verify.occurrencesAtPositionCount(0);
25+
}

0 commit comments

Comments
 (0)