public override string ToString() { VersionSpanHistory c = this.cachedSpan; System.Text.StringBuilder sb = new System.Text.StringBuilder("*"); sb.Append(ToString(c.Version, c.Span, this.trackingMode)); if (c.NoninvertibleStartHistory != null) { sb.Append("[Start"); foreach (VersionNumberPosition vp in c.NoninvertibleStartHistory) { sb.Append(string.Format(System.Globalization.CultureInfo.CurrentCulture, "V{0}@{1}", vp.VersionNumber, vp.Position)); } sb.Append("]"); } if (c.NoninvertibleEndHistory != null) { sb.Append("[End"); foreach (VersionNumberPosition vp in c.NoninvertibleEndHistory) { sb.Append(string.Format(System.Globalization.CultureInfo.CurrentCulture, "V{0}@{1}", vp.VersionNumber, vp.Position)); } sb.Append("]"); } return(sb.ToString()); }
internal HighFidelityTrackingSpan(ITextVersion version, Span span, SpanTrackingMode spanTrackingMode, TrackingFidelityMode fidelity) : base(version, span, spanTrackingMode) { if (fidelity != TrackingFidelityMode.UndoRedo && fidelity != TrackingFidelityMode.Backward) { throw new ArgumentOutOfRangeException(nameof(fidelity)); } List <VersionNumberPosition> startHistory = null; List <VersionNumberPosition> endHistory = null; if (fidelity == TrackingFidelityMode.UndoRedo && version.VersionNumber > 0) { // The system may perform undo operations that reach prior to the initial version; if any of // those transitions are noninvertible, then redoing back to the initial version will give the // wrong answer. Thus we save the state of the span for the initial version, unless // the initial version happens to be version zero (in which case we could not undo past it). startHistory = new List <VersionNumberPosition>(); endHistory = new List <VersionNumberPosition>(); if (version.VersionNumber != version.ReiteratedVersionNumber) { Debug.Assert(version.ReiteratedVersionNumber < version.VersionNumber); // If the current version and reiterated version differ, also remember the position // using the reiterated version number, since when undoing back to this point it // will be the key that is used. startHistory.Add(new VersionNumberPosition(version.ReiteratedVersionNumber, span.Start)); endHistory.Add(new VersionNumberPosition(version.ReiteratedVersionNumber, span.End)); } startHistory.Add(new VersionNumberPosition(version.VersionNumber, span.Start)); endHistory.Add(new VersionNumberPosition(version.VersionNumber, span.End)); } this.cachedSpan = new VersionSpanHistory(version, span, startHistory, endHistory); this.fidelity = fidelity; }
protected override Span TrackSpan(ITextVersion targetVersion) { // Compute the span on the requested snapshot. // This object caches the most recently requested version and the span in that version. // // We are relying on the atomicity of pointer copies (this.cachedSpan might change after we've // fetched it below but we will always get a self-consistent VersionSpan). This ensures we // have proper behavior when called from multiple threads (multiple threads may all track and update the // cached value if called at inconvenient times, but they will return consistent results). // // In most cases, one can track backwards from the cached version to a previously computed // version and get the same result, but this is not always the case: in particular, when one or both // ends of the span lie in a deleted region, simulating reinsertion of that region will not cause // the previous value of the span to be recovered. Such transitions are called noninvertible. // This class explicitly tracks the positions of span endpoints for versions for which the subsequent // transition is noninvertible; this allows the value to be computed properly when tracking backwards // or in undo/redo situations. VersionSpanHistory cached = this.cachedSpan; // must fetch just once if (targetVersion == cached.Version) { // easy! return(cached.Span); } PointTrackingMode startMode = (this.trackingMode == SpanTrackingMode.EdgeExclusive || this.trackingMode == SpanTrackingMode.EdgePositive) ? PointTrackingMode.Positive : PointTrackingMode.Negative; PointTrackingMode endMode = (this.trackingMode == SpanTrackingMode.EdgeExclusive || this.trackingMode == SpanTrackingMode.EdgeNegative) ? PointTrackingMode.Negative : PointTrackingMode.Positive; List <VersionNumberPosition> noninvertibleStartHistory = cached.NoninvertibleStartHistory; List <VersionNumberPosition> noninvertibleEndHistory = cached.NoninvertibleEndHistory; Span targetSpan; if (targetVersion.VersionNumber > cached.Version.VersionNumber) { // Compute the target span by going forward from the cached version int start = HighFidelityTrackingPoint.TrackPositionForwardInTime (startMode, this.fidelity, ref noninvertibleStartHistory, cached.Span.Start, cached.Version, targetVersion); int end = HighFidelityTrackingPoint.TrackPositionForwardInTime (endMode, this.fidelity, ref noninvertibleEndHistory, cached.Span.End, cached.Version, targetVersion); targetSpan = Span.FromBounds(start, System.Math.Max(start, end)); // Cache the new span this.cachedSpan = new VersionSpanHistory(targetVersion, targetSpan, noninvertibleStartHistory, noninvertibleEndHistory); } else { // we are looking for a version prior to the cached version. int start = HighFidelityTrackingPoint.TrackPositionBackwardInTime (startMode, this.fidelity == TrackingFidelityMode.Backward ? noninvertibleStartHistory : null, cached.Span.Start, cached.Version, targetVersion); int end = HighFidelityTrackingPoint.TrackPositionBackwardInTime (endMode, this.fidelity == TrackingFidelityMode.Backward ? noninvertibleEndHistory : null, cached.Span.End, cached.Version, targetVersion); targetSpan = Span.FromBounds(start, System.Math.Max(start, end)); } return(targetSpan); }