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