예제 #1
0
        public virtual SourceText WithChanges(IEnumerable <TextChange> changes)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }

            if (!changes.Any())
            {
                return(this);
            }

            var segments = ArrayBuilder <SourceText> .GetInstance();

            var changeRanges = ArrayBuilder <TextChangeRange> .GetInstance();

            int position = 0;

            foreach (var change in changes)
            {
                // there can be no overlapping changes
                if (change.Span.Start < position)
                {
                    // Handle the case of unordered changes by sorting the input and retrying. This is inefficient, but
                    // downstream consumers have been known to hit this case in the past and we want to avoid crashes.
                    // https://github.com/dotnet/roslyn/pull/26339
                    if (change.Span.End <= changeRanges.Last().Span.Start)
                    {
                        changes = (from c in changes
                                   where !c.Span.IsEmpty || c.NewText?.Length > 0
                                   orderby c.Span
                                   select c).ToList();
                        return(WithChanges(changes));
                    }

                    throw new ArgumentException(CodeAnalysisResources.ChangesMustNotOverlap, nameof(changes));
                }

                var newTextLength = change.NewText?.Length ?? 0;

                // ignore changes that don't change anything
                if (change.Span.Length == 0 && newTextLength == 0)
                {
                    continue;
                }

                // if we've skipped a range, add
                if (change.Span.Start > position)
                {
                    var subText = this.GetSubText(new TextSpan(position, change.Span.Start - position));
                    CompositeText.AddSegments(segments, subText);
                }

                if (newTextLength > 0)
                {
                    var segment = SourceText.From(change.NewText !, this.Encoding, this.ChecksumAlgorithm);
                    CompositeText.AddSegments(segments, segment);
                }

                position = change.Span.End;

                changeRanges.Add(new TextChangeRange(change.Span, newTextLength));
            }

            // no changes actually happened?
            if (position == 0 && segments.Count == 0)
            {
                changeRanges.Free();
                return(this);
            }

            if (position < this.Length)
            {
                var subText = this.GetSubText(new TextSpan(position, this.Length - position));
                CompositeText.AddSegments(segments, subText);
            }

            var newText = CompositeText.ToSourceTextAndFree(segments, this, adjustSegments: true);

            if (newText != this)
            {
                return(new ChangedText(this, newText, changeRanges.ToImmutableAndFree()));
            }
            else
            {
                return(this);
            }
        }
예제 #2
0
        /// <summary>
        /// Constructs a new SourceText from this text with the specified changes.
        /// </summary>
        public virtual SourceText WithChanges(IEnumerable <TextChange> changes)
        {
            if (changes == null)
            {
                throw new ArgumentNullException(nameof(changes));
            }

            if (!changes.Any())
            {
                return(this);
            }

            var segments = ArrayBuilder <SourceText> .GetInstance();

            var changeRanges = ArrayBuilder <TextChangeRange> .GetInstance();

            int position = 0;

            foreach (var change in changes)
            {
                // there can be no overlapping changes
                if (change.Span.Start < position)
                {
                    throw new ArgumentException(CodeAnalysisResources.ChangesMustBeOrderedAndNotOverlapping, nameof(changes));
                }

                var newTextLength = change.NewText?.Length ?? 0;

                // ignore changes that don't change anything
                if (change.Span.Length == 0 && newTextLength == 0)
                {
                    continue;
                }

                // if we've skipped a range, add
                if (change.Span.Start > position)
                {
                    var subText = this.GetSubText(new TextSpan(position, change.Span.Start - position));
                    CompositeText.AddSegments(segments, subText);
                }

                if (newTextLength > 0)
                {
                    var segment = SourceText.From(change.NewText, this.Encoding, this.ChecksumAlgorithm);
                    CompositeText.AddSegments(segments, segment);
                }

                position = change.Span.End;

                changeRanges.Add(new TextChangeRange(change.Span, newTextLength));
            }

            // no changes actually happend?
            if (position == 0 && segments.Count == 0)
            {
                changeRanges.Free();
                return(this);
            }

            if (position < this.Length)
            {
                var subText = this.GetSubText(new TextSpan(position, this.Length - position));
                CompositeText.AddSegments(segments, subText);
            }

            var newText = CompositeText.ToSourceTextAndFree(segments, this, adjustSegments: true);

            if (newText != this)
            {
                return(new ChangedText(this, newText, changeRanges.ToImmutableAndFree()));
            }
            else
            {
                return(this);
            }
        }