Esempio n. 1
0
        /// <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));
            }
        }
Esempio n. 2
0
        private int[] ParseLineStarts()
        {
            int length = this.Length;

            // Corner case check
            if (0 == this.Length)
            {
                return(new[] { 0 });
            }

            var position     = 0;
            var index        = 0;
            var arrayBuilder = ArrayBuilder <int> .GetInstance();

            // 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++];

                // 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' && index < length && this[index] == '\n')
                {
                    index++;
                }
                else if (!TextUtilities.IsAnyLineBreakCharacter(c))
                {
                    continue;
                }

                arrayBuilder.Add(position);
                position = index;
            }

            // Create a start for the final line.
            arrayBuilder.Add(position);

            return(arrayBuilder.ToArrayAndFree());
        }
Esempio n. 3
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());
        }
Esempio n. 4
0
        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()));
        }