Skip to content

Commit c456ef4

Browse files
committed
renamed getNodeAtPosition to getExactTokenAtPosition, added predicate parameter that will determine if token with end === position should be returned
1 parent 5fb50d8 commit c456ef4

12 files changed

+286
-110
lines changed

src/services/services.ts

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,7 +2088,7 @@ module ts {
20882088
}
20892089

20902090
// TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node
2091-
var mappedNode = getNodeAtPosition(sourceFile, TypeScript.end(node) - 1);
2091+
var mappedNode = getExactTokenAtPosition(sourceFile, TypeScript.end(node) - 1, /*includeItemAtEndPosition*/ undefined);
20922092
if (isPunctuation(mappedNode.kind)) {
20932093
mappedNode = mappedNode.parent;
20942094
}
@@ -2325,7 +2325,7 @@ module ts {
23252325

23262326
fileName = TypeScript.switchToForwardSlashes(fileName);
23272327
var sourceFile = getSourceFile(fileName);
2328-
var node = getNodeAtPosition(sourceFile, position);
2328+
var node = getExactTokenAtPosition(sourceFile, position, /*includeItemAtEndPosition*/ undefined);
23292329
if (!node) {
23302330
return undefined;
23312331
}
@@ -2434,7 +2434,7 @@ module ts {
24342434

24352435
fileName = TypeScript.switchToForwardSlashes(fileName);
24362436
var sourceFile = getSourceFile(fileName);
2437-
var node = getNodeAtPosition(sourceFile, position);
2437+
var node = getExactTokenAtPosition(sourceFile, position, /*includeItemAtEndPosition*/ undefined);
24382438
if (!node) {
24392439
return undefined;
24402440
}
@@ -2512,12 +2512,25 @@ module ts {
25122512
return false;
25132513
}
25142514

2515+
function isValidGotoDefinitionTarget(n: Node): boolean {
2516+
switch (n.kind) {
2517+
case SyntaxKind.Identifier:
2518+
case SyntaxKind.SuperKeyword:
2519+
case SyntaxKind.ThisKeyword:
2520+
case SyntaxKind.NumericLiteral:
2521+
case SyntaxKind.StringLiteral:
2522+
return true;
2523+
default:
2524+
return false;
2525+
}
2526+
}
2527+
25152528
synchronizeHostData();
25162529

25172530
filename = TypeScript.switchToForwardSlashes(filename);
25182531
var sourceFile = getSourceFile(filename);
25192532

2520-
var node = getNodeAtPosition(sourceFile, position);
2533+
var node = getExactTokenAtPosition(sourceFile, position, isValidGotoDefinitionTarget);
25212534
if (!node) {
25222535
return undefined;
25232536
}
@@ -2581,7 +2594,7 @@ module ts {
25812594
filename = TypeScript.switchToForwardSlashes(filename);
25822595
var sourceFile = getSourceFile(filename);
25832596

2584-
var node = getNodeAtPosition(sourceFile, position);
2597+
var node = getExactTokenAtPosition(sourceFile, position, isValidFindOccurencesTarget);
25852598
if (!node) {
25862599
return undefined;
25872600
}
@@ -2647,6 +2660,32 @@ module ts {
26472660

26482661
return undefined;
26492662

2663+
function isValidFindOccurencesTarget(n: Node): boolean {
2664+
switch (n.kind) {
2665+
case SyntaxKind.Identifier:
2666+
case SyntaxKind.SuperKeyword:
2667+
case SyntaxKind.ThisKeyword:
2668+
case SyntaxKind.IfKeyword:
2669+
case SyntaxKind.ElseKeyword:
2670+
case SyntaxKind.ReturnKeyword:
2671+
case SyntaxKind.TryKeyword:
2672+
case SyntaxKind.CatchKeyword:
2673+
case SyntaxKind.FinallyKeyword:
2674+
case SyntaxKind.SwitchKeyword:
2675+
case SyntaxKind.CaseKeyword:
2676+
case SyntaxKind.DefaultKeyword:
2677+
case SyntaxKind.BreakKeyword:
2678+
case SyntaxKind.ContinueKeyword:
2679+
case SyntaxKind.ForKeyword:
2680+
case SyntaxKind.WhileKeyword:
2681+
case SyntaxKind.DoKeyword:
2682+
case SyntaxKind.Constructor:
2683+
return true;
2684+
default:
2685+
return false;
2686+
}
2687+
}
2688+
26502689
function getIfElseOccurrences(ifStatement: IfStatement): ReferenceEntry[] {
26512690
var keywords: Node[] = [];
26522691

@@ -2903,7 +2942,7 @@ module ts {
29032942
filename = TypeScript.switchToForwardSlashes(filename);
29042943
var sourceFile = getSourceFile(filename);
29052944

2906-
var node = getNodeAtPosition(sourceFile, position);
2945+
var node = getExactTokenAtPosition(sourceFile, position, /*includeItemAtEndPosition*/ undefined);
29072946
if (!node) {
29082947
return undefined;
29092948
}
@@ -3086,7 +3125,7 @@ module ts {
30863125
forEach(possiblePositions, position => {
30873126
cancellationToken.throwIfCancellationRequested();
30883127

3089-
var node = getNodeAtPosition(sourceFile, position);
3128+
var node = getExactTokenAtPosition(sourceFile, position, /*includeItemAtEndPosition*/ undefined);
30903129
if (!node || node.getWidth() !== labelName.length) {
30913130
return;
30923131
}
@@ -3142,7 +3181,7 @@ module ts {
31423181
forEach(possiblePositions, position => {
31433182
cancellationToken.throwIfCancellationRequested();
31443183

3145-
var referenceLocation = getNodeAtPosition(sourceFile, position);
3184+
var referenceLocation = getExactTokenAtPosition(sourceFile, position, /*includeItemAtEndPosition*/ undefined);
31463185
if (!isValidReferencePosition(referenceLocation, searchText)) {
31473186
return;
31483187
}
@@ -3194,7 +3233,7 @@ module ts {
31943233
forEach(possiblePositions, position => {
31953234
cancellationToken.throwIfCancellationRequested();
31963235

3197-
var node = getNodeAtPosition(sourceFile, position);
3236+
var node = getExactTokenAtPosition(sourceFile, position, n => n.kind === SyntaxKind.SuperKeyword);
31983237

31993238
if (!node || node.kind !== SyntaxKind.SuperKeyword) {
32003239
return;
@@ -3260,7 +3299,7 @@ module ts {
32603299
forEach(possiblePositions, position => {
32613300
cancellationToken.throwIfCancellationRequested();
32623301

3263-
var node = getNodeAtPosition(sourceFile, position);
3302+
var node = getExactTokenAtPosition(sourceFile, position, n => n.kind === SyntaxKind.ThisKeyword);
32643303
if (!node || node.kind !== SyntaxKind.ThisKeyword) {
32653304
return;
32663305
}
@@ -4022,7 +4061,7 @@ module ts {
40224061
var sourceFile = getCurrentSourceFile(filename);
40234062
var result: TypeScript.TextSpan[] = [];
40244063

4025-
var token = getTokenAtPosition(sourceFile, position);
4064+
var token = getTokenContainingPosition(sourceFile, position);
40264065

40274066
if (token.getStart(sourceFile) === position) {
40284067
var matchKind = getMatchingTokenKind(token);
@@ -4178,7 +4217,7 @@ module ts {
41784217

41794218
// OK, we have found a match in the file. This is only an acceptable match if
41804219
// it is contained within a comment.
4181-
var token = getTokenAtPosition(sourceFile, matchPosition);
4220+
var token = getTokenContainingPosition(sourceFile, matchPosition);
41824221

41834222
if (token.getStart() <= matchPosition && matchPosition < token.getEnd()) {
41844223
// match was within the token itself. Not in the comment. Keep searching
@@ -4306,7 +4345,7 @@ module ts {
43064345
fileName = TypeScript.switchToForwardSlashes(fileName);
43074346
var sourceFile = getSourceFile(fileName);
43084347

4309-
var node = getNodeAtPosition(sourceFile, position);
4348+
var node = getExactTokenAtPosition(sourceFile, position, n => n.kind === SyntaxKind.Identifier);
43104349

43114350
// Can only rename an identifier.
43124351
if (node && node.kind === SyntaxKind.Identifier) {

src/services/utilities.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ module ts {
4949
/** Get a token that contains the position. This is guaranteed to return a token, the position can be in the
5050
* leading trivia or within the token text.
5151
*/
52-
export function getTokenAtPosition(sourceFile: SourceFile, position: number) {
52+
export function getTokenContainingPosition(sourceFile: SourceFile, position: number) {
5353
var current: Node = sourceFile;
5454
outer: while (true) {
5555
// find the child that has this
@@ -64,16 +64,29 @@ module ts {
6464
}
6565
}
6666

67-
/** Get the token whose text contains the position, or the containing node. */
68-
export function getNodeAtPosition(sourceFile: SourceFile, position: number) {
67+
/** Get the token whose text contains the position */
68+
export function getExactTokenAtPosition(sourceFile: SourceFile, position: number, includeItemAtEndPosition: (n: Node) => boolean) {
6969
var current: Node = sourceFile;
7070
outer: while (true) {
71-
// find the child that has this
72-
for (var i = 0, n = current.getChildCount(); i < n; i++) {
71+
if (isToken(current)) {
72+
// exit early
73+
return current;
74+
}
75+
76+
// find the child that contains 'position'
77+
for (var i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
7378
var child = current.getChildAt(i);
74-
if (child.getStart() <= position && position < child.getEnd()) {
75-
current = child;
76-
continue outer;
79+
if (child.getStart(sourceFile) <= position) {
80+
if (position < child.getEnd()) {
81+
current = child;
82+
continue outer;
83+
}
84+
else if (includeItemAtEndPosition && child.getEnd() === position) {
85+
var previousToken = findPrecedingToken(position, sourceFile, child);
86+
if (previousToken && includeItemAtEndPosition(previousToken)) {
87+
return previousToken;
88+
}
89+
}
7790
}
7891
}
7992
return current;
@@ -91,7 +104,7 @@ module ts {
91104
export function findTokenOnLeftOfPosition(file: SourceFile, position: number): Node {
92105
// Ideally, getTokenAtPosition should return a token. However, it is currently
93106
// broken, so we do a check to make sure the result was indeed a token.
94-
var tokenAtPosition = getTokenAtPosition(file, position);
107+
var tokenAtPosition = getTokenContainingPosition(file, position);
95108
if (isToken(tokenAtPosition) && position > tokenAtPosition.getStart(file) && position < tokenAtPosition.getEnd()) {
96109
return tokenAtPosition;
97110
}
@@ -126,8 +139,8 @@ module ts {
126139
}
127140
}
128141

129-
export function findPrecedingToken(position: number, sourceFile: SourceFile): Node {
130-
return find(sourceFile);
142+
export function findPrecedingToken(position: number, sourceFile: SourceFile, startNode?: Node): Node {
143+
return find(startNode || sourceFile);
131144

132145
function findRightmostToken(n: Node): Node {
133146
if (isToken(n)) {
@@ -163,7 +176,7 @@ module ts {
163176
}
164177
}
165178

166-
Debug.assert(n.kind === SyntaxKind.SourceFile);
179+
Debug.assert(startNode || n.kind === SyntaxKind.SourceFile);
167180

168181
// Here we know that none of child token nodes embrace the position,
169182
// the only known case is when position is at the end of the file.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
function verifyOccurencesAtMarker(marker: string, count: number) {
26+
goTo.marker(marker);
27+
verify.occurrencesAtPositionCount(count);
28+
}
29+
30+
verifyOccurencesAtMarker("1", 7);
31+
verifyOccurencesAtMarker("2", 2);
32+
verifyOccurencesAtMarker("3", 2);
33+
verifyOccurencesAtMarker("4", 2);
34+
verifyOccurencesAtMarker("5", 2);
35+
verifyOccurencesAtMarker("6", 1);
36+
verifyOccurencesAtMarker("7", 1);
37+
verifyOccurencesAtMarker("8", 7);
38+
verifyOccurencesAtMarker("9", 7);
39+
verifyOccurencesAtMarker("10", 7);
40+
verifyOccurencesAtMarker("11", 7);
41+
verifyOccurencesAtMarker("12", 7);
42+
verifyOccurencesAtMarker("13", 7);

tests/cases/fourslash/getOccurrencesIfElseNegatives.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////var arr = [1, 2, 3, 4];
4+
////label1: for (var n in arr) {
5+
//// break;
6+
//// continue;
7+
//// break label1;
8+
//// continue label1;
9+
////
10+
//// label2: for (var i = 0; i < arr[n]; i++) {
11+
//// break label1;
12+
//// continue label1;
13+
////
14+
//// break;
15+
//// continue;
16+
//// break label2;
17+
//// continue label2;
18+
////
19+
//// function foo() {
20+
//// label3: while (true) {
21+
//// break;
22+
//// continue;
23+
//// break label3;
24+
//// continue label3;
25+
////
26+
//// // these cross function boundaries
27+
//// br/*1*/eak label1;
28+
//// cont/*2*/inue label1;
29+
//// bre/*3*/ak label2;
30+
//// c/*4*/ontinue label2;
31+
////
32+
//// label4: do {
33+
//// break;
34+
//// continue;
35+
//// break label4;
36+
//// continue label4;
37+
////
38+
//// break label3;
39+
//// continue label3;
40+
////
41+
//// switch (10) {
42+
//// case 1:
43+
//// case 2:
44+
//// break;
45+
//// break label4;
46+
//// default:
47+
//// continue;
48+
//// }
49+
////
50+
//// // these cross function boundaries
51+
//// br/*5*/eak label1;
52+
//// co/*6*/ntinue label1;
53+
//// br/*7*/eak label2;
54+
//// con/*8*/tinue label2;
55+
//// () => { b/*9*/reak; }
56+
//// } while (true)
57+
//// }
58+
//// }
59+
//// }
60+
////}
61+
////
62+
////label5: while (true) break label5;
63+
////
64+
////label7: while (true) co/*10*/ntinue label5;
65+
66+
test.markers().forEach(m => {
67+
goTo.position(m.position);
68+
69+
verify.occurrencesAtPositionCount(0);
70+
});

0 commit comments

Comments
 (0)