diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts
index 17dbae83bf2bb..0e9bc25aba900 100644
--- a/src/compiler/diagnosticInformationMap.generated.ts
+++ b/src/compiler/diagnosticInformationMap.generated.ts
@@ -144,6 +144,7 @@ module ts {
Array_element_destructuring_pattern_expected: { code: 1181, category: DiagnosticCategory.Error, key: "Array element destructuring pattern expected." },
A_destructuring_declaration_must_have_an_initializer: { code: 1182, category: DiagnosticCategory.Error, key: "A destructuring declaration must have an initializer." },
Destructuring_declarations_are_not_allowed_in_ambient_contexts: { code: 1183, category: DiagnosticCategory.Error, key: "Destructuring declarations are not allowed in ambient contexts." },
+ Merge_conflict_marker_encountered: { code: 1184, category: DiagnosticCategory.Error, key: "Merge conflict marker encountered." },
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index a06b71d460ead..3f547474b269b 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -568,6 +568,10 @@
"category": "Error",
"code": 1183
},
+ "Merge conflict marker encountered.": {
+ "category": "Error",
+ "code": 1184
+ },
"Duplicate identifier '{0}'.": {
"category": "Error",
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 877a5ba187fb7..9853e22d9f15c 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -333,10 +333,14 @@ module ts {
var ch = text.charCodeAt(pos);
switch (ch) {
case CharacterCodes.carriageReturn:
- if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) pos++;
+ if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) {
+ pos++;
+ }
case CharacterCodes.lineFeed:
pos++;
- if (stopAfterLineBreak) return pos;
+ if (stopAfterLineBreak) {
+ return pos;
+ }
continue;
case CharacterCodes.tab:
case CharacterCodes.verticalTab:
@@ -367,6 +371,16 @@ module ts {
continue;
}
break;
+
+ case CharacterCodes.lessThan:
+ case CharacterCodes.equals:
+ case CharacterCodes.greaterThan:
+ if (isConflictMarkerTrivia(text, pos)) {
+ pos = scanConflictMarkerTrivia(text, pos);
+ continue;
+ }
+ break;
+
default:
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch) || isLineBreak(ch))) {
pos++;
@@ -378,6 +392,39 @@ module ts {
}
}
+ function isConflictMarkerTrivia(text: string, pos: number) {
+ // Conflict markers must be at the start of a line.
+ if (pos > 0 && isLineBreak(text.charCodeAt(pos - 1))) {
+ var ch = text.charCodeAt(pos);
+
+ // All conflict markers consist of the same character repeated seven times. If it is
+ // a <<<<<<< or >>>>>>> marker then it is also followd by a space.
+ var markerLength = "<<<<<<<".length;
+
+ if ((pos + markerLength) < text.length) {
+ for (var i = 0, n = markerLength; i < n; i++) {
+ if (text.charCodeAt(pos + i) !== ch) {
+ return false;
+ }
+ }
+
+ return ch === CharacterCodes.equals ||
+ text.charCodeAt(pos + markerLength) === CharacterCodes.space;
+ }
+ }
+
+ return false;
+ }
+
+ function scanConflictMarkerTrivia(text: string, pos: number) {
+ var len = text.length;
+ while (pos < len && !isLineBreak(text.charCodeAt(pos))) {
+ pos++;
+ }
+
+ return pos;
+ }
+
// Extract comments from the given source text starting at the given position. If trailing is false, whitespace is skipped until
// the first line break and comments between that location and the next token are returned. If trailing is true, comments occurring
// between the given position and the next line break are returned. The return value is an array containing a TextRange for each
@@ -1010,6 +1057,17 @@ module ts {
case CharacterCodes.semicolon:
return pos++, token = SyntaxKind.SemicolonToken;
case CharacterCodes.lessThan:
+ if (isConflictMarkerTrivia(text, pos)) {
+ error(Diagnostics.Merge_conflict_marker_encountered);
+ pos = scanConflictMarkerTrivia(text, pos);
+ if (skipTrivia) {
+ continue;
+ }
+ else {
+ return token = SyntaxKind.ConflictMarkerTrivia;
+ }
+ }
+
if (text.charCodeAt(pos + 1) === CharacterCodes.lessThan) {
if (text.charCodeAt(pos + 2) === CharacterCodes.equals) {
return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken;
@@ -1021,6 +1079,17 @@ module ts {
}
return pos++, token = SyntaxKind.LessThanToken;
case CharacterCodes.equals:
+ if (isConflictMarkerTrivia(text, pos)) {
+ error(Diagnostics.Merge_conflict_marker_encountered);
+ pos = scanConflictMarkerTrivia(text, pos);
+ if (skipTrivia) {
+ continue;
+ }
+ else {
+ return token = SyntaxKind.ConflictMarkerTrivia;
+ }
+ }
+
if (text.charCodeAt(pos + 1) === CharacterCodes.equals) {
if (text.charCodeAt(pos + 2) === CharacterCodes.equals) {
return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken;
@@ -1032,6 +1101,17 @@ module ts {
}
return pos++, token = SyntaxKind.EqualsToken;
case CharacterCodes.greaterThan:
+ if (isConflictMarkerTrivia(text, pos)) {
+ error(Diagnostics.Merge_conflict_marker_encountered);
+ pos = scanConflictMarkerTrivia(text, pos);
+ if (skipTrivia) {
+ continue;
+ }
+ else {
+ return token = SyntaxKind.ConflictMarkerTrivia;
+ }
+ }
+
return pos++, token = SyntaxKind.GreaterThanToken;
case CharacterCodes.question:
return pos++, token = SyntaxKind.QuestionToken;
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 8398387094aa2..8506646c410a9 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -15,6 +15,7 @@ module ts {
MultiLineCommentTrivia,
NewLineTrivia,
WhitespaceTrivia,
+ ConflictMarkerTrivia,
// Literals
NumericLiteral,
StringLiteral,
@@ -266,7 +267,7 @@ module ts {
FirstToken = Unknown,
LastToken = TypeKeyword,
FirstTriviaToken = SingleLineCommentTrivia,
- LastTriviaToken = WhitespaceTrivia,
+ LastTriviaToken = ConflictMarkerTrivia,
FirstLiteralToken = NumericLiteral,
LastLiteralToken = NoSubstitutionTemplateLiteral,
FirstTemplateToken = NoSubstitutionTemplateLiteral,
diff --git a/src/services/services.ts b/src/services/services.ts
index 74e7f924a20ad..0fe3dc271dfab 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -5780,6 +5780,7 @@ module ts {
return TokenClass.StringLiteral;
case SyntaxKind.RegularExpressionLiteral:
return TokenClass.RegExpLiteral;
+ case SyntaxKind.ConflictMarkerTrivia:
case SyntaxKind.MultiLineCommentTrivia:
case SyntaxKind.SingleLineCommentTrivia:
return TokenClass.Comment;
diff --git a/tests/baselines/reference/conflictMarkerTrivia1.errors.txt b/tests/baselines/reference/conflictMarkerTrivia1.errors.txt
new file mode 100644
index 0000000000000..79513821a00bf
--- /dev/null
+++ b/tests/baselines/reference/conflictMarkerTrivia1.errors.txt
@@ -0,0 +1,25 @@
+tests/cases/compiler/conflictMarkerTrivia1.ts(2,1): error TS1184: Merge conflict marker encountered.
+tests/cases/compiler/conflictMarkerTrivia1.ts(4,1): error TS1184: Merge conflict marker encountered.
+tests/cases/compiler/conflictMarkerTrivia1.ts(6,1): error TS1184: Merge conflict marker encountered.
+tests/cases/compiler/conflictMarkerTrivia1.ts(3,5): error TS2300: Duplicate identifier 'v'.
+tests/cases/compiler/conflictMarkerTrivia1.ts(5,5): error TS2300: Duplicate identifier 'v'.
+
+
+==== tests/cases/compiler/conflictMarkerTrivia1.ts (5 errors) ====
+ class C {
+ <<<<<<< HEAD
+
+!!! error TS1184: Merge conflict marker encountered.
+ v = 1;
+ ~
+!!! error TS2300: Duplicate identifier 'v'.
+ =======
+
+!!! error TS1184: Merge conflict marker encountered.
+ v = 2;
+ ~
+!!! error TS2300: Duplicate identifier 'v'.
+ >>>>>>> Branch-a
+
+!!! error TS1184: Merge conflict marker encountered.
+ }
\ No newline at end of file
diff --git a/tests/cases/compiler/conflictMarkerTrivia1.ts b/tests/cases/compiler/conflictMarkerTrivia1.ts
new file mode 100644
index 0000000000000..c8d14df38f78b
--- /dev/null
+++ b/tests/cases/compiler/conflictMarkerTrivia1.ts
@@ -0,0 +1,7 @@
+class C {
+<<<<<<< HEAD
+ v = 1;
+=======
+ v = 2;
+>>>>>>> Branch-a
+}
\ No newline at end of file
diff --git a/tests/cases/fourslash/formatConflictMarker1.ts b/tests/cases/fourslash/formatConflictMarker1.ts
new file mode 100644
index 0000000000000..88c9703241195
--- /dev/null
+++ b/tests/cases/fourslash/formatConflictMarker1.ts
@@ -0,0 +1,18 @@
+///
+
+////class C {
+////<<<<<<< HEAD
+//// v = 1;
+////=======
+////v = 2;
+////>>>>>>> Branch - a
+////}
+
+format.document();
+verify.currentFileContentIs("class C {\r\n\
+<<<<<<< HEAD\r\n\
+ v = 1;\r\n\
+=======\r\n\
+ v = 2;\r\n\
+>>>>>>> Branch - a\r\n\
+}");
\ No newline at end of file
diff --git a/tests/cases/unittests/services/colorization.ts b/tests/cases/unittests/services/colorization.ts
index b29f119f944b2..44804e4f9f519 100644
--- a/tests/cases/unittests/services/colorization.ts
+++ b/tests/cases/unittests/services/colorization.ts
@@ -317,7 +317,9 @@ describe('Colorization', function () {
operator("<"),
identifier("number"),
finalEndOfLineState(ts.EndOfLineState.Start));
+ });
+ it("ClassifiesConflictTokens", () => {
// no longer in something that looks generic.
test("Foo number",
ts.EndOfLineState.Start,
@@ -327,6 +329,33 @@ describe('Colorization', function () {
operator(">"),
keyword("number"),
finalEndOfLineState(ts.EndOfLineState.Start));
+
+ // Test conflict markers.
+ test(
+"class C {\r\n\
+<<<<<<< HEAD\r\n\
+ v = 1;\r\n\
+=======\r\n\
+ v = 2;\r\n\
+>>>>>>> Branch - a\r\n\
+}",
+ ts.EndOfLineState.Start,
+ keyword("class"),
+ identifier("C"),
+ punctuation("{"),
+ comment("<<<<<<< HEAD"),
+ identifier("v"),
+ operator("="),
+ numberLiteral("1"),
+ punctuation(";"),
+ comment("======="),
+ identifier("v"),
+ operator("="),
+ numberLiteral("2"),
+ punctuation(";"),
+ comment(">>>>>>> Branch - a"),
+ punctuation("}"),
+ finalEndOfLineState(ts.EndOfLineState.Start));
});
});
});
\ No newline at end of file