in src/Editor/Text/Impl/TextModel/HighFidelityTrackingPoint.cs [141:282]
internal static int TrackPositionForwardInTime(PointTrackingMode trackingMode,
TrackingFidelityMode fidelity,
ref List<VersionNumberPosition> noninvertibleHistory,
int currentPosition,
ITextVersion currentVersion,
ITextVersion targetVersion)
{
System.Diagnostics.Debug.Assert(targetVersion.VersionNumber > currentVersion.VersionNumber);
// track forward in time
ITextVersion roverVersion = currentVersion;
while (roverVersion != targetVersion)
{
currentVersion = roverVersion;
roverVersion = currentVersion.Next;
if (fidelity == TrackingFidelityMode.UndoRedo &&
roverVersion.ReiteratedVersionNumber != roverVersion.VersionNumber &&
noninvertibleHistory != null)
{
int p = noninvertibleHistory.BinarySearch
(new VersionNumberPosition(roverVersion.ReiteratedVersionNumber, 0),
VersionNumberPositionComparer.Instance);
if (p >= 0)
{
currentPosition = noninvertibleHistory[p].Position;
continue;
}
}
int changeCount = currentVersion.Changes.Count;
if (changeCount == 0)
{
// A version which has no text changes preserves the reiterated version number of the previous version,
// so its version number and reiterated version number will be different. We need to record a possibly
// noninvertible change with the reiterated version number as well as with the version number.
Debug.Assert(roverVersion.VersionNumber != roverVersion.ReiteratedVersionNumber);
if (roverVersion.VersionNumber != roverVersion.ReiteratedVersionNumber)
{
RecordNoninvertibleTransition(ref noninvertibleHistory, currentPosition, roverVersion.ReiteratedVersionNumber);
}
continue;
}
int currentVersionStartingPosition = currentPosition;
for (int c = 0; c < changeCount; ++c)
{
ITextChange textChange = currentVersion.Changes[c];
if (IsOpaque(textChange))
{
if (textChange.NewPosition + textChange.OldLength <= currentPosition)
{
// point is to the right of the opaque change. shift it.
currentPosition += textChange.Delta;
continue;
}
else if (textChange.NewPosition <= currentPosition)
{
// point is within the opaque change. stay put (but within the change)
if (textChange.NewEnd <= currentPosition)
{
// we shift to the left because the new text is shorter than what
// it replaced. this is a noninvertible transition, so remember it.
RecordNoninvertibleTransition(ref noninvertibleHistory, currentVersionStartingPosition, currentVersion.VersionNumber);
currentPosition = textChange.NewEnd;
}
// No further change in this version can affect us.
break;
}
else
{
// point is to the left of the opaque change. no effect, and we are done.
break;
}
}
if (trackingMode == PointTrackingMode.Positive)
{
// Positive tracking mode: point moves with insertions at its location.
if (textChange.NewPosition <= currentPosition)
{
if (textChange.NewPosition + textChange.OldLength <= currentPosition)
{
// easy: the change is entirely to the left of our position.
// if NewPosition + OldLength == currentPosition, it means that the character
// immediately before our position was deleted (or replaced). When going back
// in time we will see this as an insertion, and since our tracking mode is positive,
// we will put ourselves after it.
currentPosition += textChange.Delta;
}
else
{
// textChange deleted text at the position of the tracked point; now the point is
// pushed to the end of the textChange insertion.
// This is a noninvertible transition. Remember it.
RecordNoninvertibleTransition(ref noninvertibleHistory, currentVersionStartingPosition, currentVersion.VersionNumber);
currentPosition = textChange.NewEnd;
break;
}
}
else
{
// the change is entirely to the right of our position; since changes are
// sorted, we are done.
break;
}
}
else
{
// Negative tracking mode: point doesn't move with respect to insertions at its location.
if (textChange.NewPosition < currentPosition)
{
if (textChange.NewPosition + textChange.OldLength < currentPosition)
{
// easy: the change is entirely to the left of our position. Since we
// are paying attention to the tracking mode when tracking back (to handle the
// before-the-origin-version case), we need to consider NewPosition + OldLength ==
// currentPosition case as noninvertible.
currentPosition += textChange.Delta;
}
else
{
// textChange deleted text at the position of the tracked point; now the point is
// at the position of the change, prior to any insertion.
// This is a nonivertible transition.
RecordNoninvertibleTransition(ref noninvertibleHistory, currentVersionStartingPosition, currentVersion.VersionNumber);
currentPosition = textChange.NewPosition;
break;
}
}
else
{
break;
}
}
}
}
return currentPosition;
}