/// <summary> Finds a series of nodes that are affected by a change at the given index. </summary>
        /// <param name="range"> The range at which a change is being applied </param>
        /// <returns> The nodes that are affected by the change. </returns>
        private FoundNode GetAffectedNodes(Range range)
        {
            var listNode = _subFragments.First;
            int absoluteIndexOfLastSubFragment = 0;

            var foundNode = new FoundNode();

            while (listNode != null)
            {
                MarkupNodeReference subFragment = listNode.Value;

                var subFragmentAbsoluteStart = absoluteIndexOfLastSubFragment + subFragment.RelativeOffsetSinceLastNode;
                var nodeRange = new Range(subFragmentAbsoluteStart, subFragmentAbsoluteStart + subFragment.Length);

                if (range.EndIndex < nodeRange.StartIndex)
                {
                    // the change is before every remaining sub-fragment, so we can bail out now
                    foundNode.MarkRemaining(listNode);
                    break;
                }

                if (nodeRange.OverlapsInclusive(range))
                {
                    foundNode.RememberFirstAndLast(nodeRange.StartIndex, listNode);
                }

                absoluteIndexOfLastSubFragment = nodeRange.StartIndex;
                listNode = listNode.Next;
            }

            return(foundNode);
        }
        /// <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);
                }
            }
        }