Merge pull request #561 from Microsoft/getOccurrencesIfElseIfElseIfElse
getOccurrencesAtPosition support for if/else keywords
This commit is contained in:
commit
e060a8e710
|
@ -2168,6 +2168,12 @@ module ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.IfKeyword:
|
||||||
|
case SyntaxKind.ElseKeyword:
|
||||||
|
if (hasKind(node.parent, SyntaxKind.IfStatement)) {
|
||||||
|
return getIfElseOccurrences(<IfStatement>node.parent);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SyntaxKind.TryKeyword:
|
case SyntaxKind.TryKeyword:
|
||||||
case SyntaxKind.CatchKeyword:
|
case SyntaxKind.CatchKeyword:
|
||||||
case SyntaxKind.FinallyKeyword:
|
case SyntaxKind.FinallyKeyword:
|
||||||
|
@ -2195,6 +2201,66 @@ module ts {
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
|
function getIfElseOccurrences(ifStatement: IfStatement): ReferenceEntry[] {
|
||||||
|
var keywords: Node[] = [];
|
||||||
|
|
||||||
|
// Traverse upwards through all parent if-statements linked by their else-branches.
|
||||||
|
while (hasKind(ifStatement.parent, SyntaxKind.IfStatement) && (<IfStatement>ifStatement.parent).elseStatement === ifStatement) {
|
||||||
|
ifStatement = <IfStatement>ifStatement.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
|
||||||
|
while (ifStatement) {
|
||||||
|
var children = ifStatement.getChildren();
|
||||||
|
pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword);
|
||||||
|
|
||||||
|
// Generally the 'else' keyword is second-to-last, so we traverse backwards.
|
||||||
|
for (var i = children.length - 1; i >= 0; i--) {
|
||||||
|
if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasKind(ifStatement.elseStatement, SyntaxKind.IfStatement)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ifStatement = <IfStatement>ifStatement.elseStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result: ReferenceEntry[] = [];
|
||||||
|
|
||||||
|
// We'd like to highlight else/ifs together if they are only separated by whitespace
|
||||||
|
// (i.e. the keywords are separated by no comments, no newlines).
|
||||||
|
for (var i = 0; i < keywords.length; i++) {
|
||||||
|
if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) {
|
||||||
|
var elseKeyword = keywords[i];
|
||||||
|
var ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword.
|
||||||
|
|
||||||
|
var shouldHighlightNextKeyword = true;
|
||||||
|
|
||||||
|
// Avoid recalculating getStart() by iterating backwards.
|
||||||
|
for (var j = ifKeyword.getStart() - 1; j >= elseKeyword.end; j--) {
|
||||||
|
if (!isWhiteSpace(sourceFile.text.charCodeAt(j))) {
|
||||||
|
shouldHighlightNextKeyword = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldHighlightNextKeyword) {
|
||||||
|
result.push(new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(elseKeyword.getStart(), ifKeyword.end), /* isWriteAccess */ false));
|
||||||
|
i++; // skip the next keyword
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordinary case: just highlight the keyword.
|
||||||
|
result.push(keywordToReferenceEntry(keywords[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
|
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
|
||||||
var keywords: Node[] = [];
|
var keywords: Node[] = [];
|
||||||
|
|
||||||
|
@ -2208,7 +2274,7 @@ module ts {
|
||||||
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
|
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keywordsToReferenceEntries(keywords);
|
return map(keywords, keywordToReferenceEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
|
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
|
||||||
|
@ -2244,7 +2310,7 @@ module ts {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return keywordsToReferenceEntries(keywords);
|
return map(keywords, keywordToReferenceEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
|
function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
|
||||||
|
@ -2285,20 +2351,17 @@ module ts {
|
||||||
return node && node.parent;
|
return node && node.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): void {
|
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): boolean {
|
||||||
if (!token) {
|
if (token && contains(expected, token.kind)) {
|
||||||
return;
|
keywordList.push(token);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contains(<SyntaxKind[]>expected, token.kind)) {
|
return false;
|
||||||
keywordList.push(token);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function keywordsToReferenceEntries(keywords: Node[]): ReferenceEntry[]{
|
function keywordToReferenceEntry(keyword: Node): ReferenceEntry {
|
||||||
return map(keywords, keyword =>
|
return new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false);
|
||||||
new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
tests/cases/fourslash/getOccurrencesIfElse.ts
Normal file
36
tests/cases/fourslash/getOccurrencesIfElse.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////[|if|] (true) {
|
||||||
|
//// if (false) {
|
||||||
|
//// }
|
||||||
|
//// else {
|
||||||
|
//// }
|
||||||
|
//// if (true) {
|
||||||
|
//// }
|
||||||
|
//// else {
|
||||||
|
//// if (false)
|
||||||
|
//// if (true)
|
||||||
|
//// var x = undefined;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////[|else i/**/f|] (null) {
|
||||||
|
////}
|
||||||
|
////[|else|] /* whar garbl */ [|if|] (undefined) {
|
||||||
|
////}
|
||||||
|
////[|else|]
|
||||||
|
////[|if|] (false) {
|
||||||
|
////}
|
||||||
|
////[|else|] { }
|
||||||
|
|
||||||
|
test.ranges().forEach(r => {
|
||||||
|
goTo.position(r.start);
|
||||||
|
|
||||||
|
test.ranges().forEach(range => {
|
||||||
|
verify.occurrencesAtPositionContains(range, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
goTo.marker();
|
||||||
|
test.ranges().forEach(range => {
|
||||||
|
verify.occurrencesAtPositionContains(range, false);
|
||||||
|
});
|
32
tests/cases/fourslash/getOccurrencesIfElse2.ts
Normal file
32
tests/cases/fourslash/getOccurrencesIfElse2.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////if (true) {
|
||||||
|
//// [|if|] (false) {
|
||||||
|
//// }
|
||||||
|
//// [|else|]{
|
||||||
|
//// }
|
||||||
|
//// if (true) {
|
||||||
|
//// }
|
||||||
|
//// else {
|
||||||
|
//// if (false)
|
||||||
|
//// if (true)
|
||||||
|
//// var x = undefined;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////else if (null) {
|
||||||
|
////}
|
||||||
|
////else /* whar garbl */ if (undefined) {
|
||||||
|
////}
|
||||||
|
////else
|
||||||
|
////if (false) {
|
||||||
|
////}
|
||||||
|
////else { }
|
||||||
|
|
||||||
|
|
||||||
|
test.ranges().forEach(r => {
|
||||||
|
goTo.position(r.start);
|
||||||
|
|
||||||
|
test.ranges().forEach(range => {
|
||||||
|
verify.occurrencesAtPositionContains(range, false);
|
||||||
|
});
|
||||||
|
});
|
32
tests/cases/fourslash/getOccurrencesIfElse3.ts
Normal file
32
tests/cases/fourslash/getOccurrencesIfElse3.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////if (true) {
|
||||||
|
//// if (false) {
|
||||||
|
//// }
|
||||||
|
//// else {
|
||||||
|
//// }
|
||||||
|
//// [|if|] (true) {
|
||||||
|
//// }
|
||||||
|
//// [|else|] {
|
||||||
|
//// if (false)
|
||||||
|
//// if (true)
|
||||||
|
//// var x = undefined;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////else if (null) {
|
||||||
|
////}
|
||||||
|
////else /* whar garbl */ if (undefined) {
|
||||||
|
////}
|
||||||
|
////else
|
||||||
|
////if (false) {
|
||||||
|
////}
|
||||||
|
////else { }
|
||||||
|
|
||||||
|
|
||||||
|
test.ranges().forEach(r => {
|
||||||
|
goTo.position(r.start);
|
||||||
|
|
||||||
|
test.ranges().forEach(range => {
|
||||||
|
verify.occurrencesAtPositionContains(range, false);
|
||||||
|
});
|
||||||
|
});
|
30
tests/cases/fourslash/getOccurrencesIfElse4.ts
Normal file
30
tests/cases/fourslash/getOccurrencesIfElse4.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////if (true) {
|
||||||
|
//// if (false) {
|
||||||
|
//// }
|
||||||
|
//// else {
|
||||||
|
//// }
|
||||||
|
//// if (true) {
|
||||||
|
//// }
|
||||||
|
//// else {
|
||||||
|
//// /*1*/if (false)
|
||||||
|
//// /*2*/i/*3*/f (true)
|
||||||
|
//// var x = undefined;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////else if (null) {
|
||||||
|
////}
|
||||||
|
////else /* whar garbl */ if (undefined) {
|
||||||
|
////}
|
||||||
|
////else
|
||||||
|
////if (false) {
|
||||||
|
////}
|
||||||
|
////else { }
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 1; i <= test.markers().length; i++) {
|
||||||
|
goTo.marker("" + i);
|
||||||
|
|
||||||
|
verify.occurrencesAtPositionCount(1);
|
||||||
|
}
|
24
tests/cases/fourslash/getOccurrencesIfElseBroken.ts
Normal file
24
tests/cases/fourslash/getOccurrencesIfElseBroken.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
|
||||||
|
////[|if|] (true) {
|
||||||
|
//// var x = 1;
|
||||||
|
////}
|
||||||
|
////[|else if|] ()
|
||||||
|
////[|else if|]
|
||||||
|
////[|else|] /* whar garbl */ [|if|] (i/**/f (true) { } else { })
|
||||||
|
////else
|
||||||
|
|
||||||
|
// It would be nice if in the future,
|
||||||
|
// We could include that last 'else'.
|
||||||
|
|
||||||
|
test.ranges().forEach(r => {
|
||||||
|
goTo.position(r.start);
|
||||||
|
|
||||||
|
test.ranges().forEach(range => {
|
||||||
|
verify.occurrencesAtPositionContains(range, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
goTo.marker();
|
||||||
|
verify.occurrencesAtPositionCount(2);
|
29
tests/cases/fourslash/getOccurrencesIfElseNegatives.ts
Normal file
29
tests/cases/fourslash/getOccurrencesIfElseNegatives.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/// <reference path='fourslash.ts' />
|
||||||
|
|
||||||
|
////if/*1*/ (true) {
|
||||||
|
//// if/*2*/ (false) {
|
||||||
|
//// }
|
||||||
|
//// else/*3*/ {
|
||||||
|
//// }
|
||||||
|
//// if/*4*/ (true) {
|
||||||
|
//// }
|
||||||
|
//// else/*5*/ {
|
||||||
|
//// if/*6*/ (false)
|
||||||
|
//// if/*7*/ (true)
|
||||||
|
//// var x = undefined;
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
////else/*8*/ if (null) {
|
||||||
|
////}
|
||||||
|
////else/*9*/ /* whar garbl */ if/*10*/ (undefined) {
|
||||||
|
////}
|
||||||
|
////else/*11*/
|
||||||
|
////if/*12*/ (false) {
|
||||||
|
////}
|
||||||
|
////else/*13*/ { }
|
||||||
|
|
||||||
|
|
||||||
|
test.markers().forEach(m => {
|
||||||
|
goTo.position(m.position, m.fileName)
|
||||||
|
verify.occurrencesAtPositionCount(0);
|
||||||
|
});
|
Loading…
Reference in a new issue