Merge pull request #549 from Microsoft/getOccurrences_switchCaseDefault
Get occurrences for switch/case/default/break keywords.
This commit is contained in:
commit
41d8d6c4e1
|
@ -483,6 +483,22 @@ module ts {
|
|||
nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext;
|
||||
}
|
||||
|
||||
export function isKeyword(token: SyntaxKind): boolean {
|
||||
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
|
||||
}
|
||||
|
||||
export function isModifier(token: SyntaxKind): boolean {
|
||||
switch (token) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ExportKeyword:
|
||||
case SyntaxKind.DeclareKeyword:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget, version: string, isOpen: boolean = false): SourceFile {
|
||||
var file: SourceFile;
|
||||
var scanner: Scanner;
|
||||
|
@ -853,23 +869,6 @@ module ts {
|
|||
return parseIdentifierName();
|
||||
}
|
||||
|
||||
|
||||
function isKeyword(token: SyntaxKind): boolean {
|
||||
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
|
||||
}
|
||||
|
||||
function isModifier(token: SyntaxKind): boolean {
|
||||
switch (token) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ExportKeyword:
|
||||
case SyntaxKind.DeclareKeyword:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseContextualModifier(t: SyntaxKind): boolean {
|
||||
return token === t && tryParse(() => {
|
||||
nextToken();
|
||||
|
|
|
@ -1314,12 +1314,13 @@ module ts {
|
|||
|
||||
function isAnyFunction(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.Method:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.Constructor:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1933,7 +1934,9 @@ module ts {
|
|||
current = child;
|
||||
continue outer;
|
||||
}
|
||||
if (child.end > position) break;
|
||||
if (child.end > position) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
@ -2160,13 +2163,143 @@ 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]);
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.TryKeyword:
|
||||
case SyntaxKind.CatchKeyword:
|
||||
case SyntaxKind.FinallyKeyword:
|
||||
if (hasKind(parent(parent(node)), SyntaxKind.TryStatement)) {
|
||||
return getTryCatchFinallyOccurrences(<TryStatement>node.parent.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.SwitchKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.SwitchStatement)) {
|
||||
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.CaseKeyword:
|
||||
case SyntaxKind.DefaultKeyword:
|
||||
if (hasKind(parent(parent(node)), SyntaxKind.SwitchStatement)) {
|
||||
return getSwitchCaseDefaultOccurrences(<SwitchStatement>node.parent.parent);
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BreakKeyword:
|
||||
if (hasKind(node.parent, SyntaxKind.BreakStatement)) {
|
||||
return getBreakStatementOccurences(<BreakOrContinueStatement>node.parent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
|
||||
var keywords: Node[] = [];
|
||||
|
||||
pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword);
|
||||
|
||||
if (tryStatement.catchBlock) {
|
||||
pushKeywordIf(keywords, tryStatement.catchBlock.getFirstToken(), SyntaxKind.CatchKeyword);
|
||||
}
|
||||
|
||||
if (tryStatement.finallyBlock) {
|
||||
pushKeywordIf(keywords, tryStatement.finallyBlock.getFirstToken(), SyntaxKind.FinallyKeyword);
|
||||
}
|
||||
|
||||
return keywordsToReferenceEntries(keywords);
|
||||
}
|
||||
|
||||
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) {
|
||||
var keywords: Node[] = [];
|
||||
|
||||
pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword);
|
||||
|
||||
// Go through each clause in the switch statement, collecting the clause keywords.
|
||||
forEach(switchStatement.clauses, clause => {
|
||||
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);
|
||||
|
||||
// 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, it cannot be part of a switch block.
|
||||
if (!(<BreakOrContinueStatement>node).label) {
|
||||
pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.BreakKeyword);
|
||||
}
|
||||
// Fall through
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not cross function boundaries.
|
||||
if (!isAnyFunction(node)) {
|
||||
forEachChild(node, aggregateBreakKeywords);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return keywordsToReferenceEntries(keywords);
|
||||
}
|
||||
|
||||
function getBreakStatementOccurences(breakStatement: BreakOrContinueStatement): ReferenceEntry[]{
|
||||
// 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(<SwitchStatement>owner);
|
||||
|
||||
default:
|
||||
if (isAnyFunction(owner)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getReferencesForNode(node, [sourceFile]);
|
||||
// returns true if 'node' is defined and has a matching 'kind'.
|
||||
function hasKind(node: Node, kind: SyntaxKind) {
|
||||
return !!(node && node.kind === kind);
|
||||
}
|
||||
|
||||
// Null-propagating 'parent' function.
|
||||
function parent(node: Node): Node {
|
||||
return node && node.parent;
|
||||
}
|
||||
|
||||
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): void {
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (contains(<SyntaxKind[]>expected, token.kind)) {
|
||||
keywordList.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
function keywordsToReferenceEntries(keywords: Node[]): ReferenceEntry[]{
|
||||
return map(keywords, keyword =>
|
||||
new ReferenceEntry(filename, TypeScript.TextSpan.fromBounds(keyword.getStart(), keyword.end), /* isWriteAccess */ false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesAtPosition(filename: string, position: number): ReferenceEntry[] {
|
||||
|
@ -2284,13 +2417,13 @@ module ts {
|
|||
var container = getContainerNode(declarations[i]);
|
||||
|
||||
if (scope && scope !== container) {
|
||||
// Diffrent declarations have diffrent containers, bail out
|
||||
// Different declarations have different containers, bail out
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
|
||||
// This is a global variable and not an external module, any declaration defined
|
||||
// withen this scope is visible outside the file
|
||||
// within this scope is visible outside the file
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -2957,7 +3090,7 @@ module ts {
|
|||
// ["// hack 1", "// ", "hack 1", undefined, "hack"]
|
||||
//
|
||||
// Here are the relevant capture groups:
|
||||
// 0) The full match for hte entire regex.
|
||||
// 0) The full match for the entire regex.
|
||||
// 1) The preamble to the message portion.
|
||||
// 2) The message portion.
|
||||
// 3...N) The descriptor that was matched - by index. 'undefined' for each
|
||||
|
|
29
tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts
Normal file
29
tests/cases/fourslash/getOccurrencesSwitchCaseDefault.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////[|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);
|
||||
});
|
||||
}
|
29
tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts
Normal file
29
tests/cases/fourslash/getOccurrencesSwitchCaseDefault2.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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:
|
||||
//// function f() {
|
||||
//// br/*11*/eak;
|
||||
//// /*12*/break;
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////sw/*6*/itch (10) {
|
||||
//// de/*7*/fault
|
||||
//// case 1:
|
||||
//// case 2
|
||||
////
|
||||
//// c/*8*/ose 4:
|
||||
//// case 8:
|
||||
//// case 0xBEEF:
|
||||
//// bre/*9*/ak;
|
||||
//// case 16:
|
||||
//// () => bre/*10*/ak;
|
||||
////}
|
||||
|
||||
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;
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////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);
|
||||
}
|
27
tests/cases/fourslash/getOccurrencesTryCatchFinally.ts
Normal file
27
tests/cases/fourslash/getOccurrencesTryCatchFinally.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
/////*1*/[|try|] {
|
||||
//// try {
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// }
|
||||
////
|
||||
//// try {
|
||||
//// }
|
||||
//// finally {
|
||||
//// }
|
||||
////}
|
||||
////[|cat/*2*/ch|] (e) {
|
||||
////}
|
||||
////[|fina/*3*/lly|] {
|
||||
////}
|
||||
|
||||
|
||||
for (var i = 1; i <= test.markers().length; i++) {
|
||||
goTo.marker("" + i);
|
||||
verify.occurrencesAtPositionCount(3);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
}
|
27
tests/cases/fourslash/getOccurrencesTryCatchFinally2.ts
Normal file
27
tests/cases/fourslash/getOccurrencesTryCatchFinally2.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////try {
|
||||
//// [|t/*1*/r/*2*/y|] {
|
||||
//// }
|
||||
//// [|c/*3*/atch|] (x) {
|
||||
//// }
|
||||
////
|
||||
//// try {
|
||||
//// }
|
||||
//// finally {
|
||||
//// }
|
||||
////}
|
||||
////catch (e) {
|
||||
////}
|
||||
////finally {
|
||||
////}
|
||||
|
||||
|
||||
for (var i = 1; i <= test.markers().length; i++) {
|
||||
goTo.marker("" + i);
|
||||
verify.occurrencesAtPositionCount(2);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
}
|
27
tests/cases/fourslash/getOccurrencesTryCatchFinally3.ts
Normal file
27
tests/cases/fourslash/getOccurrencesTryCatchFinally3.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////try {
|
||||
//// try {
|
||||
//// }
|
||||
//// catch (x) {
|
||||
//// }
|
||||
////
|
||||
//// [|t/*1*/r/*2*/y|] {
|
||||
//// }
|
||||
//// [|finall/*3*/y|] {
|
||||
//// }
|
||||
////}
|
||||
////catch (e) {
|
||||
////}
|
||||
////finally {
|
||||
////}
|
||||
|
||||
|
||||
for (var i = 1; i <= test.markers().length; i++) {
|
||||
goTo.marker("" + i);
|
||||
verify.occurrencesAtPositionCount(2);
|
||||
|
||||
test.ranges().forEach(range => {
|
||||
verify.occurrencesAtPositionContains(range, false);
|
||||
});
|
||||
}
|
55
tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts
Normal file
55
tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////t /*1*/ry {
|
||||
//// t/*2*/ry {
|
||||
//// }
|
||||
//// ctch (x) {
|
||||
//// }
|
||||
////
|
||||
//// tr {
|
||||
//// }
|
||||
//// fin/*3*/ally {
|
||||
//// }
|
||||
////}
|
||||
////c/*4*/atch (e) {
|
||||
////}
|
||||
////f/*5*/inally {
|
||||
////}
|
||||
////
|
||||
////// Missing catch variable
|
||||
////t/*6*/ry {
|
||||
////}
|
||||
////catc/*7*/h {
|
||||
////}
|
||||
/////*8*/finally {
|
||||
////}
|
||||
////
|
||||
////// Missing try entirely
|
||||
////cat/*9*/ch (x) {
|
||||
////}
|
||||
////final/*10*/ly {
|
||||
////}
|
||||
|
||||
|
||||
for (var i = 1; i <= test.markers().length; i++) {
|
||||
goTo.marker("" + i);
|
||||
|
||||
switch (i) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
verify.occurrencesAtPositionCount(1);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 9:
|
||||
case 10:
|
||||
verify.occurrencesAtPositionCount(2);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
verify.occurrencesAtPositionCount(3);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////try/*1*/ {
|
||||
//// try/*2*/ {
|
||||
//// }
|
||||
//// catch/*3*/ (x) {
|
||||
//// }
|
||||
////
|
||||
//// try/*4*/ {
|
||||
//// }
|
||||
//// finally/*5*/ {/*8*/
|
||||
//// }
|
||||
////}
|
||||
////catch/*6*/ (e) {
|
||||
////}
|
||||
////finally/*7*/ {
|
||||
////}
|
||||
|
||||
|
||||
for (var i = 1; i <= test.markers().length; i++) {
|
||||
goTo.marker("" + i);
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
}
|
Loading…
Reference in a new issue