/// <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"); }
private Action CreateActionFor(Range r) { return(() => _collection.MarkRange(r, _markupDescriptor, null)); }