/// <summary>
        /// Computes line starts faster given already computed line starts from text before the change.
        /// </summary>
        protected override TextLineCollection GetLinesCore()
        {
            SourceText         oldText;
            TextLineCollection oldLineInfo;

            if (!_info.WeakOldText.TryGetTarget(out oldText) || !oldText.TryGetLines(out oldLineInfo))
            {
                // no old line starts? do it the hard way.
                return(base.GetLinesCore());
            }

            // compute line starts given changes and line starts already computed from previous text
            var lineStarts = ArrayBuilder <int> .GetInstance();

            lineStarts.Add(0);

            // position in the original document
            var position = 0;

            // delta generated by already processed changes (position in the new document = position + delta)
            var delta = 0;

            // true if last segment ends with CR and we need to check for CR+LF code below assumes that both CR and LF are also line breaks alone
            var endsWithCR = false;

            foreach (var change in _info.ChangeRanges)
            {
                // include existing line starts that occur before this change
                if (change.Span.Start > position)
                {
                    if (endsWithCR && _newText[position + delta] == '\n')
                    {
                        // remove last added line start (it was due to previous CR)
                        // a new line start including the LF will be added next
                        lineStarts.RemoveLast();
                    }

                    var lps = oldLineInfo.GetLinePositionSpan(TextSpan.FromBounds(position, change.Span.Start));
                    for (int i = lps.Start.Line + 1; i <= lps.End.Line; i++)
                    {
                        lineStarts.Add(oldLineInfo[i].Start + delta);
                    }

                    endsWithCR = oldText[change.Span.Start - 1] == '\r';

                    // in case change is inserted between CR+LF we treat CR as line break alone,
                    // but this line break might be retracted and replaced with new one in case LF is inserted
                    if (endsWithCR && change.Span.Start < oldText.Length && oldText[change.Span.Start] == '\n')
                    {
                        lineStarts.Add(change.Span.Start + delta);
                    }
                }

                // include line starts that occur within newly inserted text
                if (change.NewLength > 0)
                {
                    var changeStart = change.Span.Start + delta;
                    var text        = GetSubText(new TextSpan(changeStart, change.NewLength));
                    if (endsWithCR && text[0] == '\n')
                    {
                        // remove last added line start (it was due to previous CR)
                        // a new line start including the LF will be added next
                        lineStarts.RemoveLast();
                    }

                    // Skip first line (it is always at offset 0 and corresponds to the previous line)
                    for (int i = 1; i < text.Lines.Count; i++)
                    {
                        lineStarts.Add(changeStart + text.Lines[i].Start);
                    }

                    endsWithCR = text[change.NewLength - 1] == '\r';
                }

                position = change.Span.End;
                delta   += change.NewLength - change.Span.Length;
            }

            // include existing line starts that occur after all changes
            if (position < oldText.Length)
            {
                if (endsWithCR && _newText[position + delta] == '\n')
                {
                    // remove last added line start (it was due to previous CR)
                    // a new line start including the LF will be added next
                    lineStarts.RemoveLast();
                }

                var lps = oldLineInfo.GetLinePositionSpan(TextSpan.FromBounds(position, oldText.Length));
                for (int i = lps.Start.Line + 1; i <= lps.End.Line; i++)
                {
                    lineStarts.Add(oldLineInfo[i].Start + delta);
                }
            }

            return(new LineInfo(this, lineStarts.ToArrayAndFree()));
        }
 public override SourceText GetSubText(TextSpan span)
 {
     return(_newText.GetSubText(span));
 }
 /// <summary>
 /// Convert a <see cref="LinePositionSpan"/> to <see cref="TextSpan"/>.
 /// </summary>
 /// <param name="span"></param>
 public TextSpan GetTextSpan(LinePositionSpan span)
 {
     return(TextSpan.FromBounds(GetPosition(span.Start), GetPosition(span.End)));
 }
 public override string ToString(TextSpan span)
 {
     return(_newText.ToString(span));
 }
 /// <summary>
 /// Convert a <see cref="TextSpan"/> to a <see cref="LinePositionSpan"/>.
 /// </summary>
 /// <param name="span"></param>
 public LinePositionSpan GetLinePositionSpan(TextSpan span)
 {
     return(new LinePositionSpan(GetLinePosition(span.Start), GetLinePosition(span.End)));
 }