Revert "Support overlapping deletions in quick fixes"

This reverts commit ff34b811741db5ce5a55bb0ed00d6e026ef9e09b.

Reason for revert: This causes a failing test when trying to roll the engine into flutter/flutter. See https://github.com/flutter/flutter/issues/92181 for more context.

Original change's description:
> Support overlapping deletions in quick fixes
>
> I believe that the tests cover the added behavior (and some existing
> behavior), but they aren't complete. I'll add more tests over time, but
> for now the test coverage is strictly better than it used to be.
>
> Change-Id: I8dd228cb2b3c477c28e6d20c6da4a549a30d1afb
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/217280
> Reviewed-by: Phil Quitslund <pquitslund@google.com>
> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>

# Not skipping CQ checks because original CL landed > 1 day ago.

Change-Id: Ib50418544a2fd30c255ffc118b42c45aded3ae1c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/217760
Reviewed-by: Devon Carew <devoncarew@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Devon Carew <devoncarew@google.com>
diff --git a/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart b/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
index 459974c..777ccda 100644
--- a/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
+++ b/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
@@ -4,7 +4,6 @@
 
 import 'dart:collection';
 import 'dart:convert' hide JsonDecoder;
-import 'dart:math' as math;
 
 import 'package:analyzer_plugin/protocol/protocol.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -29,17 +28,6 @@
 /// If the invariants can't be preserved, then a [ConflictingEditException] is
 /// thrown.
 void addEditForSource(SourceFileEdit sourceFileEdit, SourceEdit sourceEdit) {
-  /// If the [leftEdit] and the [rightEdit] can be merged, then merge them.
-  SourceEdit? _merge(SourceEdit leftEdit, SourceEdit rightEdit) {
-    assert(leftEdit.offset <= rightEdit.offset);
-    if (leftEdit.isDeletion && rightEdit.isDeletion) {
-      var offset = leftEdit.offset;
-      var end = math.max(leftEdit.end, rightEdit.end);
-      return SourceEdit(offset, end - offset, '');
-    }
-    return null;
-  }
-
   var edits = sourceFileEdit.edits;
   var length = edits.length;
   var index = 0;
@@ -51,12 +39,7 @@
     // The [previousEdit] has an offset that is strictly greater than the offset
     // of the [sourceEdit] so we only need to look at the end of the
     // [sourceEdit] to know whether they overlap.
-    if (sourceEdit.end > previousEdit.offset) {
-      var mergedEdit = _merge(sourceEdit, previousEdit);
-      if (mergedEdit != null) {
-        edits[index - 1] = mergedEdit;
-        return;
-      }
+    if (sourceEdit.offset + sourceEdit.length > previousEdit.offset) {
       throw ConflictingEditException(
           newEdit: sourceEdit, existingEdit: previousEdit);
     }
@@ -71,12 +54,7 @@
     if ((sourceEdit.offset == nextEdit.offset &&
             sourceEdit.length > 0 &&
             nextEdit.length > 0) ||
-        nextEdit.end > sourceEdit.offset) {
-      var mergedEdit = _merge(nextEdit, sourceEdit);
-      if (mergedEdit != null) {
-        edits[index] = mergedEdit;
-        return;
-      }
+        nextEdit.offset + nextEdit.length > sourceEdit.offset) {
       throw ConflictingEditException(
           newEdit: sourceEdit, existingEdit: nextEdit);
     }
@@ -490,11 +468,3 @@
   /// the given [id], where the request was received at the given [requestTime].
   Response toResponse(String id, int requestTime);
 }
-
-extension SourceEditExtensions on SourceEdit {
-  /// Return `true` if this source edit represents a deletion.
-  bool get isDeletion => replacement.isEmpty;
-
-  /// Return `true` if this source edit represents an insertion.
-  bool get isInsertion => length == 0;
-}
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
index 6ed22f9..a571f04 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
@@ -17,7 +17,6 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(ChangeBuilderImplTest);
     defineReflectiveTests(EditBuilderImplTest);
-    defineReflectiveTests(FileEditBuilderImpl_ConflictingTest);
     defineReflectiveTests(FileEditBuilderImplTest);
     defineReflectiveTests(LinkedEditBuilderImplTest);
   });
@@ -298,33 +297,27 @@
   }
 }
 
-/// Tests that are specifically targeted at the handling of conflicting edits.
 @reflectiveTest
-class FileEditBuilderImpl_ConflictingTest extends AbstractChangeBuilderTest {
+class FileEditBuilderImplTest extends AbstractChangeBuilderTest {
   String path = '/test.dart';
 
-  Matcher get hasConflict => throwsA(isA<ConflictingEditException>());
-
-  Future<void> test_deletion_deletion_adjacent_left() async {
-    var firstOffset = 30;
-    var firstLength = 5;
-    var secondOffset = 23;
-    var secondLength = 7;
+  Future<void> test_addDeletion() async {
+    var offset = 23;
+    var length = 7;
     await builder.addGenericFileEdit(path, (builder) {
-      builder.addDeletion(SourceRange(firstOffset, firstLength));
-      builder.addDeletion(SourceRange(secondOffset, secondLength));
+      builder.addDeletion(SourceRange(offset, length));
     });
     var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(2));
-    expect(edits[0].offset, firstOffset);
-    expect(edits[0].length, firstLength);
+    expect(edits, hasLength(1));
+    expect(edits[0].offset, offset);
+    expect(edits[0].length, length);
     expect(edits[0].replacement, isEmpty);
-    expect(edits[1].offset, secondOffset);
-    expect(edits[1].length, secondLength);
-    expect(edits[1].replacement, isEmpty);
   }
 
-  Future<void> test_deletion_deletion_adjacent_right() async {
+  Future<void> test_addDeletion_adjacent_lowerOffsetFirst() async {
+    // TODO(brianwilkerson) This should also merge the deletions, but is written
+    //  to ensure that existing uses of FileEditBuilder continue to work even
+    //  without that change.
     var firstOffset = 23;
     var firstLength = 7;
     var secondOffset = 30;
@@ -343,23 +336,31 @@
     expect(edits[1].replacement, isEmpty);
   }
 
-  Future<void> test_deletion_deletion_overlap_left() async {
-    var firstOffset = 27;
-    var firstLength = 8;
-    var secondOffset = 23;
-    var secondLength = 7;
+  Future<void> test_addDeletion_adjacent_lowerOffsetSecond() async {
+    // TODO(brianwilkerson) This should also merge the deletions, but is written
+    //  to ensure that existing uses of FileEditBuilder continue to work even
+    //  without that change.
+    var firstOffset = 23;
+    var firstLength = 7;
+    var secondOffset = 30;
+    var secondLength = 5;
     await builder.addGenericFileEdit(path, (builder) {
-      builder.addDeletion(SourceRange(firstOffset, firstLength));
       builder.addDeletion(SourceRange(secondOffset, secondLength));
+      builder.addDeletion(SourceRange(firstOffset, firstLength));
     });
     var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
+    expect(edits, hasLength(2));
     expect(edits[0].offset, secondOffset);
-    expect(edits[0].length, firstOffset + firstLength - secondOffset);
+    expect(edits[0].length, secondLength);
     expect(edits[0].replacement, isEmpty);
+    expect(edits[1].offset, firstOffset);
+    expect(edits[1].length, firstLength);
+    expect(edits[1].replacement, isEmpty);
   }
 
-  Future<void> test_deletion_deletion_overlap_right() async {
+  @failingTest
+  Future<void> test_addDeletion_overlapping() async {
+    // This support is not yet implemented.
     var firstOffset = 23;
     var firstLength = 7;
     var secondOffset = 27;
@@ -375,169 +376,6 @@
     expect(edits[0].replacement, isEmpty);
   }
 
-  Future<void> test_deletion_insertion_adjacent_left() async {
-    var deletionOffset = 23;
-    var deletionLength = 7;
-    var insertionOffset = 23;
-    var insertionText = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addDeletion(SourceRange(deletionOffset, deletionLength));
-      expect(() {
-        builder.addSimpleInsertion(insertionOffset, insertionText);
-      }, hasConflict);
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
-    expect(edits[0].offset, deletionOffset);
-    expect(edits[0].length, deletionLength);
-    expect(edits[0].replacement, '');
-  }
-
-  Future<void> test_deletion_insertion_adjacent_right() async {
-    var deletionOffset = 23;
-    var deletionLength = 7;
-    var insertionOffset = 30;
-    var insertionText = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addDeletion(SourceRange(deletionOffset, deletionLength));
-      builder.addSimpleInsertion(insertionOffset, insertionText);
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(2));
-    expect(edits[0].offset, insertionOffset);
-    expect(edits[0].length, 0);
-    expect(edits[0].replacement, insertionText);
-    expect(edits[1].offset, deletionOffset);
-    expect(edits[1].length, deletionLength);
-    expect(edits[1].replacement, isEmpty);
-  }
-
-  Future<void> test_deletion_insertion_overlap() async {
-    var deletionOffset = 23;
-    var deletionLength = 7;
-    var insertionOffset = 26;
-    var insertionText = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addDeletion(SourceRange(deletionOffset, deletionLength));
-      expect(() {
-        builder.addSimpleInsertion(insertionOffset, insertionText);
-      }, hasConflict);
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
-    expect(edits[0].offset, deletionOffset);
-    expect(edits[0].length, deletionLength);
-    expect(edits[0].replacement, '');
-  }
-
-  Future<void> test_insertion_deletion_adjacent_left() async {
-    var deletionOffset = 23;
-    var deletionLength = 7;
-    var insertionOffset = 23;
-    var insertionText = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addSimpleInsertion(insertionOffset, insertionText);
-      builder.addDeletion(SourceRange(deletionOffset, deletionLength));
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(2));
-    expect(edits[0].offset, deletionOffset);
-    expect(edits[0].length, deletionLength);
-    expect(edits[0].replacement, isEmpty);
-    expect(edits[1].offset, insertionOffset);
-    expect(edits[1].length, 0);
-    expect(edits[1].replacement, insertionText);
-  }
-
-  Future<void> test_insertion_deletion_adjacent_right() async {
-    var deletionOffset = 23;
-    var deletionLength = 7;
-    var insertionOffset = 30;
-    var insertionText = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addSimpleInsertion(insertionOffset, insertionText);
-      builder.addDeletion(SourceRange(deletionOffset, deletionLength));
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(2));
-    expect(edits[0].offset, insertionOffset);
-    expect(edits[0].length, 0);
-    expect(edits[0].replacement, insertionText);
-    expect(edits[1].offset, deletionOffset);
-    expect(edits[1].length, deletionLength);
-    expect(edits[1].replacement, isEmpty);
-  }
-
-  Future<void> test_insertion_deletion_overlap() async {
-    var deletionOffset = 23;
-    var deletionLength = 7;
-    var insertionOffset = 26;
-    var insertionText = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addSimpleInsertion(insertionOffset, insertionText);
-      expect(() {
-        builder.addDeletion(SourceRange(deletionOffset, deletionLength));
-      }, hasConflict);
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
-    expect(edits[0].offset, insertionOffset);
-    expect(edits[0].length, 0);
-    expect(edits[0].replacement, insertionText);
-  }
-
-  Future<void> test_replacement_replacement_overlap_left() async {
-    var offset = 23;
-    var length = 7;
-    var text = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addSimpleReplacement(SourceRange(offset, length), text);
-      expect(() {
-        builder.addSimpleReplacement(SourceRange(offset - 2, length), text);
-      }, hasConflict);
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
-    expect(edits[0].offset, offset);
-    expect(edits[0].length, length);
-    expect(edits[0].replacement, text);
-  }
-
-  Future<void> test_replacement_replacement_overlap_right() async {
-    var offset = 23;
-    var length = 7;
-    var text = 'x';
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addSimpleReplacement(SourceRange(offset, length), text);
-      expect(() {
-        builder.addSimpleReplacement(SourceRange(offset + 2, length), text);
-      }, hasConflict);
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
-    expect(edits[0].offset, offset);
-    expect(edits[0].length, length);
-    expect(edits[0].replacement, text);
-  }
-}
-
-@reflectiveTest
-class FileEditBuilderImplTest extends AbstractChangeBuilderTest {
-  String path = '/test.dart';
-
-  Future<void> test_addDeletion() async {
-    var offset = 23;
-    var length = 7;
-    await builder.addGenericFileEdit(path, (builder) {
-      builder.addDeletion(SourceRange(offset, length));
-    });
-    var edits = builder.sourceChange.edits[0].edits;
-    expect(edits, hasLength(1));
-    expect(edits[0].offset, offset);
-    expect(edits[0].length, length);
-    expect(edits[0].replacement, isEmpty);
-  }
-
   Future<void> test_addInsertion() async {
     await builder.addGenericFileEdit(path, (builder) {
       builder.addInsertion(10, (builder) {
@@ -634,6 +472,40 @@
     expect(edits[1].replacement, text);
   }
 
+  Future<void> test_addSimpleReplacement_overlapsHead() async {
+    var offset = 23;
+    var length = 7;
+    var text = 'xyz';
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addSimpleReplacement(SourceRange(offset, length), text);
+      expect(() {
+        builder.addSimpleReplacement(SourceRange(offset - 2, length), text);
+      }, throwsA(isA<ConflictingEditException>()));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(1));
+    expect(edits[0].offset, offset);
+    expect(edits[0].length, length);
+    expect(edits[0].replacement, text);
+  }
+
+  Future<void> test_addSimpleReplacement_overlapsTail() async {
+    var offset = 23;
+    var length = 7;
+    var text = 'xyz';
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addSimpleReplacement(SourceRange(offset, length), text);
+      expect(() {
+        builder.addSimpleReplacement(SourceRange(offset + 2, length), text);
+      }, throwsA(isA<ConflictingEditException>()));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(1));
+    expect(edits[0].offset, offset);
+    expect(edits[0].length, length);
+    expect(edits[0].replacement, text);
+  }
+
   Future<void> test_createEditBuilder() async {
     await builder.addGenericFileEdit(path, (builder) {
       var offset = 4;