Skip to content

Commit e060a8e

Browse files
Merge pull request #561 from Microsoft/getOccurrencesIfElseIfElseIfElse
getOccurrencesAtPosition support for if/else keywords
2 parents 41d8d6c + 3f3dd29 commit e060a8e

File tree

7 files changed

+258
-12
lines changed

7 files changed

+258
-12
lines changed

src/services/services.ts

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,12 @@ module ts {
21682168
}
21692169

21702170
switch (node.kind) {
2171+
case SyntaxKind.IfKeyword:
2172+
case SyntaxKind.ElseKeyword:
2173+
if (hasKind(node.parent, SyntaxKind.IfStatement)) {
2174+
return getIfElseOccurrences(<IfStatement>node.parent);
2175+
}
2176+
break;
21712177
case SyntaxKind.TryKeyword:
21722178
case SyntaxKind.CatchKeyword:
21732179
case SyntaxKind.FinallyKeyword:
@@ -2195,6 +2201,66 @@ module ts {
21952201

21962202
return undefined;
21972203

2204+
function getIfElseOccurrences(ifStatement: IfStatement): ReferenceEntry[] {
2205+
var keywords: Node[] = [];
2206+
2207+
// Traverse upwards through all parent if-statements linked by their else-branches.
2208+
while (hasKind(ifStatement.parent, SyntaxKind.IfStatement) && (<IfStatement>ifStatement.parent).elseStatement === ifStatement) {
2209+
ifStatement = <IfStatement>ifStatement.parent;
2210+
}
2211+
2212+
// Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
2213+
while (ifStatement) {
2214+
var children = ifStatement.getChildren();
2215+
pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword);
2216+
2217+
// Generally the 'else' keyword is second-to-last, so we traverse backwards.
2218+
for (var i = children.length - 1; i >= 0; i--) {
2219+
if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) {
2220+
break;
2221+
}
2222+
}
2223+
2224+
if (!hasKind(ifStatement.elseStatement, SyntaxKind.IfStatement)) {
2225+
break
2226+
}
2227+
2228+
ifStatement = <IfStatement>ifStatement.elseStatement;
2229+
}
2230+
2231+
var result: ReferenceEntry[] = [];
2232+
2233+
// We'd like to highlight else/ifs together if they are only separated by whitespace
2234+
// (i.e. the keywords are separated by no comments, no newlines).
2235+
for (var i = 0; i < keywords.length; i++) {
2236+
if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) {
2237+
var elseKeyword = keywords[i];
2238+
var ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword.
2239+
2240+
var shouldHighlightNextKeyword = true;
2241+
2242+
// Avoid recalculating getStart() by iterating backwards.
2243+
for (var j = ifKeyword.getStart() - 1; j >= elseKeyword.end; j--) {
2244+
if (!isWhiteSpace(sourceFile.text.charCodeAt(j))) {
2245+
shouldHighlightNextKeyword = false;
2246+
break;
2247+
}
2248+
}
2249+
2250+
if (shouldHighlightNextKeyword) {
2251+
result.push(new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(elseKeyword.getStart(), ifKeyword.end), /* isWriteAccess */ false));
2252+
i++; // skip the next keyword
2253+
continue;
2254+
}
2255+
}
2256+
2257+
// Ordinary case: just highlight the keyword.
2258+
result.push(keywordToReferenceEntry(keywords[i]));
2259+
}
2260+
2261+
return result;
2262+
}
2263+
21982264
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
21992265
var keywords: Node[] = [];
22002266

@@ -2208,7 +2274,7 @@ module ts {
22082274
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
22092275
}
22102276

2211-
return keywordsToReferenceEntries(keywords);
2277+
return map(keywords, keywordToReferenceEntry);
22122278
}
22132279

22142280
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
@@ -2244,7 +2310,7 @@ module ts {
22442310
});
22452311
});
22462312

2247-
return keywordsToReferenceEntries(keywords);
2313+
return map(keywords, keywordToReferenceEntry);
22482314
}
22492315

22502316
function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
@@ -2285,20 +2351,17 @@ module ts {
22852351
return node && node.parent;
22862352
}
22872353

2288-
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): void {
2289-
if (!token) {
2290-
return;
2291-
}
2292-
2293-
if (contains(<SyntaxKind[]>expected, token.kind)) {
2354+
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): boolean {
2355+
if (token && contains(expected, token.kind)) {
22942356
keywordList.push(token);
2357+
return true;
22952358
}
2359+
2360+
return false;
22962361
}
22972362

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-
);
2363+
function keywordToReferenceEntry(keyword: Node): ReferenceEntry {
2364+
return new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false);
23022365
}
23032366
}
23042367

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////[|if|] (true) {
4+
//// if (false) {
5+
//// }
6+
//// else {
7+
//// }
8+
//// if (true) {
9+
//// }
10+
//// else {
11+
//// if (false)
12+
//// if (true)
13+
//// var x = undefined;
14+
//// }
15+
////}
16+
////[|else i/**/f|] (null) {
17+
////}
18+
////[|else|] /* whar garbl */ [|if|] (undefined) {
19+
////}
20+
////[|else|]
21+
////[|if|] (false) {
22+
////}
23+
////[|else|] { }
24+
25+
test.ranges().forEach(r => {
26+
goTo.position(r.start);
27+
28+
test.ranges().forEach(range => {
29+
verify.occurrencesAtPositionContains(range, false);
30+
});
31+
});
32+
33+
goTo.marker();
34+
test.ranges().forEach(range => {
35+
verify.occurrencesAtPositionContains(range, false);
36+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////if (true) {
4+
//// [|if|] (false) {
5+
//// }
6+
//// [|else|]{
7+
//// }
8+
//// if (true) {
9+
//// }
10+
//// else {
11+
//// if (false)
12+
//// if (true)
13+
//// var x = undefined;
14+
//// }
15+
////}
16+
////else if (null) {
17+
////}
18+
////else /* whar garbl */ if (undefined) {
19+
////}
20+
////else
21+
////if (false) {
22+
////}
23+
////else { }
24+
25+
26+
test.ranges().forEach(r => {
27+
goTo.position(r.start);
28+
29+
test.ranges().forEach(range => {
30+
verify.occurrencesAtPositionContains(range, false);
31+
});
32+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////if (true) {
4+
//// if (false) {
5+
//// }
6+
//// else {
7+
//// }
8+
//// [|if|] (true) {
9+
//// }
10+
//// [|else|] {
11+
//// if (false)
12+
//// if (true)
13+
//// var x = undefined;
14+
//// }
15+
////}
16+
////else if (null) {
17+
////}
18+
////else /* whar garbl */ if (undefined) {
19+
////}
20+
////else
21+
////if (false) {
22+
////}
23+
////else { }
24+
25+
26+
test.ranges().forEach(r => {
27+
goTo.position(r.start);
28+
29+
test.ranges().forEach(range => {
30+
verify.occurrencesAtPositionContains(range, false);
31+
});
32+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////if (true) {
4+
//// if (false) {
5+
//// }
6+
//// else {
7+
//// }
8+
//// if (true) {
9+
//// }
10+
//// else {
11+
//// /*1*/if (false)
12+
//// /*2*/i/*3*/f (true)
13+
//// var x = undefined;
14+
//// }
15+
////}
16+
////else if (null) {
17+
////}
18+
////else /* whar garbl */ if (undefined) {
19+
////}
20+
////else
21+
////if (false) {
22+
////}
23+
////else { }
24+
25+
26+
for (var i = 1; i <= test.markers().length; i++) {
27+
goTo.marker("" + i);
28+
29+
verify.occurrencesAtPositionCount(1);
30+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
4+
////[|if|] (true) {
5+
//// var x = 1;
6+
////}
7+
////[|else if|] ()
8+
////[|else if|]
9+
////[|else|] /* whar garbl */ [|if|] (i/**/f (true) { } else { })
10+
////else
11+
12+
// It would be nice if in the future,
13+
// We could include that last 'else'.
14+
15+
test.ranges().forEach(r => {
16+
goTo.position(r.start);
17+
18+
test.ranges().forEach(range => {
19+
verify.occurrencesAtPositionContains(range, false);
20+
});
21+
});
22+
23+
goTo.marker();
24+
verify.occurrencesAtPositionCount(2);
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+
////if/*1*/ (true) {
4+
//// if/*2*/ (false) {
5+
//// }
6+
//// else/*3*/ {
7+
//// }
8+
//// if/*4*/ (true) {
9+
//// }
10+
//// else/*5*/ {
11+
//// if/*6*/ (false)
12+
//// if/*7*/ (true)
13+
//// var x = undefined;
14+
//// }
15+
////}
16+
////else/*8*/ if (null) {
17+
////}
18+
////else/*9*/ /* whar garbl */ if/*10*/ (undefined) {
19+
////}
20+
////else/*11*/
21+
////if/*12*/ (false) {
22+
////}
23+
////else/*13*/ { }
24+
25+
26+
test.markers().forEach(m => {
27+
goTo.position(m.position, m.fileName)
28+
verify.occurrencesAtPositionCount(0);
29+
});

0 commit comments

Comments
 (0)