Skip to content

Commit 6463880

Browse files
committed
Merge branch 'master' into sigHelp
2 parents b2bca72 + db7e980 commit 6463880

16 files changed

+351
-144
lines changed
3.81 KB
Binary file not shown.
-69.4 KB
Binary file not shown.

doc/spec.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,10 @@ For this switch statement, the compiler will generate the following code.
581581

582582
```TypeScript
583583
switch (op) {
584-
case 0 /* Operator.ADD */ :
584+
case 0 /* Operator.ADD */:
585585
// execute add
586586
break;
587-
case 1 /* Operator.DIV */ :
587+
case 1 /* Operator.DIV */:
588588
// execute div
589589
break;
590590
// ...
@@ -738,7 +738,7 @@ var M;
738738
return s;
739739
}
740740
M.f = f;
741-
})(M||(M={}));
741+
})(M || (M = {}));
742742
```
743743

744744
In this case, the compiler assumes that the module object resides in global variable ‘M’, which may or may not have been initialized to the desired module object.
@@ -1068,7 +1068,7 @@ Type references (section [3.6.2](#3.6.2)) to class and interface types are class
10681068

10691069
### <a name="3.3.2"/>3.3.2 Array Types
10701070

1071-
***Array types*** represent JavaScript arrays with a common element type. Array types are named type references created from the generic interface type ‘Array’ in the global module with the array element type as a type argument. Array type literals (section [Error! Reference source not found.](#Error! Reference source not found.)) provide a shorthand notation for creating such references.
1071+
***Array types*** represent JavaScript arrays with a common element type. Array types are named type references created from the generic interface type ‘Array’ in the global module with the array element type as a type argument. Array type literals (section [3.6.4](#3.6.4)) provide a shorthand notation for creating such references.
10721072

10731073
The declaration of the ‘Array’ interface includes a property ‘length’ and a numeric index signature for the element type, along with other members:
10741074

src/services/services.ts

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,7 +2121,7 @@ module ts {
21212121
}
21222122

21232123
// TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node
2124-
var mappedNode = getNodeAtPosition(sourceFile, TypeScript.end(node) - 1);
2124+
var mappedNode = getTouchingToken(sourceFile, TypeScript.end(node) - 1);
21252125
if (isPunctuation(mappedNode.kind)) {
21262126
mappedNode = mappedNode.parent;
21272127
}
@@ -2358,7 +2358,7 @@ module ts {
23582358

23592359
fileName = TypeScript.switchToForwardSlashes(fileName);
23602360
var sourceFile = getSourceFile(fileName);
2361-
var node = getNodeAtPosition(sourceFile, position);
2361+
var node = getTouchingPropertyName(sourceFile, position);
23622362
if (!node) {
23632363
return undefined;
23642364
}
@@ -2466,7 +2466,7 @@ module ts {
24662466

24672467
fileName = TypeScript.switchToForwardSlashes(fileName);
24682468
var sourceFile = getSourceFile(fileName);
2469-
var node = getNodeAtPosition(sourceFile, position);
2469+
var node = getTouchingWord(sourceFile, position);
24702470
if (!node) {
24712471
return undefined;
24722472
}
@@ -2549,7 +2549,7 @@ module ts {
25492549
filename = TypeScript.switchToForwardSlashes(filename);
25502550
var sourceFile = getSourceFile(filename);
25512551

2552-
var node = getNodeAtPosition(sourceFile, position);
2552+
var node = getTouchingPropertyName(sourceFile, position);
25532553
if (!node) {
25542554
return undefined;
25552555
}
@@ -2613,7 +2613,7 @@ module ts {
26132613
filename = TypeScript.switchToForwardSlashes(filename);
26142614
var sourceFile = getSourceFile(filename);
26152615

2616-
var node = getNodeAtPosition(sourceFile, position);
2616+
var node = getTouchingWord(sourceFile, position);
26172617
if (!node) {
26182618
return undefined;
26192619
}
@@ -3058,7 +3058,7 @@ module ts {
30583058
filename = TypeScript.switchToForwardSlashes(filename);
30593059
var sourceFile = getSourceFile(filename);
30603060

3061-
var node = getNodeAtPosition(sourceFile, position);
3061+
var node = getTouchingPropertyName(sourceFile, position);
30623062
if (!node) {
30633063
return undefined;
30643064
}
@@ -3241,7 +3241,7 @@ module ts {
32413241
forEach(possiblePositions, position => {
32423242
cancellationToken.throwIfCancellationRequested();
32433243

3244-
var node = getNodeAtPosition(sourceFile, position);
3244+
var node = getTouchingWord(sourceFile, position);
32453245
if (!node || node.getWidth() !== labelName.length) {
32463246
return;
32473247
}
@@ -3297,7 +3297,7 @@ module ts {
32973297
forEach(possiblePositions, position => {
32983298
cancellationToken.throwIfCancellationRequested();
32993299

3300-
var referenceLocation = getNodeAtPosition(sourceFile, position);
3300+
var referenceLocation = getTouchingPropertyName(sourceFile, position);
33013301
if (!isValidReferencePosition(referenceLocation, searchText)) {
33023302
return;
33033303
}
@@ -3349,7 +3349,7 @@ module ts {
33493349
forEach(possiblePositions, position => {
33503350
cancellationToken.throwIfCancellationRequested();
33513351

3352-
var node = getNodeAtPosition(sourceFile, position);
3352+
var node = getTouchingWord(sourceFile, position);
33533353

33543354
if (!node || node.kind !== SyntaxKind.SuperKeyword) {
33553355
return;
@@ -3415,7 +3415,7 @@ module ts {
34153415
forEach(possiblePositions, position => {
34163416
cancellationToken.throwIfCancellationRequested();
34173417

3418-
var node = getNodeAtPosition(sourceFile, position);
3418+
var node = getTouchingWord(sourceFile, position);
34193419
if (!node || node.kind !== SyntaxKind.ThisKeyword) {
34203420
return;
34213421
}
@@ -4229,7 +4229,7 @@ module ts {
42294229
var sourceFile = getCurrentSourceFile(filename);
42304230
var result: TypeScript.TextSpan[] = [];
42314231

4232-
var token = getTokenAtPosition(sourceFile, position);
4232+
var token = getTouchingToken(sourceFile, position);
42334233

42344234
if (token.getStart(sourceFile) === position) {
42354235
var matchKind = getMatchingTokenKind(token);
@@ -4513,7 +4513,7 @@ module ts {
45134513
fileName = TypeScript.switchToForwardSlashes(fileName);
45144514
var sourceFile = getSourceFile(fileName);
45154515

4516-
var node = getNodeAtPosition(sourceFile, position);
4516+
var node = getTouchingWord(sourceFile, position);
45174517

45184518
// Can only rename an identifier.
45194519
if (node && node.kind === SyntaxKind.Identifier) {
@@ -4599,26 +4599,58 @@ module ts {
45994599
/// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where
46004600
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
46014601
/// locations where a regexp cannot exist.
4602-
var noRegexTable: boolean[];
4603-
if (!noRegexTable) {
4604-
noRegexTable = [];
4605-
noRegexTable[SyntaxKind.Identifier] = true;
4606-
noRegexTable[SyntaxKind.StringLiteral] = true;
4607-
noRegexTable[SyntaxKind.NumericLiteral] = true;
4608-
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
4609-
noRegexTable[SyntaxKind.ThisKeyword] = true;
4610-
noRegexTable[SyntaxKind.PlusPlusToken] = true;
4611-
noRegexTable[SyntaxKind.MinusMinusToken] = true;
4612-
noRegexTable[SyntaxKind.CloseParenToken] = true;
4613-
noRegexTable[SyntaxKind.CloseBracketToken] = true;
4614-
noRegexTable[SyntaxKind.CloseBraceToken] = true;
4615-
noRegexTable[SyntaxKind.TrueKeyword] = true;
4616-
noRegexTable[SyntaxKind.FalseKeyword] = true;
4602+
var noRegexTable: boolean[] = [];
4603+
noRegexTable[SyntaxKind.Identifier] = true;
4604+
noRegexTable[SyntaxKind.StringLiteral] = true;
4605+
noRegexTable[SyntaxKind.NumericLiteral] = true;
4606+
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
4607+
noRegexTable[SyntaxKind.ThisKeyword] = true;
4608+
noRegexTable[SyntaxKind.PlusPlusToken] = true;
4609+
noRegexTable[SyntaxKind.MinusMinusToken] = true;
4610+
noRegexTable[SyntaxKind.CloseParenToken] = true;
4611+
noRegexTable[SyntaxKind.CloseBracketToken] = true;
4612+
noRegexTable[SyntaxKind.CloseBraceToken] = true;
4613+
noRegexTable[SyntaxKind.TrueKeyword] = true;
4614+
noRegexTable[SyntaxKind.FalseKeyword] = true;
4615+
4616+
function isAccessibilityModifier(kind: SyntaxKind) {
4617+
switch (kind) {
4618+
case SyntaxKind.PublicKeyword:
4619+
case SyntaxKind.PrivateKeyword:
4620+
case SyntaxKind.ProtectedKeyword:
4621+
return true;
4622+
}
4623+
4624+
return false;
4625+
}
4626+
4627+
/** Returns true if 'keyword2' can legally follow 'keyword1' in any language construct. */
4628+
function canFollow(keyword1: SyntaxKind, keyword2: SyntaxKind) {
4629+
if (isAccessibilityModifier(keyword1)) {
4630+
if (keyword2 === SyntaxKind.GetKeyword ||
4631+
keyword2 === SyntaxKind.SetKeyword ||
4632+
keyword2 === SyntaxKind.ConstructorKeyword ||
4633+
keyword2 === SyntaxKind.StaticKeyword) {
4634+
4635+
// Allow things like "public get", "public constructor" and "public static".
4636+
// These are all legal.
4637+
return true;
4638+
}
4639+
4640+
// Any other keyword following "public" is actually an identifier an not a real
4641+
// keyword.
4642+
return false;
4643+
}
4644+
4645+
// Assume any other keyword combination is legal. This can be refined in the future
4646+
// if there are more cases we want the classifier to be better at.
4647+
return true;
46174648
}
46184649

46194650
function getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult {
46204651
var offset = 0;
46214652
var lastTokenOrCommentEnd = 0;
4653+
var token = SyntaxKind.Unknown;
46224654
var lastNonTriviaToken = SyntaxKind.Unknown;
46234655

46244656
// If we're in a string literal, then prepend: "\
@@ -4648,8 +4680,6 @@ module ts {
46484680
entries: []
46494681
};
46504682

4651-
4652-
var token = SyntaxKind.Unknown;
46534683
do {
46544684
token = scanner.scan();
46554685

@@ -4662,6 +4692,13 @@ module ts {
46624692
else if (lastNonTriviaToken === SyntaxKind.DotToken && isKeyword(token)) {
46634693
token = SyntaxKind.Identifier;
46644694
}
4695+
else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
4696+
// We have two keywords in a row. Only treat the second as a keyword if
4697+
// it's a sequence that could legally occur in the language. Otherwise
4698+
// treat it as an identifier. This way, if someone writes "private var"
4699+
// we recognize that 'var' is actually an identifier here.
4700+
token = SyntaxKind.Identifier;
4701+
}
46654702

46664703
lastNonTriviaToken = token;
46674704
}

src/services/utilities.ts

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,54 @@ module ts {
5050
return -1;
5151
}
5252

53-
/** Get a token that contains the position. This is guaranteed to return a token, the position can be in the
54-
* leading trivia or within the token text.
55-
*/
56-
export function getTokenAtPosition(sourceFile: SourceFile, position: number) {
57-
var current: Node = sourceFile;
58-
outer: while (true) {
59-
// find the child that has this
60-
for (var i = 0, n = current.getChildCount(); i < n; i++) {
61-
var child = current.getChildAt(i);
62-
if (child.getFullStart() <= position && position < child.getEnd()) {
63-
current = child;
64-
continue outer;
65-
}
66-
}
67-
return current;
68-
}
53+
/* Gets the token whose text has range [start, end) and
54+
* position >= start and (position < end or (position === end && token is keyword or identifier))
55+
*/
56+
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
57+
return getTouchingToken(sourceFile, position, isWord);
58+
}
59+
60+
/* Gets the token whose text has range [start, end) and position >= start
61+
* and (position < end or (position === end && token is keyword or identifier or numeric\string litera))
62+
*/
63+
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
64+
return getTouchingToken(sourceFile, position, isPropertyName);
65+
}
66+
67+
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
68+
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node {
69+
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition);
6970
}
7071

71-
/** Get the token whose text contains the position, or the containing node. */
72-
export function getNodeAtPosition(sourceFile: SourceFile, position: number) {
72+
/** Returns a token if position is in [start-of-leading-trivia, end) */
73+
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
74+
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined);
75+
}
76+
77+
/** Get the token whose text contains the position */
78+
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node {
7379
var current: Node = sourceFile;
7480
outer: while (true) {
75-
// find the child that has this
76-
for (var i = 0, n = current.getChildCount(); i < n; i++) {
81+
if (isToken(current)) {
82+
// exit early
83+
return current;
84+
}
85+
86+
// find the child that contains 'position'
87+
for (var i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
7788
var child = current.getChildAt(i);
78-
if (child.getStart() <= position && position < child.getEnd()) {
79-
current = child;
80-
continue outer;
89+
var start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile);
90+
if (start <= position) {
91+
if (position < child.getEnd()) {
92+
current = child;
93+
continue outer;
94+
}
95+
else if (includeItemAtEndPosition && child.getEnd() === position) {
96+
var previousToken = findPrecedingToken(position, sourceFile, child);
97+
if (previousToken && includeItemAtEndPosition(previousToken)) {
98+
return previousToken;
99+
}
100+
}
81101
}
82102
}
83103
return current;
@@ -130,8 +150,8 @@ module ts {
130150
}
131151
}
132152

133-
export function findPrecedingToken(position: number, sourceFile: SourceFile): Node {
134-
return find(sourceFile);
153+
export function findPrecedingToken(position: number, sourceFile: SourceFile, startNode?: Node): Node {
154+
return find(startNode || sourceFile);
135155

136156
function findRightmostToken(n: Node): Node {
137157
if (isToken(n)) {
@@ -167,7 +187,7 @@ module ts {
167187
}
168188
}
169189

170-
Debug.assert(n.kind === SyntaxKind.SourceFile);
190+
Debug.assert(startNode || n.kind === SyntaxKind.SourceFile);
171191

172192
// Here we know that none of child token nodes embrace the position,
173193
// the only known case is when position is at the end of the file.
@@ -205,4 +225,16 @@ module ts {
205225
function isToken(n: Node): boolean {
206226
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
207227
}
228+
229+
function isKeyword(n: Node): boolean {
230+
return n.kind >= SyntaxKind.FirstKeyword && n.kind <= SyntaxKind.LastKeyword;
231+
}
232+
233+
function isWord(n: Node): boolean {
234+
return n.kind === SyntaxKind.Identifier || isKeyword(n);
235+
}
236+
237+
function isPropertyName(n: Node): boolean {
238+
return n.kind === SyntaxKind.StringLiteral || n.kind === SyntaxKind.NumericLiteral || isWord(n);
239+
}
208240
}
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);

0 commit comments

Comments
 (0)