/// <summary> /// Creates a ReadOnlyRegionHandle for the given buffer and span. /// </summary> /// <param name="version"> /// The <see cref="TextVersion"/> with which this read only region is associated. /// </param> /// <param name="span"> /// The span of interest. /// </param> /// <param name="trackingMode"> /// Specifies the tracking behavior of the read only region. /// </param> /// <param name="edgeInsertionMode"> /// Specifies if insertions should be allowed at the edges /// </param> /// <remarks> /// Don't call this constructor with invalid parameters. It doesn't verify all of them. /// </remarks> internal ReadOnlyRegion(TextVersion version, Span span, SpanTrackingMode trackingMode, EdgeInsertionMode edgeInsertionMode, DynamicReadOnlyRegionQuery callback) { _edgeInsertionMode = edgeInsertionMode; // TODO: change to simple forward tracking text span _trackingSpan = new ForwardFidelityTrackingSpan(version, span, trackingMode); QueryCallback = callback; }
internal TextContentChangedEventArgs ApplyReload(StringRebuilder newContent, EditOptions editOptions, object editTag) { // we construct a normalized change list where the inserted text is a reference string that // points "forward" to the next snapshot and whose deleted text is a reference string that points // "backward" to the prior snapshot. This pins both snapshots in memory but that's better than materializing // giant strings, and when (?) we have paging text storage, memory requirements will be minimal. TextVersion newVersion = new TextVersion(this, this.currentVersion.VersionNumber + 1, this.currentVersion.VersionNumber + 1, newContent.Length); ITextSnapshot oldSnapshot = this.currentSnapshot; TextSnapshot newSnapshot = new TextSnapshot(this, newVersion, newContent); ReferenceChangeString oldText = new ReferenceChangeString(new SnapshotSpan(oldSnapshot, 0, oldSnapshot.Length)); ReferenceChangeString newText = new ReferenceChangeString(new SnapshotSpan(newSnapshot, 0, newSnapshot.Length)); TextChange change = new TextChange(oldPosition: 0, oldText: oldText, newText: newText, currentSnapshot: oldSnapshot); this.currentVersion.AddNextVersion(NormalizedTextChangeCollection.Create(new FrugalList <TextChange>() { change }, editOptions.ComputeMinimalChange ? (StringDifferenceOptions?)editOptions.DifferenceOptions : null, this.textDifferencingService, oldSnapshot, newSnapshot), newVersion); this.builder = newContent; this.currentVersion = newVersion; this.currentSnapshot = newSnapshot; return(new TextContentChangedEventArgs(oldSnapshot, newSnapshot, editOptions, editTag)); }
internal TextContentChangedEventArgs ApplyReload(StringRebuilder newContent, EditOptions editOptions, object editTag) { // we construct a normalized change list where the inserted text is a reference string that // points "forward" to the next snapshot and whose deleted text is a reference string that points // "backward" to the prior snapshot. This pins both snapshots in memory but that's better than materializing // giant strings, and when (?) we have paging text storage, memory requirements will be minimal. ITextSnapshot oldSnapshot = this.currentSnapshot; StringRebuilder oldContent = BufferFactoryService.StringRebuilderFromSnapshotSpan(new SnapshotSpan(oldSnapshot, 0, oldSnapshot.Length)); TextChange change = TextChange.Create(oldPosition: 0, oldText: oldContent, newText: newContent, currentSnapshot: oldSnapshot); TextVersion newVersion = this.currentVersion.CreateNext(changes: null, newLength: newContent.Length, reiteratedVersionNumber: -1); TextSnapshot newSnapshot = new TextSnapshot(this, newVersion, newContent); this.currentVersion.SetChanges(NormalizedTextChangeCollection.Create(new TextChange[] { change }, editOptions.ComputeMinimalChange ? (StringDifferenceOptions?)editOptions.DifferenceOptions : null, this.textDifferencingService, oldSnapshot, newSnapshot)); this.currentVersion = newVersion; this.builder = newContent; this.currentSnapshot = newSnapshot; return(new TextContentChangedEventArgs(oldSnapshot, newSnapshot, editOptions, editTag)); }
public ForwardFidelityCustomTrackingSpan(TextVersion version, Span span, object customState, CustomTrackToVersion behavior) : base(version, span, SpanTrackingMode.Custom) { if (behavior == null) { throw new ArgumentNullException(nameof(behavior)); } this.behavior = behavior; this.customState = customState; }
protected BaseBuffer(IContentType contentType, int initialLength, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations) { // parameters are validated outside Debug.Assert(contentType != null); this.contentType = contentType; this.currentVersion = new TextVersion(this, 0, 0, initialLength); this.textDifferencingService = textDifferencingService; this.guardedOperations = guardedOperations; }
protected BaseBuffer(IContentType contentType, int initialLength, ITextDifferencingService textDifferencingService, GuardedOperations guardedOperations) { // parameters are validated outside Debug.Assert(contentType != null); this.contentType = contentType; this.currentVersion = new TextVersion(this, new TextImageVersion(initialLength)); // this.builder should be set in calling ctor this.textDifferencingService = textDifferencingService; this.guardedOperations = guardedOperations; }
/// <summary> /// Attaches the change to the current node and creates the next version node /// </summary> public TextVersion CreateNext(INormalizedTextChangeCollection changes, int reiteratedVersionNumber) { this.normalizedChanges = changes; int delta = 0; int changeCount = changes.Count; for (int c = 0; c < changeCount; ++c) { delta += changes[c].Delta; } this.next = new TextVersion(this.TextBuffer, this.VersionNumber + 1, reiteratedVersionNumber, this.versionLength + delta); return(this.next); }
/// <summary> /// Create a new version based on applying <paramref name="changes"/> to this. /// </summary> /// <param name="changes">null if set later</param> /// <param name="newLength">use -1 to compute a length</param> /// <param name="reiteratedVersionNumber">use -1 to get the default value</param> /// <remarks> /// <para>If <paramref name="changes"/> can be null, then <paramref name="newLength"/> cannot be -1.</para> /// </remarks> internal TextVersion CreateNext(INormalizedTextChangeCollection changes, int newLength = -1, int reiteratedVersionNumber = -1) { if (this.Next != null) { throw new InvalidOperationException("Not allowed to CreateNext twice"); } var newTextImageVersion = this._textImageVersion.CreateNext(reiteratedVersionNumber: reiteratedVersionNumber, length: newLength, changes: changes); var next = new TextVersion(this.TextBuffer, newTextImageVersion); this.Next = next; return(next); }
/// <summary> /// Used when the normalized change list of a version needs to refer ahead to the following snapshot /// </summary> public void AddNextVersion(INormalizedTextChangeCollection changes, TextVersion nextVersion) { this.normalizedChanges = changes; this.next = nextVersion; }
protected void SetCurrentVersionAndSnapshot(INormalizedTextChangeCollection normalizedChanges, int reiteratedVersionNumber = -1) { this.currentVersion = this.currentVersion.CreateNext(normalizedChanges, newLength: -1, reiteratedVersionNumber: reiteratedVersionNumber); this.builder = this.ApplyChangesToStringRebuilder(normalizedChanges, this.builder); this.currentSnapshot = TakeSnapshot(); }
protected void SetCurrentVersionAndSnapshot(INormalizedTextChangeCollection normalizedChanges) { this.currentVersion = this.currentVersion.CreateNext(normalizedChanges); UpdateSnapshot(); }
protected void SetCurrentVersionAndSnapshot(INormalizedTextChangeCollection normalizedChanges, int reiteratedVersionNumber) { this.currentVersion = this.currentVersion.CreateNext(normalizedChanges, reiteratedVersionNumber); UpdateSnapshot(); }
/// <summary> /// Construct a ReadOnlySpanCollection that contains everything in a list of spans. /// </summary> /// <param name="regions">ReadOnlyRegions</param> /// <param name="version">The version this span collection applies to.</param> /// <remarks> /// <para>The list of spans will be sorted and normalized (overlapping and adjoining spans will be combined).</para> /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="regions"/> is null.</exception> internal ReadOnlySpanCollection(TextVersion version, IEnumerable <IReadOnlyRegion> regions) : base(NormalizeSpans(version, regions)) { regionsWithActions = regions.Where(region => region.QueryCallback != null).ToList(); }
private static IList <ReadOnlySpan> NormalizeSpans(TextVersion version, IEnumerable <IReadOnlyRegion> regions) { List <IReadOnlyRegion> sorted = new List <IReadOnlyRegion>(regions.Where(region => region.QueryCallback == null)); if (sorted.Count == 0) { return(new FrugalList <ReadOnlySpan>()); } else if (sorted.Count == 1) { return(new FrugalList <ReadOnlySpan>() { new ReadOnlySpan(version, sorted[0]) }); } else { sorted.Sort((s1, s2) => s1.Span.GetSpan(version).Start.CompareTo(s2.Span.GetSpan(version).Start)); List <ReadOnlySpan> normalized = new List <ReadOnlySpan>(sorted.Count); int oldStart = sorted[0].Span.GetSpan(version).Start; int oldEnd = sorted[0].Span.GetSpan(version).End; EdgeInsertionMode oldStartEdgeInsertionMode = sorted[0].EdgeInsertionMode; EdgeInsertionMode oldEndEdgeInsertionMode = sorted[0].EdgeInsertionMode; SpanTrackingMode oldSpanTrackingMode = sorted[0].Span.TrackingMode; for (int i = 1; (i < sorted.Count); ++i) { int newStart = sorted[i].Span.GetSpan(version).Start; int newEnd = sorted[i].Span.GetSpan(version).End; // Since the new span's start occurs after the old span's end, we can just add the old span directly. if (oldEnd < newStart) { normalized.Add(new ReadOnlySpan(version, new Span(oldStart, oldEnd - oldStart), oldSpanTrackingMode, oldStartEdgeInsertionMode, oldEndEdgeInsertionMode)); oldStart = newStart; oldEnd = newEnd; oldStartEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldEndEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldSpanTrackingMode = sorted[i].Span.TrackingMode; } else { // The two read only regions start at the same position if (newStart == oldStart) { // If one read only region denies edge insertions, combined they do as well if (sorted[i].EdgeInsertionMode == EdgeInsertionMode.Deny) { oldStartEdgeInsertionMode = EdgeInsertionMode.Deny; } // This is tricky. We want one span that will be inclusive tracking, and one that won't. if (oldSpanTrackingMode != sorted[i].Span.TrackingMode) { // Since the read only regions cover the same exact span, the combined one will be edge inclusive tracking if (oldEnd == newEnd) { oldSpanTrackingMode = SpanTrackingMode.EdgeInclusive; } else if (oldEnd < newEnd) { // Since the old span and new span don't have the same span tracking mode and don't end in the same position, we need to create a new span that is edge inclusive // and deny inserts between it and the next span. normalized.Add(new ReadOnlySpan(version, new Span(oldStart, oldEnd - oldStart), SpanTrackingMode.EdgeInclusive, oldStartEdgeInsertionMode, EdgeInsertionMode.Deny)); oldStart = oldEnd; // Explicitly use the old end here since we want these spans to be adjacent oldEnd = newEnd; oldStartEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldEndEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldSpanTrackingMode = sorted[i].Span.TrackingMode; } else { // Since the new span ends first, create a span that is edge inclusive tracking that ends at the the new span's end. normalized.Add(new ReadOnlySpan(version, new Span(newStart, newEnd - newStart), SpanTrackingMode.EdgeInclusive, oldStartEdgeInsertionMode, EdgeInsertionMode.Deny)); oldStart = newEnd; // Explicitly use the new end here since we want these spans to be adjacent } } } if (oldEnd < newEnd) { // If the tracking modes are different then we need to create a new span // with the old tracking mode, and start a new span with the new span tracking mode. // Also, if the old end and the new start are identical and both edge insertion mode's // are allow, then we need to create a new span. if (((oldEnd == newStart) && ((oldEndEdgeInsertionMode == EdgeInsertionMode.Allow) && (sorted[i].EdgeInsertionMode == EdgeInsertionMode.Allow))) || (oldSpanTrackingMode != sorted[i].Span.TrackingMode)) { normalized.Add(new ReadOnlySpan(version, new Span(oldStart, oldEnd - oldStart), oldSpanTrackingMode, oldStartEdgeInsertionMode, oldEndEdgeInsertionMode)); oldStart = oldEnd; // Explicitly use the old end here since we want these spans to be adjacent. oldEnd = newEnd; // If we are splitting up the spans because of a change in tracking mode, then explicitly deny inserting between them if (oldSpanTrackingMode != sorted[i].Span.TrackingMode) { oldStartEdgeInsertionMode = EdgeInsertionMode.Deny; // Explicitly use deny here since we don't want to allow insertions between these spans } else { oldStartEdgeInsertionMode = EdgeInsertionMode.Allow; } oldEndEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldSpanTrackingMode = sorted[i].Span.TrackingMode; } else { oldEnd = newEnd; oldEndEdgeInsertionMode = sorted[i].EdgeInsertionMode; } } else if (oldEnd == newEnd) { if (sorted[i].EdgeInsertionMode == EdgeInsertionMode.Deny) { oldEndEdgeInsertionMode = EdgeInsertionMode.Deny; } if (oldSpanTrackingMode != sorted[i].Span.TrackingMode) { normalized.Add(new ReadOnlySpan(version, new Span(oldStart, oldEnd - oldStart), oldSpanTrackingMode, oldStartEdgeInsertionMode, oldEndEdgeInsertionMode)); oldStart = newEnd; oldEnd = newEnd; oldStartEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldEndEdgeInsertionMode = sorted[i].EdgeInsertionMode; oldSpanTrackingMode = sorted[i].Span.TrackingMode; } } } } normalized.Add(new ReadOnlySpan(version, new Span(oldStart, oldEnd - oldStart), oldSpanTrackingMode, oldStartEdgeInsertionMode, oldEndEdgeInsertionMode)); return(normalized); } }