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