public ProjectionLineInfo GetLineFromPosition()

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;
        }