/// <summary> /// Creates a <see cref="TextLine"/> instance. /// </summary> /// <param name="text">The source text.</param> /// <param name="span">The span of the line.</param> /// <returns>An instance of <see cref="TextLine"/>.</returns> /// <exception cref="ArgumentOutOfRangeException">The span does not represent a text line.</exception> public static TextLine FromSpan(SourceText text, TextSpan span) { if (text == null) { throw new ArgumentNullException(nameof(text)); } if (span.Start > text.Length || span.Start < 0 || span.End > text.Length) { throw new ArgumentOutOfRangeException(nameof(span)); } if (text.Length > 0) { // check span is start of line if (span.Start > 0 && !TextUtilities.IsAnyLineBreakCharacter(text[span.Start - 1])) { throw new ArgumentOutOfRangeException(nameof(span), CodeAnalysisResources.SpanDoesNotIncludeStartOfLine); } bool endIncludesLineBreak = false; if (span.End > span.Start) { endIncludesLineBreak = TextUtilities.IsAnyLineBreakCharacter(text[span.End - 1]); } if (!endIncludesLineBreak && span.End < text.Length) { var lineBreakLen = TextUtilities.GetLengthOfLineBreak(text, span.End); if (lineBreakLen > 0) { // adjust span to include line breaks endIncludesLineBreak = true; span = new TextSpan(span.Start, span.Length + lineBreakLen); } } // check end of span is at end of line if (span.End < text.Length && !endIncludesLineBreak) { throw new ArgumentOutOfRangeException(nameof(span), CodeAnalysisResources.SpanDoesNotIncludeEndOfLine); } return(new TextLine(text, span.Start, span.End)); } else { return(new TextLine(text, 0, 0)); } }
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()); }