A segment that can be put into a TextSegmentCollection{T}.

A TextSegment can be stand-alone or part of a TextSegmentCollection{T}. If the segment is stored inside a TextSegmentCollection, its Offset and Length will be updated by that collection.

When the document changes, the offsets of all text segments in the TextSegmentCollection will be adjusted accordingly. Start offsets move like AnchorMovementType.AfterInsertion, end offsets move like AnchorMovementType.BeforeInsertion (i.e. the segment will always stay as small as possible).

If a document change causes a segment to be deleted completely, it will be reduced to length 0, but segments are never automatically removed from the collection. Segments with length 0 will never expand due to document changes, and they move as AfterInsertion.

Thread-safety: a TextSegmentCollection that is connected to a TextDocument may only be used on that document's owner thread. A disconnected TextSegmentCollection is safe for concurrent reads, but concurrent access is not safe when there are writes. Keep in mind that reading the Offset properties of a text segment inside the collection is a read access on the collection; and setting an Offset property of a text segment is a write access on the collection.

Inheritance: ISegment
 public void SetSelection(TextSegment segment)
 {
     SelectionStart = segment.StartOffset;
     SelectionEnd = segment.EndOffset;
 }
        private void FixTreeOnDelete(TextSegment node, TextSegment parentNode)
        {
            Debug.Assert(node == null || node.parent == parentNode);
            if (parentNode == null)
            {
                return;
            }

            // warning: node may be null
            var sibling = Sibling(node, parentNode);

            if (sibling.color == RED)
            {
                parentNode.color = RED;
                sibling.color    = BLACK;
                if (node == parentNode.left)
                {
                    RotateLeft(parentNode);
                }
                else
                {
                    RotateRight(parentNode);
                }

                sibling = Sibling(node, parentNode); // update value of sibling after rotation
            }

            if (parentNode.color == BLACK &&
                sibling.color == BLACK &&
                GetColor(sibling.left) == BLACK &&
                GetColor(sibling.right) == BLACK)
            {
                sibling.color = RED;
                FixTreeOnDelete(parentNode, parentNode.parent);
                return;
            }

            if (parentNode.color == RED &&
                sibling.color == BLACK &&
                GetColor(sibling.left) == BLACK &&
                GetColor(sibling.right) == BLACK)
            {
                sibling.color    = RED;
                parentNode.color = BLACK;
                return;
            }

            if (node == parentNode.left &&
                sibling.color == BLACK &&
                GetColor(sibling.left) == RED &&
                GetColor(sibling.right) == BLACK)
            {
                sibling.color      = RED;
                sibling.left.color = BLACK;
                RotateRight(sibling);
            }
            else if (node == parentNode.right &&
                     sibling.color == BLACK &&
                     GetColor(sibling.right) == RED &&
                     GetColor(sibling.left) == BLACK)
            {
                sibling.color       = RED;
                sibling.right.color = BLACK;
                RotateLeft(sibling);
            }
            sibling = Sibling(node, parentNode); // update value of sibling after rotation

            sibling.color    = parentNode.color;
            parentNode.color = BLACK;
            if (node == parentNode.left)
            {
                if (sibling.right != null)
                {
                    Debug.Assert(sibling.right.color == RED);
                    sibling.right.color = BLACK;
                }
                RotateLeft(parentNode);
            }
            else
            {
                if (sibling.left != null)
                {
                    Debug.Assert(sibling.left.color == RED);
                    sibling.left.color = BLACK;
                }
                RotateRight(parentNode);
            }
        }
        public TextSegment GetSelectionAsSegment()
        {
            TextSegment result = null;

            if (SelectionStart < SelectionEnd)
            {
                result = new TextSegment { StartOffset = SelectionStart, EndOffset = SelectionEnd };
            }
            else
            {
                result = new TextSegment { StartOffset = SelectionEnd, EndOffset = SelectionStart };
            }

            return result;
        }
        private void FixTreeOnInsert(TextSegment node)
        {
            Debug.Assert(node != null);
            Debug.Assert(node.color == RED);
            Debug.Assert(node.left == null || node.left.color == BLACK);
            Debug.Assert(node.right == null || node.right.color == BLACK);

            var parentNode = node.parent;

            if (parentNode == null)
            {
                // we inserted in the root -> the node must be black
                // since this is a root node, making the node black increments the number of black nodes
                // on all paths by one, so it is still the same for all paths.
                node.color = BLACK;
                return;
            }
            if (parentNode.color == BLACK)
            {
                // if the parent node where we inserted was black, our red node is placed correctly.
                // since we inserted a red node, the number of black nodes on each path is unchanged
                // -> the tree is still balanced
                return;
            }
            // parentNode is red, so there is a conflict here!

            // because the root is black, parentNode is not the root -> there is a grandparent node
            var grandparentNode = parentNode.parent;
            var uncleNode       = Sibling(parentNode);

            if (uncleNode != null && uncleNode.color == RED)
            {
                parentNode.color      = BLACK;
                uncleNode.color       = BLACK;
                grandparentNode.color = RED;
                FixTreeOnInsert(grandparentNode);
                return;
            }
            // now we know: parent is red but uncle is black
            // First rotation:
            if (node == parentNode.right && parentNode == grandparentNode.left)
            {
                RotateLeft(parentNode);
                node = node.left;
            }
            else if (node == parentNode.left && parentNode == grandparentNode.right)
            {
                RotateRight(parentNode);
                node = node.right;
            }
            // because node might have changed, reassign variables:
            parentNode      = node.parent;
            grandparentNode = parentNode.parent;

            // Now recolor a bit:
            parentNode.color      = BLACK;
            grandparentNode.color = RED;
            // Second rotation:
            if (node == parentNode.left && parentNode == grandparentNode.left)
            {
                RotateRight(grandparentNode);
            }
            else
            {
                // because of the first rotation, this is guaranteed:
                Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
                RotateLeft(grandparentNode);
            }
        }
 void ISegmentTree.Remove(TextSegment s)
 {
     RemoveSegment(s);
 }
 void ISegmentTree.UpdateAugmentedData(TextSegment node)
 {
     UpdateAugmentedData(node);
 }
 void ISegmentTree.Add(TextSegment s)
 {
     AddSegment(s);
 }
 private static bool GetColor(TextSegment node)
 {
     return(node != null ? node.color : BLACK);
 }