Merge pull request #1535 from Microsoft/mergeMarkers1

Provide better error recovery when we encounter merge markers in the source.
This commit is contained in:
CyrusNajmabadi
2014-12-29 18:57:58 -08:00
12 changed files with 325 additions and 155 deletions

View File

@@ -23,10 +23,10 @@ module ts {
export interface StringSet extends Map<any> { }
export function forEach<T, U>(array: T[], callback: (element: T) => U): U {
export function forEach<T, U>(array: T[], callback: (element: T, index: number) => U): U {
if (array) {
for (var i = 0, len = array.length; i < len; i++) {
var result = callback(array[i]);
var result = callback(array[i], i);
if (result) {
return result;
}

View File

@@ -7,10 +7,6 @@ module ts {
(message: DiagnosticMessage, length: number): void;
}
export interface CommentCallback {
(pos: number, end: number): void;
}
export interface Scanner {
getStartPos(): number;
getToken(): SyntaxKind;
@@ -396,8 +392,10 @@ module ts {
var mergeConflictMarkerLength = "<<<<<<<".length;
function isConflictMarkerTrivia(text: string, pos: number) {
Debug.assert(pos >= 0);
// Conflict markers must be at the start of a line.
if (pos > 0 && isLineBreak(text.charCodeAt(pos - 1))) {
if (pos === 0 || isLineBreak(text.charCodeAt(pos - 1))) {
var ch = text.charCodeAt(pos);
if ((pos + mergeConflictMarkerLength) < text.length) {
@@ -415,10 +413,31 @@ module ts {
return false;
}
function scanConflictMarkerTrivia(text: string, pos: number) {
function scanConflictMarkerTrivia(text: string, pos: number, error?: ErrorCallback) {
if (error) {
error(Diagnostics.Merge_conflict_marker_encountered, mergeConflictMarkerLength);
}
var ch = text.charCodeAt(pos);
var len = text.length;
while (pos < len && !isLineBreak(text.charCodeAt(pos))) {
pos++;
if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) {
while (pos < len && !isLineBreak(text.charCodeAt(pos))) {
pos++;
}
}
else {
Debug.assert(ch === CharacterCodes.equals);
// Consume everything from the start of the mid-conlict marker to the start of the next
// end-conflict marker.
while (pos < len) {
var ch = text.charCodeAt(pos);
if (ch === CharacterCodes.greaterThan && isConflictMarkerTrivia(text, pos)) {
break;
}
pos++;
}
}
return pos;
@@ -1057,8 +1076,7 @@ module ts {
return pos++, token = SyntaxKind.SemicolonToken;
case CharacterCodes.lessThan:
if (isConflictMarkerTrivia(text, pos)) {
mergeConflictError();
pos = scanConflictMarkerTrivia(text, pos);
pos = scanConflictMarkerTrivia(text, pos, error);
if (skipTrivia) {
continue;
}
@@ -1079,8 +1097,7 @@ module ts {
return pos++, token = SyntaxKind.LessThanToken;
case CharacterCodes.equals:
if (isConflictMarkerTrivia(text, pos)) {
mergeConflictError();
pos = scanConflictMarkerTrivia(text, pos);
pos = scanConflictMarkerTrivia(text, pos, error);
if (skipTrivia) {
continue;
}
@@ -1101,8 +1118,7 @@ module ts {
return pos++, token = SyntaxKind.EqualsToken;
case CharacterCodes.greaterThan:
if (isConflictMarkerTrivia(text, pos)) {
mergeConflictError();
pos = scanConflictMarkerTrivia(text, pos);
pos = scanConflictMarkerTrivia(text, pos, error);
if (skipTrivia) {
continue;
}
@@ -1171,10 +1187,6 @@ module ts {
}
}
function mergeConflictError() {
error(Diagnostics.Merge_conflict_marker_encountered, mergeConflictMarkerLength);
}
function reScanGreaterToken(): SyntaxKind {
if (token === SyntaxKind.GreaterThanToken) {
if (text.charCodeAt(pos) === CharacterCodes.greaterThan) {

View File

@@ -16,6 +16,8 @@ module ts {
MultiLineCommentTrivia,
NewLineTrivia,
WhitespaceTrivia,
// We detect and provide better error recovery when we encounter a git merge marker. This
// allows us to edit files with git-conflict markers in them in a much more pleasant manner.
ConflictMarkerTrivia,
// Literals
NumericLiteral,
@@ -272,8 +274,6 @@ module ts {
LastLiteralToken = NoSubstitutionTemplateLiteral,
FirstTemplateToken = NoSubstitutionTemplateLiteral,
LastTemplateToken = TemplateTail,
FirstOperator = SemicolonToken,
LastOperator = CaretEqualsToken,
FirstBinaryOperator = LessThanToken,
LastBinaryOperator = CaretEqualsToken,
FirstNode = QualifiedName,

View File

@@ -125,10 +125,8 @@ module ts.formatting {
static Any: TokenRange = TokenRange.AllTokens();
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
static Operators = TokenRange.FromRange(SyntaxKind.FirstOperator, SyntaxKind.LastOperator);
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword]);
static ReservedKeywords = TokenRange.FromRange(SyntaxKind.FirstFutureReservedWord, SyntaxKind.LastFutureReservedWord);
static UnaryPrefixOperators = TokenRange.FromTokens([SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);

View File

@@ -2155,7 +2155,7 @@ module ts {
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
displayName = displayName.substring(1, displayName.length - 1);
}
var isValid = isIdentifierStart(displayName.charCodeAt(0), target);
for (var i = 1, n = displayName.length; isValid && i < n; i++) {
isValid = isIdentifierPart(displayName.charCodeAt(i), target);
@@ -2206,7 +2206,7 @@ module ts {
// Completion not allowed inside comments, bail out if this is the case
var insideComment = isInsideComment(sourceFile, currentToken, position);
host.log("getCompletionsAtPosition: Is inside comment: " + (new Date().getTime() - start));
if (insideComment) {
host.log("Returning an empty list because completion was inside a comment.");
return undefined;
@@ -2593,7 +2593,7 @@ module ts {
if (flags & SymbolFlags.TypeAlias) return ScriptElementKind.typeElement;
if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement;
if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
var result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, flags, typeResolver, location);
if (result === ScriptElementKind.unknown) {
if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
@@ -2656,7 +2656,7 @@ module ts {
return ScriptElementKind.unknown;
}
function getTypeKind(type: Type): string {
var flags = type.getFlags();
@@ -2730,7 +2730,7 @@ module ts {
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
var right = (<PropertyAccessExpression>location.parent).name;
// Either the location is on the right of a property access, or on the left and the right is missing
if (right === location || (right && right.getFullWidth() === 0)){
if (right === location || (right && right.getFullWidth() === 0)) {
location = location.parent;
}
}
@@ -2974,7 +2974,7 @@ module ts {
symbolFlags & SymbolFlags.Method ||
symbolFlags & SymbolFlags.Constructor ||
symbolFlags & SymbolFlags.Signature ||
symbolFlags & SymbolFlags.Accessor ||
symbolFlags & SymbolFlags.Accessor ||
symbolKind === ScriptElementKind.memberFunctionElement) {
var allSignatures = type.getCallSignatures();
addSignatureDisplayParts(allSignatures[0], allSignatures);
@@ -3333,7 +3333,7 @@ module ts {
if (!hasKind(ifStatement.elseStatement, SyntaxKind.IfStatement)) {
break
}
ifStatement = <IfStatement>ifStatement.elseStatement;
}
@@ -3355,7 +3355,7 @@ module ts {
break;
}
}
if (shouldHighlightNextKeyword) {
result.push({
fileName: filename,
@@ -3394,7 +3394,7 @@ module ts {
return map(keywords, getReferenceEntryFromNode);
}
function getThrowOccurrences(throwStatement: ThrowStatement) {
var owner = getThrowStatementOwner(throwStatement);
@@ -3403,7 +3403,7 @@ module ts {
}
var keywords: Node[] = [];
forEach(aggregateOwnedThrowStatements(owner), throwStatement => {
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
});
@@ -3415,7 +3415,7 @@ module ts {
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
});
}
return map(keywords, getReferenceEntryFromNode);
}
@@ -3551,7 +3551,7 @@ module ts {
return map(keywords, getReferenceEntryFromNode);
}
function getBreakOrContinueStatementOccurences(breakOrContinueStatement: BreakOrContinueStatement): ReferenceEntry[]{
function getBreakOrContinueStatementOccurences(breakOrContinueStatement: BreakOrContinueStatement): ReferenceEntry[] {
var owner = getBreakOrContinueOwner(breakOrContinueStatement);
if (owner) {
@@ -3599,7 +3599,7 @@ module ts {
if (statement.kind === SyntaxKind.ContinueStatement) {
continue;
}
// Fall through.
// Fall through.
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.WhileStatement:
@@ -4024,13 +4024,13 @@ module ts {
* searchLocation: a node where the search value
*/
function getReferencesInNode(container: Node,
searchSymbol: Symbol,
searchText: string,
searchLocation: Node,
searchMeaning: SemanticMeaning,
findInStrings: boolean,
findInComments: boolean,
result: ReferenceEntry[]): void {
searchSymbol: Symbol,
searchText: string,
searchLocation: Node,
searchMeaning: SemanticMeaning,
findInStrings: boolean,
findInComments: boolean,
result: ReferenceEntry[]): void {
var sourceFile = container.getSourceFile();
var tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</
@@ -4110,7 +4110,7 @@ module ts {
}
}
function getReferencesForSuperKeyword(superKeyword: Node): ReferenceEntry[]{
function getReferencesForSuperKeyword(superKeyword: Node): ReferenceEntry[] {
var searchSpaceNode = getSuperContainer(superKeyword);
if (!searchSpaceNode) {
return undefined;
@@ -4184,7 +4184,7 @@ module ts {
if (isExternalModule(<SourceFile>searchSpaceNode)) {
return undefined;
}
// Fall through
// Fall through
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
break;
@@ -4861,26 +4861,105 @@ module ts {
fileName = normalizeSlashes(fileName);
var sourceFile = getCurrentSourceFile(fileName);
// Make a scanner we can get trivia from.
var triviaScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text);
var mergeConflictScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text);
var result: ClassifiedSpan[] = [];
processElement(sourceFile);
return result;
function classifyComment(comment: CommentRange) {
var width = comment.end - comment.pos;
if (textSpanIntersectsWith(span, comment.pos, width)) {
function classifyLeadingTrivia(token: Node): void {
var tokenStart = skipTrivia(sourceFile.text, token.pos, /*stopAfterLineBreak:*/ false);
if (tokenStart === token.pos) {
return;
}
// token has trivia. Classify them appropriately.
triviaScanner.setTextPos(token.pos);
while (true) {
var start = triviaScanner.getTextPos();
var kind = triviaScanner.scan();
var end = triviaScanner.getTextPos();
var width = end - start;
if (textSpanIntersectsWith(span, start, width)) {
if (!isTrivia(kind)) {
return;
}
if (isComment(kind)) {
// Simple comment. Just add as is.
result.push({
textSpan: createTextSpan(start, width),
classificationType: ClassificationTypeNames.comment
})
continue;
}
if (kind === SyntaxKind.ConflictMarkerTrivia) {
var text = sourceFile.text;
var ch = text.charCodeAt(start);
// for the <<<<<<< and >>>>>>> markers, we just add them in as comments
// in the classification stream.
if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) {
result.push({
textSpan: createTextSpan(start, width),
classificationType: ClassificationTypeNames.comment
});
continue;
}
// for the ======== add a comment for the first line, and then lex all
// subsequent lines up until the end of the conflict marker.
Debug.assert(ch === CharacterCodes.equals);
classifyDisabledMergeCode(text, start, end);
}
}
}
}
function classifyDisabledMergeCode(text: string, start: number, end: number) {
// Classify the line that the ======= marker is on as a comment. Then just lex
// all further tokens and add them to the result.
for (var i = start; i < end; i++) {
if (isLineBreak(text.charCodeAt(i))) {
break;
}
}
result.push({
textSpan: createTextSpanFromBounds(start, i),
classificationType: ClassificationTypeNames.comment
});
mergeConflictScanner.setTextPos(i);
while (mergeConflictScanner.getTextPos() < end) {
classifyDisabledCodeToken();
}
}
function classifyDisabledCodeToken() {
var start = mergeConflictScanner.getTextPos();
var tokenKind = mergeConflictScanner.scan();
var end = mergeConflictScanner.getTextPos();
var type = classifyTokenType(tokenKind);
if (type) {
result.push({
textSpan: createTextSpan(comment.pos, width),
classificationType: ClassificationTypeNames.comment
textSpan: createTextSpanFromBounds(start, end),
classificationType: type
});
}
}
function classifyToken(token: Node): void {
forEach(getLeadingCommentRanges(sourceFile.text, token.getFullStart()), classifyComment);
classifyLeadingTrivia(token);
if (token.getWidth() > 0) {
var type = classifyTokenType(token);
var type = classifyTokenType(token.kind, token);
if (type) {
result.push({
textSpan: createTextSpan(token.getStart(), token.getWidth()),
@@ -4888,12 +4967,12 @@ module ts {
});
}
}
forEach(getTrailingCommentRanges(sourceFile.text, token.getEnd()), classifyComment);
}
function classifyTokenType(token: Node): string {
var tokenKind = token.kind;
// for accurate classification, the actual token should be passed in. however, for
// cases like 'disabled merge code' classification, we just get the token kind and
// classify based on that instead.
function classifyTokenType(tokenKind: SyntaxKind, token?: Node): string {
if (isKeyword(tokenKind)) {
return ClassificationTypeNames.keyword;
}
@@ -4903,23 +4982,31 @@ module ts {
if (tokenKind === SyntaxKind.LessThanToken || tokenKind === SyntaxKind.GreaterThanToken) {
// If the node owning the token has a type argument list or type parameter list, then
// we can effectively assume that a '<' and '>' belong to those lists.
if (getTypeArgumentOrTypeParameterList(token.parent)) {
if (token && getTypeArgumentOrTypeParameterList(token.parent)) {
return ClassificationTypeNames.punctuation;
}
}
if (isPunctuation(token.kind)) {
// the '=' in a variable declaration is special cased here.
if (token.parent.kind === SyntaxKind.BinaryExpression ||
token.parent.kind === SyntaxKind.VariableDeclaration ||
token.parent.kind === SyntaxKind.PrefixUnaryExpression ||
token.parent.kind === SyntaxKind.PostfixUnaryExpression ||
token.parent.kind === SyntaxKind.ConditionalExpression) {
return ClassificationTypeNames.operator;
}
else {
return ClassificationTypeNames.punctuation;
if (isPunctuation(tokenKind)) {
if (token) {
if (tokenKind === SyntaxKind.EqualsToken) {
// the '=' in a variable declaration is special cased here.
if (token.parent.kind === SyntaxKind.VariableDeclaration ||
token.parent.kind === SyntaxKind.PropertyDeclaration ||
token.parent.kind === SyntaxKind.Parameter) {
return ClassificationTypeNames.operator;
}
}
if (token.parent.kind === SyntaxKind.BinaryExpression ||
token.parent.kind === SyntaxKind.PrefixUnaryExpression ||
token.parent.kind === SyntaxKind.PostfixUnaryExpression ||
token.parent.kind === SyntaxKind.ConditionalExpression) {
return ClassificationTypeNames.operator;
}
}
return ClassificationTypeNames.punctuation;
}
else if (tokenKind === SyntaxKind.NumericLiteral) {
return ClassificationTypeNames.numericLiteral;
@@ -4936,35 +5023,37 @@ module ts {
return ClassificationTypeNames.stringLiteral;
}
else if (tokenKind === SyntaxKind.Identifier) {
switch (token.parent.kind) {
case SyntaxKind.ClassDeclaration:
if ((<ClassDeclaration>token.parent).name === token) {
return ClassificationTypeNames.className;
}
return;
case SyntaxKind.TypeParameter:
if ((<TypeParameterDeclaration>token.parent).name === token) {
return ClassificationTypeNames.typeParameterName;
}
return;
case SyntaxKind.InterfaceDeclaration:
if ((<InterfaceDeclaration>token.parent).name === token) {
return ClassificationTypeNames.interfaceName;
}
return;
case SyntaxKind.EnumDeclaration:
if ((<EnumDeclaration>token.parent).name === token) {
return ClassificationTypeNames.enumName;
}
return;
case SyntaxKind.ModuleDeclaration:
if ((<ModuleDeclaration>token.parent).name === token) {
return ClassificationTypeNames.moduleName;
}
return;
default:
return ClassificationTypeNames.text;
if (token) {
switch (token.parent.kind) {
case SyntaxKind.ClassDeclaration:
if ((<ClassDeclaration>token.parent).name === token) {
return ClassificationTypeNames.className;
}
return;
case SyntaxKind.TypeParameter:
if ((<TypeParameterDeclaration>token.parent).name === token) {
return ClassificationTypeNames.typeParameterName;
}
return;
case SyntaxKind.InterfaceDeclaration:
if ((<InterfaceDeclaration>token.parent).name === token) {
return ClassificationTypeNames.interfaceName;
}
return;
case SyntaxKind.EnumDeclaration:
if ((<EnumDeclaration>token.parent).name === token) {
return ClassificationTypeNames.enumName;
}
return;
case SyntaxKind.ModuleDeclaration:
if ((<ModuleDeclaration>token.parent).name === token) {
return ClassificationTypeNames.moduleName;
}
return;
}
}
return ClassificationTypeNames.text;
}
}
@@ -5493,7 +5582,6 @@ module ts {
var start = scanner.getTokenPos();
var end = scanner.getTextPos();
// add the token
addResult(end - start, classFromKind(token));
if (end >= text.length) {

View File

@@ -1,24 +1,18 @@
tests/cases/compiler/conflictMarkerTrivia1.ts(2,1): error TS1185: Merge conflict marker encountered.
tests/cases/compiler/conflictMarkerTrivia1.ts(3,5): error TS2300: Duplicate identifier 'v'.
tests/cases/compiler/conflictMarkerTrivia1.ts(4,1): error TS1185: Merge conflict marker encountered.
tests/cases/compiler/conflictMarkerTrivia1.ts(5,5): error TS2300: Duplicate identifier 'v'.
tests/cases/compiler/conflictMarkerTrivia1.ts(6,1): error TS1185: Merge conflict marker encountered.
==== tests/cases/compiler/conflictMarkerTrivia1.ts (5 errors) ====
==== tests/cases/compiler/conflictMarkerTrivia1.ts (3 errors) ====
class C {
<<<<<<< HEAD
~~~~~~~
!!! error TS1185: Merge conflict marker encountered.
v = 1;
~
!!! error TS2300: Duplicate identifier 'v'.
=======
~~~~~~~
!!! error TS1185: Merge conflict marker encountered.
v = 2;
~
!!! error TS2300: Duplicate identifier 'v'.
>>>>>>> Branch-a
~~~~~~~
!!! error TS1185: Merge conflict marker encountered.

View File

@@ -0,0 +1,28 @@
tests/cases/compiler/conflictMarkerTrivia2.ts(3,1): error TS1185: Merge conflict marker encountered.
tests/cases/compiler/conflictMarkerTrivia2.ts(4,6): error TS2304: Cannot find name 'a'.
tests/cases/compiler/conflictMarkerTrivia2.ts(6,1): error TS1185: Merge conflict marker encountered.
tests/cases/compiler/conflictMarkerTrivia2.ts(9,1): error TS1185: Merge conflict marker encountered.
==== tests/cases/compiler/conflictMarkerTrivia2.ts (4 errors) ====
class C {
foo() {
<<<<<<< B
~~~~~~~
!!! error TS1185: Merge conflict marker encountered.
a();
~
!!! error TS2304: Cannot find name 'a'.
}
=======
~~~~~~~
!!! error TS1185: Merge conflict marker encountered.
b();
}
>>>>>>> A
~~~~~~~
!!! error TS1185: Merge conflict marker encountered.
public bar() { }
}

View File

@@ -0,0 +1,12 @@
class C {
foo() {
<<<<<<< B
a();
}
=======
b();
}
>>>>>>> A
public bar() { }
}

View File

@@ -2,7 +2,7 @@
////class C {
////<<<<<<< HEAD
//// v = 1;
////v = 1;
////=======
////v = 2;
////>>>>>>> Branch - a
@@ -13,6 +13,6 @@ verify.currentFileContentIs("class C {\r\n\
<<<<<<< HEAD\r\n\
v = 1;\r\n\
=======\r\n\
v = 2;\r\n\
v = 2;\r\n\
>>>>>>> Branch - a\r\n\
}");

View File

@@ -0,0 +1,19 @@
/// <reference path="fourslash.ts"/>
////class C {
////<<<<<<< HEAD
//// v = 1;
////=======
//// v = 2;
////>>>>>>> Branch - a
////}
debugger;
var c = classification;
verify.syntacticClassificationsAre(
c.keyword("class"), c.className("C"), c.punctuation("{"),
c.comment("<<<<<<< HEAD"),
c.text("v"), c.operator("="), c.numericLiteral("1"), c.punctuation(";"),
c.comment("======="),
c.text("v"), c.punctuation("="), c.numericLiteral("2"), c.punctuation(";"),
c.comment(">>>>>>> Branch - a"),
c.punctuation("}"));

View File

@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts"/>
////<<<<<<< HEAD
////class C { }
////=======
////class D { }
////>>>>>>> Branch - a
var c = classification;
verify.syntacticClassificationsAre(
c.comment("<<<<<<< HEAD"),
c.keyword("class"), c.className("C"), c.punctuation("{"), c.punctuation("}"),
c.comment("======="),
c.keyword("class"), c.text("D"), c.punctuation("{"), c.punctuation("}"),
c.comment(">>>>>>> Branch - a"));

View File

@@ -21,7 +21,7 @@ describe('Colorization', function () {
var mytypescriptLS = new Harness.LanguageService.TypeScriptLS();
var myclassifier = mytypescriptLS.getClassifier();
function getClassifications(code: string, initialEndOfLineState: ts.EndOfLineState = ts.EndOfLineState.Start): ClassiferResult {
function getLexicalClassifications(code: string, initialEndOfLineState: ts.EndOfLineState = ts.EndOfLineState.Start): ClassiferResult {
var classResult = myclassifier.getClassificationsForLine(code, initialEndOfLineState).split('\n');
var tuples: Classification[] = [];
var i = 0;
@@ -71,8 +71,8 @@ describe('Colorization', function () {
function regExpLiteral(text: string) { return { value: text, class: ts.TokenClass.RegExpLiteral }; }
function finalEndOfLineState(value: number) { return { value: value, class: <ts.TokenClass>undefined }; }
function test(text: string, initialEndOfLineState: ts.EndOfLineState, ...expectedEntries: ClassificationEntry[]): void {
var result = getClassifications(text, initialEndOfLineState);
function testLexicalClassification(text: string, initialEndOfLineState: ts.EndOfLineState, ...expectedEntries: ClassificationEntry[]): void {
var result = getLexicalClassifications(text, initialEndOfLineState);
for (var i = 0, n = expectedEntries.length; i < n; i++) {
var expectedEntry = expectedEntries[i];
@@ -95,7 +95,7 @@ describe('Colorization', function () {
describe("test getClassifications", function () {
it("Returns correct token classes", function () {
test("var x: string = \"foo\"; //Hello",
testLexicalClassification("var x: string = \"foo\"; //Hello",
ts.EndOfLineState.Start,
keyword("var"),
whitespace(" "),
@@ -109,7 +109,7 @@ describe('Colorization', function () {
});
it("correctly classifies a comment after a divide operator", function () {
test("1 / 2 // comment",
testLexicalClassification("1 / 2 // comment",
ts.EndOfLineState.Start,
numberLiteral("1"),
whitespace(" "),
@@ -119,7 +119,7 @@ describe('Colorization', function () {
});
it("correctly classifies a literal after a divide operator", function () {
test("1 / 2, 3 / 4",
testLexicalClassification("1 / 2, 3 / 4",
ts.EndOfLineState.Start,
numberLiteral("1"),
whitespace(" "),
@@ -131,131 +131,131 @@ describe('Colorization', function () {
});
it("correctly classifies a multi-line string with one backslash", function () {
test("'line1\\",
testLexicalClassification("'line1\\",
ts.EndOfLineState.Start,
stringLiteral("'line1\\"),
finalEndOfLineState(ts.EndOfLineState.InSingleQuoteStringLiteral));
});
it("correctly classifies a multi-line string with three backslashes", function () {
test("'line1\\\\\\",
testLexicalClassification("'line1\\\\\\",
ts.EndOfLineState.Start,
stringLiteral("'line1\\\\\\"),
finalEndOfLineState(ts.EndOfLineState.InSingleQuoteStringLiteral));
});
it("correctly classifies an unterminated single-line string with no backslashes", function () {
test("'line1",
testLexicalClassification("'line1",
ts.EndOfLineState.Start,
stringLiteral("'line1"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies an unterminated single-line string with two backslashes", function () {
test("'line1\\\\",
testLexicalClassification("'line1\\\\",
ts.EndOfLineState.Start,
stringLiteral("'line1\\\\"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies an unterminated single-line string with four backslashes", function () {
test("'line1\\\\\\\\",
testLexicalClassification("'line1\\\\\\\\",
ts.EndOfLineState.Start,
stringLiteral("'line1\\\\\\\\"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies the continuing line of a multi-line string ending in one backslash", function () {
test("\\",
testLexicalClassification("\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\"),
finalEndOfLineState(ts.EndOfLineState.InDoubleQuoteStringLiteral));
});
it("correctly classifies the continuing line of a multi-line string ending in three backslashes", function () {
test("\\",
testLexicalClassification("\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\"),
finalEndOfLineState(ts.EndOfLineState.InDoubleQuoteStringLiteral));
});
it("correctly classifies the last line of an unterminated multi-line string ending in no backslashes", function () {
test(" ",
testLexicalClassification(" ",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral(" "),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies the last line of an unterminated multi-line string ending in two backslashes", function () {
test("\\\\",
testLexicalClassification("\\\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\\\"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies the last line of an unterminated multi-line string ending in four backslashes", function () {
test("\\\\\\\\",
testLexicalClassification("\\\\\\\\",
ts.EndOfLineState.InDoubleQuoteStringLiteral,
stringLiteral("\\\\\\\\"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies the last line of a multi-line string", function () {
test("'",
testLexicalClassification("'",
ts.EndOfLineState.InSingleQuoteStringLiteral,
stringLiteral("'"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies an unterminated multiline comment", function () {
test("/*",
testLexicalClassification("/*",
ts.EndOfLineState.Start,
comment("/*"),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
it("correctly classifies the termination of a multiline comment", function () {
test(" */ ",
testLexicalClassification(" */ ",
ts.EndOfLineState.InMultiLineCommentTrivia,
comment(" */"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("correctly classifies the continuation of a multiline comment", function () {
test("LOREM IPSUM DOLOR ",
testLexicalClassification("LOREM IPSUM DOLOR ",
ts.EndOfLineState.InMultiLineCommentTrivia,
comment("LOREM IPSUM DOLOR "),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
it("correctly classifies an unterminated multiline comment on a line ending in '/*/'", function () {
test(" /*/",
testLexicalClassification(" /*/",
ts.EndOfLineState.Start,
comment("/*/"),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
it("correctly classifies an unterminated multiline comment with trailing space", function () {
test("/* ",
testLexicalClassification("/* ",
ts.EndOfLineState.Start,
comment("/* "),
finalEndOfLineState(ts.EndOfLineState.InMultiLineCommentTrivia));
});
it("correctly classifies a keyword after a dot", function () {
test("a.var",
testLexicalClassification("a.var",
ts.EndOfLineState.Start,
identifier("var"));
});
it("correctly classifies a string literal after a dot", function () {
test("a.\"var\"",
testLexicalClassification("a.\"var\"",
ts.EndOfLineState.Start,
stringLiteral("\"var\""));
});
it("correctly classifies a keyword after a dot separated by comment trivia", function () {
test("a./*hello world*/ var",
testLexicalClassification("a./*hello world*/ var",
ts.EndOfLineState.Start,
identifier("a"),
punctuation("."),
@@ -264,27 +264,27 @@ describe('Colorization', function () {
});
it("classifies a property access with whitespace around the dot", function () {
test(" x .\tfoo ()",
testLexicalClassification(" x .\tfoo ()",
ts.EndOfLineState.Start,
identifier("x"),
identifier("foo"));
});
it("classifies a keyword after a dot on previous line", function () {
test("var",
testLexicalClassification("var",
ts.EndOfLineState.Start,
keyword("var"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("classifies multiple keywords properly", function () {
test("public static",
testLexicalClassification("public static",
ts.EndOfLineState.Start,
keyword("public"),
keyword("static"),
finalEndOfLineState(ts.EndOfLineState.Start));
test("public var",
testLexicalClassification("public var",
ts.EndOfLineState.Start,
keyword("public"),
identifier("var"),
@@ -292,7 +292,7 @@ describe('Colorization', function () {
});
it("classifies partially written generics correctly.", function () {
test("Foo<number",
testLexicalClassification("Foo<number",
ts.EndOfLineState.Start,
identifier("Foo"),
operator("<"),
@@ -300,14 +300,14 @@ describe('Colorization', function () {
finalEndOfLineState(ts.EndOfLineState.Start));
// Looks like a cast, should get classified as a keyword.
test("<number",
testLexicalClassification("<number",
ts.EndOfLineState.Start,
operator("<"),
keyword("number"),
finalEndOfLineState(ts.EndOfLineState.Start));
// handle nesting properly.
test("Foo<Foo,Foo<number",
testLexicalClassification("Foo<Foo,Foo<number",
ts.EndOfLineState.Start,
identifier("Foo"),
operator("<"),
@@ -319,19 +319,11 @@ describe('Colorization', function () {
finalEndOfLineState(ts.EndOfLineState.Start));
});
it("ClassifiesConflictTokens", () => {
// no longer in something that looks generic.
test("Foo<Foo> number",
ts.EndOfLineState.Start,
identifier("Foo"),
operator("<"),
identifier("Foo"),
operator(">"),
keyword("number"),
finalEndOfLineState(ts.EndOfLineState.Start));
it("LexicallyClassifiesConflictTokens", () => {
debugger;
// Test conflict markers.
test(
testLexicalClassification(
"class C {\r\n\
<<<<<<< HEAD\r\n\
v = 1;\r\n\
@@ -348,14 +340,26 @@ describe('Colorization', function () {
operator("="),
numberLiteral("1"),
punctuation(";"),
comment("======="),
identifier("v"),
operator("="),
numberLiteral("2"),
punctuation(";"),
comment("=======\r\n v = 2;\r\n"),
comment(">>>>>>> Branch - a"),
punctuation("}"),
finalEndOfLineState(ts.EndOfLineState.Start));
testLexicalClassification(
"<<<<<<< HEAD\r\n\
class C { }\r\n\
=======\r\n\
class D { }\r\n\
>>>>>>> Branch - a\r\n",
ts.EndOfLineState.Start,
comment("<<<<<<< HEAD"),
keyword("class"),
identifier("C"),
punctuation("{"),
punctuation("}"),
comment("=======\r\nclass D { }\r\n"),
comment(">>>>>>> Branch - a"),
finalEndOfLineState(ts.EndOfLineState.Start));
});
});
});