Merge branch 'master' into sigHelp

This commit is contained in:
Jason Freeman 2014-10-01 13:10:03 -07:00
commit 64638804f8
16 changed files with 351 additions and 144 deletions

View File

@ -581,10 +581,10 @@ For this switch statement, the compiler will generate the following code.
```TypeScript
switch (op) {
case 0 /* Operator.ADD */ :
case 0 /* Operator.ADD */:
// execute add
break;
case 1 /* Operator.DIV */ :
case 1 /* Operator.DIV */:
// execute div
break;
// ...
@ -738,7 +738,7 @@ var M;
return s;
}
M.f = f;
})(M||(M={}));
})(M || (M = {}));
```
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
### <a name="3.3.2"/>3.3.2 Array Types
***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.
***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.
The declaration of the Array interface includes a property length and a numeric index signature for the element type, along with other members:

View File

@ -2121,7 +2121,7 @@ module ts {
}
// TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node
var mappedNode = getNodeAtPosition(sourceFile, TypeScript.end(node) - 1);
var mappedNode = getTouchingToken(sourceFile, TypeScript.end(node) - 1);
if (isPunctuation(mappedNode.kind)) {
mappedNode = mappedNode.parent;
}
@ -2358,7 +2358,7 @@ module ts {
fileName = TypeScript.switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
return undefined;
}
@ -2466,7 +2466,7 @@ module ts {
fileName = TypeScript.switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingWord(sourceFile, position);
if (!node) {
return undefined;
}
@ -2549,7 +2549,7 @@ module ts {
filename = TypeScript.switchToForwardSlashes(filename);
var sourceFile = getSourceFile(filename);
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
return undefined;
}
@ -2613,7 +2613,7 @@ module ts {
filename = TypeScript.switchToForwardSlashes(filename);
var sourceFile = getSourceFile(filename);
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingWord(sourceFile, position);
if (!node) {
return undefined;
}
@ -3058,7 +3058,7 @@ module ts {
filename = TypeScript.switchToForwardSlashes(filename);
var sourceFile = getSourceFile(filename);
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
return undefined;
}
@ -3241,7 +3241,7 @@ module ts {
forEach(possiblePositions, position => {
cancellationToken.throwIfCancellationRequested();
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingWord(sourceFile, position);
if (!node || node.getWidth() !== labelName.length) {
return;
}
@ -3297,7 +3297,7 @@ module ts {
forEach(possiblePositions, position => {
cancellationToken.throwIfCancellationRequested();
var referenceLocation = getNodeAtPosition(sourceFile, position);
var referenceLocation = getTouchingPropertyName(sourceFile, position);
if (!isValidReferencePosition(referenceLocation, searchText)) {
return;
}
@ -3349,7 +3349,7 @@ module ts {
forEach(possiblePositions, position => {
cancellationToken.throwIfCancellationRequested();
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingWord(sourceFile, position);
if (!node || node.kind !== SyntaxKind.SuperKeyword) {
return;
@ -3415,7 +3415,7 @@ module ts {
forEach(possiblePositions, position => {
cancellationToken.throwIfCancellationRequested();
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingWord(sourceFile, position);
if (!node || node.kind !== SyntaxKind.ThisKeyword) {
return;
}
@ -4229,7 +4229,7 @@ module ts {
var sourceFile = getCurrentSourceFile(filename);
var result: TypeScript.TextSpan[] = [];
var token = getTokenAtPosition(sourceFile, position);
var token = getTouchingToken(sourceFile, position);
if (token.getStart(sourceFile) === position) {
var matchKind = getMatchingTokenKind(token);
@ -4513,7 +4513,7 @@ module ts {
fileName = TypeScript.switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var node = getNodeAtPosition(sourceFile, position);
var node = getTouchingWord(sourceFile, position);
// Can only rename an identifier.
if (node && node.kind === SyntaxKind.Identifier) {
@ -4599,26 +4599,58 @@ module ts {
/// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
/// locations where a regexp cannot exist.
var noRegexTable: boolean[];
if (!noRegexTable) {
noRegexTable = [];
noRegexTable[SyntaxKind.Identifier] = true;
noRegexTable[SyntaxKind.StringLiteral] = true;
noRegexTable[SyntaxKind.NumericLiteral] = true;
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
noRegexTable[SyntaxKind.ThisKeyword] = true;
noRegexTable[SyntaxKind.PlusPlusToken] = true;
noRegexTable[SyntaxKind.MinusMinusToken] = true;
noRegexTable[SyntaxKind.CloseParenToken] = true;
noRegexTable[SyntaxKind.CloseBracketToken] = true;
noRegexTable[SyntaxKind.CloseBraceToken] = true;
noRegexTable[SyntaxKind.TrueKeyword] = true;
noRegexTable[SyntaxKind.FalseKeyword] = true;
var noRegexTable: boolean[] = [];
noRegexTable[SyntaxKind.Identifier] = true;
noRegexTable[SyntaxKind.StringLiteral] = true;
noRegexTable[SyntaxKind.NumericLiteral] = true;
noRegexTable[SyntaxKind.RegularExpressionLiteral] = true;
noRegexTable[SyntaxKind.ThisKeyword] = true;
noRegexTable[SyntaxKind.PlusPlusToken] = true;
noRegexTable[SyntaxKind.MinusMinusToken] = true;
noRegexTable[SyntaxKind.CloseParenToken] = true;
noRegexTable[SyntaxKind.CloseBracketToken] = true;
noRegexTable[SyntaxKind.CloseBraceToken] = true;
noRegexTable[SyntaxKind.TrueKeyword] = true;
noRegexTable[SyntaxKind.FalseKeyword] = true;
function isAccessibilityModifier(kind: SyntaxKind) {
switch (kind) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
return true;
}
return false;
}
/** Returns true if 'keyword2' can legally follow 'keyword1' in any language construct. */
function canFollow(keyword1: SyntaxKind, keyword2: SyntaxKind) {
if (isAccessibilityModifier(keyword1)) {
if (keyword2 === SyntaxKind.GetKeyword ||
keyword2 === SyntaxKind.SetKeyword ||
keyword2 === SyntaxKind.ConstructorKeyword ||
keyword2 === SyntaxKind.StaticKeyword) {
// Allow things like "public get", "public constructor" and "public static".
// These are all legal.
return true;
}
// Any other keyword following "public" is actually an identifier an not a real
// keyword.
return false;
}
// Assume any other keyword combination is legal. This can be refined in the future
// if there are more cases we want the classifier to be better at.
return true;
}
function getClassificationsForLine(text: string, lexState: EndOfLineState): ClassificationResult {
var offset = 0;
var lastTokenOrCommentEnd = 0;
var token = SyntaxKind.Unknown;
var lastNonTriviaToken = SyntaxKind.Unknown;
// If we're in a string literal, then prepend: "\
@ -4648,8 +4680,6 @@ module ts {
entries: []
};
var token = SyntaxKind.Unknown;
do {
token = scanner.scan();
@ -4662,6 +4692,13 @@ module ts {
else if (lastNonTriviaToken === SyntaxKind.DotToken && isKeyword(token)) {
token = SyntaxKind.Identifier;
}
else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
// We have two keywords in a row. Only treat the second as a keyword if
// it's a sequence that could legally occur in the language. Otherwise
// treat it as an identifier. This way, if someone writes "private var"
// we recognize that 'var' is actually an identifier here.
token = SyntaxKind.Identifier;
}
lastNonTriviaToken = token;
}

View File

@ -50,34 +50,54 @@ module ts {
return -1;
}
/** Get a token that contains the position. This is guaranteed to return a token, the position can be in the
* leading trivia or within the token text.
*/
export function getTokenAtPosition(sourceFile: SourceFile, position: number) {
var current: Node = sourceFile;
outer: while (true) {
// find the child that has this
for (var i = 0, n = current.getChildCount(); i < n; i++) {
var child = current.getChildAt(i);
if (child.getFullStart() <= position && position < child.getEnd()) {
current = child;
continue outer;
}
}
return current;
}
/* Gets the token whose text has range [start, end) and
* position >= start and (position < end or (position === end && token is keyword or identifier))
*/
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, isWord);
}
/** Get the token whose text contains the position, or the containing node. */
export function getNodeAtPosition(sourceFile: SourceFile, position: number) {
/* Gets the token whose text has range [start, end) and position >= start
* and (position < end or (position === end && token is keyword or identifier or numeric\string litera))
*/
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, isPropertyName);
}
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition);
}
/** Returns a token if position is in [start-of-leading-trivia, end) */
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined);
}
/** Get the token whose text contains the position */
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node {
var current: Node = sourceFile;
outer: while (true) {
// find the child that has this
for (var i = 0, n = current.getChildCount(); i < n; i++) {
if (isToken(current)) {
// exit early
return current;
}
// find the child that contains 'position'
for (var i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
var child = current.getChildAt(i);
if (child.getStart() <= position && position < child.getEnd()) {
current = child;
continue outer;
var start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile);
if (start <= position) {
if (position < child.getEnd()) {
current = child;
continue outer;
}
else if (includeItemAtEndPosition && child.getEnd() === position) {
var previousToken = findPrecedingToken(position, sourceFile, child);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
}
return current;
@ -130,8 +150,8 @@ module ts {
}
}
export function findPrecedingToken(position: number, sourceFile: SourceFile): Node {
return find(sourceFile);
export function findPrecedingToken(position: number, sourceFile: SourceFile, startNode?: Node): Node {
return find(startNode || sourceFile);
function findRightmostToken(n: Node): Node {
if (isToken(n)) {
@ -167,7 +187,7 @@ module ts {
}
}
Debug.assert(n.kind === SyntaxKind.SourceFile);
Debug.assert(startNode || n.kind === SyntaxKind.SourceFile);
// Here we know that none of child token nodes embrace the position,
// the only known case is when position is at the end of the file.
@ -205,4 +225,16 @@ module ts {
function isToken(n: Node): boolean {
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
}
function isKeyword(n: Node): boolean {
return n.kind >= SyntaxKind.FirstKeyword && n.kind <= SyntaxKind.LastKeyword;
}
function isWord(n: Node): boolean {
return n.kind === SyntaxKind.Identifier || isKeyword(n);
}
function isPropertyName(n: Node): boolean {
return n.kind === SyntaxKind.StringLiteral || n.kind === SyntaxKind.NumericLiteral || isWord(n);
}
}

View File

@ -0,0 +1,42 @@
/// <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*/ { }
function verifyOccurencesAtMarker(marker: string, count: number) {
goTo.marker(marker);
verify.occurrencesAtPositionCount(count);
}
verifyOccurencesAtMarker("1", 7);
verifyOccurencesAtMarker("2", 2);
verifyOccurencesAtMarker("3", 2);
verifyOccurencesAtMarker("4", 2);
verifyOccurencesAtMarker("5", 2);
verifyOccurencesAtMarker("6", 1);
verifyOccurencesAtMarker("7", 1);
verifyOccurencesAtMarker("8", 7);
verifyOccurencesAtMarker("9", 7);
verifyOccurencesAtMarker("10", 7);
verifyOccurencesAtMarker("11", 7);
verifyOccurencesAtMarker("12", 7);
verifyOccurencesAtMarker("13", 7);

View File

@ -1,29 +0,0 @@
/// <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);
});

View File

@ -0,0 +1,70 @@
/// <reference path='fourslash.ts' />
////var arr = [1, 2, 3, 4];
////label1: for (var n in arr) {
//// break;
//// continue;
//// break label1;
//// continue label1;
////
//// label2: for (var i = 0; i < arr[n]; i++) {
//// break label1;
//// continue label1;
////
//// break;
//// continue;
//// break label2;
//// continue label2;
////
//// function foo() {
//// label3: while (true) {
//// break;
//// continue;
//// break label3;
//// continue label3;
////
//// // these cross function boundaries
//// br/*1*/eak label1;
//// cont/*2*/inue label1;
//// bre/*3*/ak label2;
//// c/*4*/ontinue label2;
////
//// label4: do {
//// break;
//// continue;
//// break label4;
//// continue label4;
////
//// break label3;
//// continue label3;
////
//// switch (10) {
//// case 1:
//// case 2:
//// break;
//// break label4;
//// default:
//// continue;
//// }
////
//// // these cross function boundaries
//// br/*5*/eak label1;
//// co/*6*/ntinue label1;
//// br/*7*/eak label2;
//// con/*8*/tinue label2;
//// () => { b/*9*/reak; }
//// } while (true)
//// }
//// }
//// }
////}
////
////label5: while (true) break label5;
////
////label7: while (true) co/*10*/ntinue label5;
test.markers().forEach(m => {
goTo.position(m.position);
verify.occurrencesAtPositionCount(0);
});

View File

@ -19,7 +19,15 @@
//// return/*7*/ true;
////}
test.markers().forEach(m => {
goTo.position(m.position, m.fileName)
verify.occurrencesAtPositionCount(0);
});
function verifyOccurencesAtMarker(marker: string, count: number) {
goTo.marker(marker);
verify.occurrencesAtPositionCount(count);
}
verifyOccurencesAtMarker("1", 4);
verifyOccurencesAtMarker("2", 4);
verifyOccurencesAtMarker("3", 4);
verifyOccurencesAtMarker("4", 4);
verifyOccurencesAtMarker("5", 1);
verifyOccurencesAtMarker("6", 3);
verifyOccurencesAtMarker("7", 3);

View File

@ -0,0 +1,40 @@
/// <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*/:
////}
function verifyOccurencesAtMarker(marker: string, count: number) {
goTo.marker(marker);
verify.occurrencesAtPositionCount(count);
}
verifyOccurencesAtMarker("1", 9);
verifyOccurencesAtMarker("2", 9);
verifyOccurencesAtMarker("3", 9);
verifyOccurencesAtMarker("4", 9);
verifyOccurencesAtMarker("5", 9);
verifyOccurencesAtMarker("6", 6);
verifyOccurencesAtMarker("7", 6);
verifyOccurencesAtMarker("8", 6);
verifyOccurencesAtMarker("9", 6);
verifyOccurencesAtMarker("10", 6);
verifyOccurencesAtMarker("11", 9);
verifyOccurencesAtMarker("12", 9);
verifyOccurencesAtMarker("13", 9);
verifyOccurencesAtMarker("14", 0);

View File

@ -1,25 +0,0 @@
/// <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);
}

View File

@ -142,8 +142,14 @@
////}
test.markers().forEach(m => {
goTo.position(m.position, m.fileName)
function verifyOccurencesAtMarker(marker: string, count: number) {
goTo.marker(marker);
verify.occurrencesAtPositionCount(count);
}
verify.occurrencesAtPositionCount(0);
});
verifyOccurencesAtMarker("1", 2);
verifyOccurencesAtMarker("2", 6);
verifyOccurencesAtMarker("3", 1);
verifyOccurencesAtMarker("4", 1);
verifyOccurencesAtMarker("5", 1);
verifyOccurencesAtMarker("6", 0);

View File

@ -0,0 +1,30 @@
/// <reference path='fourslash.ts' />
////try/*1*/ {
//// try/*2*/ {
//// }
//// catch/*3*/ (x) {
//// }
////
//// try/*4*/ {
//// }
//// finally/*5*/ {/*8*/
//// }
////}
////catch/*6*/ (e) {
////}
////finally/*7*/ {
////}
function verifyOccurencesAtMarker(marker: string, count: number) {
goTo.marker(marker);
verify.occurrencesAtPositionCount(count);
}
verifyOccurencesAtMarker("1", 3);
verifyOccurencesAtMarker("2", 2);
verifyOccurencesAtMarker("3", 2);
verifyOccurencesAtMarker("4", 2);
verifyOccurencesAtMarker("5", 2);
verifyOccurencesAtMarker("6", 3);
verifyOccurencesAtMarker("7", 3);
verifyOccurencesAtMarker("8", 0);

View File

@ -1,23 +0,0 @@
/// <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);
}

View File

@ -5,7 +5,12 @@
// @Filename: Consumption.ts
//// var n = new /*1*/c();
//// var n = new c/*3*/();
goTo.marker('1');
goTo.definition();
verify.caretAtMarker('2');
goTo.marker('3');
goTo.definition();
verify.caretAtMarker('2');

View File

@ -217,5 +217,19 @@ describe('Colorization', function () {
keyword("var"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("classifies multiple keywords properly", function () {
test("public static",
ts.EndOfLineState.Start,
keyword("public"),
keyword("static"),
finalEndOfLineState(ts.EndOfLineState.Start));
test("public var",
ts.EndOfLineState.Start,
keyword("public"),
identifier("var"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
});
});