in src/Editor/Text/Impl/TextModel/Projection/ElisionMapNode.cs [491:727]
public ProjectionLineInfo GetLineFromPosition(ITextSnapshot sourceSnapshot,
int exposedPosition,
int sourcePrefixLineBreakCount,
int hiddenPrefixLineBreakCount,
int sourcePrefixSize,
int exposedPrefixSize,
int level)
{
//TraceEnter(level);
Span relativeExposedSpan = new Span(LeftTotalExposedSize(), this.exposedSize);
ProjectionLineCalculationState state = ProjectionLineCalculationState.Primary;
int relativeExposedPosition = exposedPosition;
ProjectionLineInfo pendingInfo = new ProjectionLineInfo();
do
{
if (relativeExposedPosition < relativeExposedSpan.Start)
{
#region relative ExposedPosition is in left subtree
Debug.Assert(state != ProjectionLineCalculationState.Append);
// recursively compute that part of the line that is in the left subtree
ProjectionLineInfo leftInfo = this.left.GetLineFromPosition
(sourceSnapshot : sourceSnapshot,
exposedPosition : relativeExposedPosition,
sourcePrefixLineBreakCount : sourcePrefixLineBreakCount,
hiddenPrefixLineBreakCount : hiddenPrefixLineBreakCount,
sourcePrefixSize : sourcePrefixSize,
exposedPrefixSize : exposedPrefixSize,
level : level + 1);
if (state == ProjectionLineCalculationState.Primary)
{
if (leftInfo.endComplete)
{
// leftInfo.startComplete may be false, but we aren't going to find anything
// further to the left at this level of the tree, so we are done here
// TraceExit(level, leftInfo);
return leftInfo;
}
else
{
// the end of the line extends into the current node. our new position
// of interest is the start of the exposed text in this node.
state = ProjectionLineCalculationState.Append;
pendingInfo = leftInfo;
relativeExposedPosition = relativeExposedSpan.Start;
continue;
}
}
else
{
// We've just been looking for the beginning of the line in the left subtree
Debug.Assert(state == ProjectionLineCalculationState.Prepend || state == ProjectionLineCalculationState.Bipend);
if (pendingInfo.lineNumber == leftInfo.lineNumber)
{
// the left node contained more of the line we are looking for
// we may or may not have found the start of the line, but there
// is no more to find at this level
pendingInfo.start = leftInfo.start;
pendingInfo.startComplete = leftInfo.startComplete;
}
else
{
// the left exposed source ended with a line break, so
// there is no change to the previously computed start
pendingInfo.startComplete = true;
}
if (state == ProjectionLineCalculationState.Bipend)
{
// now we need to look in the right subtree
state = ProjectionLineCalculationState.Append;
relativeExposedPosition = relativeExposedSpan.End;
continue;
}
else
{
// TraceExit(level, pendingInfo);
return pendingInfo;
}
}
#endregion
}
else if (relativeExposedPosition < relativeExposedSpan.End || this.right == null)
{
#region relative ExposedPosition is in current node
int absoluteSourcePosition = sourcePrefixSize + LeftTotalHiddenSize() + relativeExposedPosition;
ITextSnapshotLine sourceLine = sourceSnapshot.GetLineFromPosition(absoluteSourcePosition);
ProjectionLineCalculationState nextState = ProjectionLineCalculationState.Primary;
int provisionalLineNumber = sourceLine.LineNumber - (LeftTotalHiddenLineBreakCount() + hiddenPrefixLineBreakCount);
if (state == ProjectionLineCalculationState.Primary)
{
pendingInfo = new ProjectionLineInfo();
// the primary position we are searching for is in the current node.
// now we know the line number!
pendingInfo.lineNumber = provisionalLineNumber;
Debug.Assert(pendingInfo.lineNumber >= 0);
}
// compute the length of the portion of the source line that precedes the position we've been searching for
int sourceLeader = absoluteSourcePosition - sourceLine.Start;
// where would that map to in the current node?
int relativeExposedLineStart = relativeExposedPosition - sourceLeader;
if (state == ProjectionLineCalculationState.Prepend && provisionalLineNumber < pendingInfo.lineNumber)
{
// we were trying to pick up the beginning of a line that had been elided, but it was
// elided all the way to its beginning, so we are done.
pendingInfo.startComplete = true;
}
else if (state == ProjectionLineCalculationState.Primary || state == ProjectionLineCalculationState.Prepend)
{
// if we are lucky, the whole line will be contained in this node
if (relativeExposedLineStart > relativeExposedSpan.Start)
{
// we are sure that nothing to our left is of interest
pendingInfo.start = exposedPrefixSize + relativeExposedLineStart;
pendingInfo.startComplete = true;
}
else
{
// rats! part of the 'leader' is elided.
// start with the beginning of this segment
pendingInfo.start = exposedPrefixSize + relativeExposedSpan.Start;
pendingInfo.startComplete = false;
// and check further to the left if there is anything there
if (LeftTotalExposedSize() > 0)
{
nextState = ProjectionLineCalculationState.Prepend;
relativeExposedPosition = relativeExposedSpan.Start - 1;
}
}
}
if (state == ProjectionLineCalculationState.Primary || state == ProjectionLineCalculationState.Append)
{
int exposedLineEnd = relativeExposedLineStart + sourceLine.LengthIncludingLineBreak;
if (exposedLineEnd <= relativeExposedSpan.End)
{
// good!
pendingInfo.end = exposedLineEnd + exposedPrefixSize - sourceLine.LineBreakLength;
pendingInfo.endComplete = true;
pendingInfo.lineBreakLength = sourceLine.LineBreakLength;
}
else
{
pendingInfo.end = exposedPrefixSize + relativeExposedSpan.End;
pendingInfo.endComplete = false;
if (this.right == null)
{
if (nextState != ProjectionLineCalculationState.Prepend)
{
// we need to go further right but there is nothing more below us
// TraceExit(level, pendingInfo);
return pendingInfo;
}
}
else
{
if (nextState == ProjectionLineCalculationState.Prepend)
{
nextState = ProjectionLineCalculationState.Bipend;
}
else
{
nextState = ProjectionLineCalculationState.Append;
relativeExposedPosition = relativeExposedSpan.End;
}
}
}
}
if (nextState == ProjectionLineCalculationState.Primary)
{
// TraceExit(level, pendingInfo);
return pendingInfo;
}
state = nextState;
#endregion
}
else
{
#region relative ExposedPosition is in right subtree
Debug.Assert(state != ProjectionLineCalculationState.Bipend);
// recursively compute that part of the line that is in the right subtree
ProjectionLineInfo rightInfo = this.right.GetLineFromPosition
(sourceSnapshot : sourceSnapshot,
exposedPosition : relativeExposedPosition - (LeftTotalExposedSize() + this.exposedSize),
sourcePrefixLineBreakCount : sourcePrefixLineBreakCount + LeftTotalSourceLineBreakCount() + this.sourceLineBreakCount,
hiddenPrefixLineBreakCount : hiddenPrefixLineBreakCount + LeftTotalHiddenLineBreakCount() + (this.sourceLineBreakCount - this.exposedLineBreakCount),
sourcePrefixSize : sourcePrefixSize + LeftTotalSourceSize() + this.sourceSize,
exposedPrefixSize : exposedPrefixSize + LeftTotalExposedSize() + this.exposedSize,
level : level + 1);
if (state == ProjectionLineCalculationState.Primary)
{
if (rightInfo.startComplete)
{
// rightInfo.endComplete may be false, but we aren't going to find anything
// further to the right at this level of three, so we are done here
// TraceExit(level, rightInfo);
return rightInfo;
}
else
{
// the begnning of the line extends into the current node. our new position
// of interest is the end of the exposed text in this node.
state = ProjectionLineCalculationState.Prepend;
pendingInfo = rightInfo;
relativeExposedPosition = relativeExposedSpan.End - 1;
continue;
}
}
else
{
// We've just been looking for the end of the line in the right subtree
Debug.Assert(state == ProjectionLineCalculationState.Append);
// the first line we saw in the right subtree must have been the same line
// since there wasn't a line break at the end of the current node
Debug.Assert(pendingInfo.lineNumber == rightInfo.lineNumber);
pendingInfo.end = rightInfo.end;
pendingInfo.endComplete = rightInfo.endComplete;
pendingInfo.lineBreakLength = rightInfo.lineBreakLength;
// TraceExit(level, pendingInfo);
return pendingInfo;
}
#endregion
}
} while (relativeExposedPosition >= 0 && relativeExposedPosition <= this.totalExposedSize);
// TraceExit(level, pendingInfo);
return pendingInfo;
}