/// <summary> Transforms a single range using the given modification. </summary> private Range Transform(Range range, RangeModification modification) { if (modification.WasAdded) { if (range.ContainsExclusive(modification.Index)) { return(new Range(range.StartIndex, range.EndIndex + modification.NumberOfItems)); } else if (range.StartIndex < modification.Index) { return(range); } else { return(new Range(range.StartIndex + modification.NumberOfItems, range.EndIndex + modification.NumberOfItems)); } } else { var deletionRange = new Range(modification.Index, modification.Index + modification.NumberOfItems); if (deletionRange.ContainsInclusive(range.StartIndex) && deletionRange.ContainsInclusive(range.EndIndex)) { // if the deletion range took out the range, remove it altogether return(new Range(deletionRange.StartIndex, deletionRange.StartIndex)); } else if (deletionRange.OverlapsInclusive(range)) { if (deletionRange.StartIndex <= range.StartIndex) { int overlappingCharCount = deletionRange.EndIndex - range.StartIndex; int numberOfCharactersBefore = range.StartIndex - deletionRange.StartIndex; int numberOfAvailableCharsToDelete = Math.Min(overlappingCharCount, range.Length); return(new Range(range.StartIndex - numberOfCharactersBefore, range.EndIndex - numberOfCharactersBefore - numberOfAvailableCharsToDelete)); } else /* range.StartIndex <= deletionRange.EndIndex */ { var numberOfCharsToDelete = Math.Min(range.EndIndex - deletionRange.StartIndex, deletionRange.Length); return(new Range(range.StartIndex, range.EndIndex - numberOfCharsToDelete)); } } else if (deletionRange.StartIndex < range.StartIndex) { return(new Range(range.StartIndex - modification.NumberOfItems, range.EndIndex - modification.NumberOfItems)); } else { return(range); } } }
/// <summary> /// Verifies that when the given modification is operated on the given range, that the expect /// range is output. /// </summary> private void VerifyModification(Range original, RangeModification modification, Range expected) { // note, technically the single range is all that SHOULD be tested, but since everything // funnels through here, we might as well test that when the ranges are duplicated, that all of // the ranges end up the same (this wasn't always the case :: ) { // single range, easy Reset(); _collection.MarkRange(original, _markupDescriptor, null); /* Extra Check */ var originalMarkup = _collection.First(); originalMarkup.GetRange().Should().BeEquivalentTo(original); /* /Extra Check */ _collection.UpdateFromEvent(modification); Ranges.Should().HaveElementAt(0, expected); /* Extra Check */ var latestMarkup = _collection.First(); latestMarkup.Should().BeSameAs(originalMarkup); /* /Extra Check */ } { // duplicated range, let's make sure that works right Reset(); _collection.MarkRange(original, _markupDescriptor, null); _collection.MarkRange(original, _markupDescriptor, null); _collection.UpdateFromEvent(modification); Ranges.Should().HaveElementAt(0, expected, "'duplicated twice'"); Ranges.Should().HaveElementAt(1, expected, "'duplicated twice'"); } { // duplicated duplicated range, just for good measure Reset(); _collection.MarkRange(original, _markupDescriptor, null); _collection.MarkRange(original, _markupDescriptor, null); _collection.MarkRange(original, _markupDescriptor, null); _collection.UpdateFromEvent(modification); Ranges.Should().HaveElementAt(0, expected, "'duplicated thrice'"); Ranges.Should().HaveElementAt(1, expected, "'duplicated thrice'"); Ranges.Should().HaveElementAt(2, expected, "'duplicated thrice'"); } // this is really to make sure that our Transform method is working correctly var transformed = Transform(original, modification); transformed.Should().Be(expected, "the Transform is supposed to be correct"); }
public void InsertText_Iterations(int insertionPoint, int length) { // it's actually very easy to take a given TextRange and TextModification and figure out what // the output region does (SubFragmentMarkupCollection is so complicated because it manages the // text-ranges in an efficient, easy to update manner). Given that the algorithm is so easy // that it can be implemented in the function Transform, we can easily loop through every // combination of range and modification and verify that we get what we expect. That's what // this function does. var actualModification = new RangeModification(insertionPoint, length, wasAdded: true); VerifyCorrectness(actualModification); }
private void VerifyCorrectness(RangeModification modification) { AddAllRangesToCollection(_allValidTextRanges.Value); _collection.UpdateFromEvent(modification); var rangesOriginal = _allValidTextRanges.Value; var actualRanges = Ranges; for (var i = 0; i < Ranges.Length; i++) { var type = modification.WasAdded ? "insert" : "deletion"; var msg = $"'operation {type} @{modification.Index} of @{modification.NumberOfItems} characters'. Original was {rangesOriginal[i]}"; var expected = Transform(rangesOriginal[i], modification); DidYouKnow.That(Ranges).Should().HaveElementAt(i, expected, because: msg); // msg } }
public TestRangeModification(RangeModification modification) { Modification = modification; }