protected override TextLineCollection GetLinesCore() { var oldLineInfo = _oldText.Lines; 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 _changes) { // change.Span.Start < position already ruled out by SourceText.WithChanges // if we've skipped a range, add if (change.Span.Start > position) { if (endsWithCR && _newText[position + delta] == '\n') { 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); } } if (change.NewLength > 0) { var text = GetSubText(new TextSpan(change.Span.Start + delta, change.NewLength)); // optimizations copied from SourceText.LineInfo.ParseLineStarts var index = 0; while (index < text.Length) { char c = text[index++]; // Common case - ASCII & not a line break // if (c > '\r' && c <= 127) // if (c >= ('\r'+1) && c <= 127) const uint bias = '\r' + 1; if (unchecked (c - bias) <= (127 - bias)) { continue; } if (endsWithCR && c == '\n') { lineStarts.RemoveLast(); } else if (c == '\r' && index < text.Length && text[index] == '\n') { index++; } else if (!TextUtilities.IsAnyLineBreakCharacter(c)) { continue; } lineStarts.Add(change.Span.Start + delta + index); } endsWithCR = text[change.NewLength - 1] == '\r'; } position = change.Span.End; delta += (change.NewLength - change.Span.Length); } if (position < _oldText.Length) { if (endsWithCR && _newText[position + delta] == '\n') { 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())); }
private int[] ParseLineStarts() { // Corner case check if (0 == this.Length) { return(new[] { 0 }); } var lineStarts = ArrayBuilder <int> .GetInstance(); lineStarts.Add(0); // there is always the first line var lastWasCR = false; // The following loop goes through every character in the text. It is highly // performance critical, and thus inlines knowledge about common line breaks // and non-line breaks. EnumerateChars( (int position, char[] buffer, int length) => { var index = 0; if (lastWasCR) { if (length > 0 && buffer[0] == '\n') { index++; } lineStarts.Add(position + index); lastWasCR = false; } while (index < length) { char c = buffer[index]; index++; // Common case - ASCII & not a line break // if (c > '\r' && c <= 127) // if (c >= ('\r'+1) && c <= 127) const uint bias = '\r' + 1; if (unchecked (c - bias) <= (127 - bias)) { continue; } // Assumes that the only 2-char line break sequence is CR+LF if (c == '\r') { if (index < length && buffer[index] == '\n') { index++; } else if (index >= length) { lastWasCR = true; continue; } } else if (!TextUtilities.IsAnyLineBreakCharacter(c)) { continue; } // next line starts at index lineStarts.Add(position + index); } } ); return(lineStarts.ToArrayAndFree()); }
private int[] ParseLineStarts() { int length = this.Length; // Corner case check if (0 == this.Length) { return(new int[] { 0 }); } var position = 0; var index = 0; var arrayBuilder = ArrayBuilder <int> .GetInstance(); var lineNumber = 0; // The following loop goes through every character in the text. It is highly // performance critical, and thus inlines knowledge about common line breaks // and non-line breaks. while (index < length) { char c = this[index]; int lineBreakLength; // common case - ASCII & not a line break if (c > '\r' & c <= 127) { index++; continue; } else if (c == '\r' && index + 1 < length && this[index + 1] == '\n') { lineBreakLength = 2; } else if (c == '\n') { lineBreakLength = 1; } else { lineBreakLength = TextUtilities.GetLengthOfLineBreak(this, index); } if (0 == lineBreakLength) { index++; } else { arrayBuilder.Add(position); index += lineBreakLength; position = index; lineNumber++; } } // Create a start for the final line. arrayBuilder.Add(position); return(arrayBuilder.ToArrayAndFree()); }