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