public static string IndentAndWrap()

in DbgProvider/internal/CaStringUtil.cs [735:1046]


        public static string IndentAndWrap( string str,
                                            int outputWidth,
                                            IndentAndWrapOptions options,
                                            int indent,
                                            int addtlContinuationIndent )
        {
            bool truncate = 0 != (options & IndentAndWrapOptions.TruncateInsteadOfWrap);

            int minContent = 2; // 1 char of content, and a newline char

            if( truncate )
            {
                minContent = 3; // 1 char of content, 1 ellipsis char, and a newline char

                if( addtlContinuationIndent != 0 )
                {
                    throw new ArgumentException( "The combination of TruncateInsteadOfWrap and non-zero addtlContinuationIndent makes no sense." );
                }
            }

            if( (indent + addtlContinuationIndent) > (outputWidth - minContent) )
            {
                throw new ArgumentOutOfRangeException(
                    Util.Sprintf( "The outputWidth ({0}) should be somewhat larger than " +
                                      "the max possible indent ({1} + {2}).",
                                  outputWidth,
                                  indent,
                                  addtlContinuationIndent ),
                    innerException: null );
            }

            if( null == str )
                throw new ArgumentNullException( nameof( str ) );

            StringBuilder sb = new StringBuilder( str.Length * 2 ); // just an estimate

            int spaceToUse = outputWidth - indent - 1; // "- 1" because the newline actually uses a spot.

            int srcIdx = 0;

            int lastSpaceOrTabSrcIdx = -1;
            int lastSpaceOrTabDstIdx = -1;

            // We may need to replace the last char of content (space or not) when
            // truncating (to put an ellipsis in its place).
            int lastContentDstIdx = -1;

            bool inLeadingSpace = true;
            int leadingSpaceLen = 0;

            // So that we don't need to inserts push/pops for plain text.
            bool haveSeenControlSequence = false;

            bool pushInserted = false;

            void _insertIndent( int numSpaces )
            {
                sb.Append( ' ', numSpaces );
            }

            void _rememberLastSpaceOrTabIndexes()
            {
                lastSpaceOrTabSrcIdx = srcIdx;
                lastSpaceOrTabDstIdx = sb.Length;
            }

            void _rememberLastContentIndex()
            {
                lastContentDstIdx = sb.Length;
            }

            bool _weHaveConsumedAllAvailableOutputWidth()
            {
                return spaceToUse == 0;
            }

            bool _backtrackToLastSpaceOrTab()
            {
                bool backtrackingAllowed = 0 == (options & IndentAndWrapOptions.NoWordBreaking);

                if( backtrackingAllowed &&
                    (lastSpaceOrTabSrcIdx > 0) ) // can't be 0, because we don't count leading space
                {
                    Util.Assert( lastSpaceOrTabDstIdx >= 0 );
                    srcIdx = lastSpaceOrTabSrcIdx;
                    sb.Length = lastSpaceOrTabDstIdx;

                    return true;
                }

                return false;
            }

            bool _stillInBounds()
            {
                return srcIdx < str.Length;
            }

            // Returns true if srcIdx is still within the bounds of str.
            bool _consumeControlSequences()
            {
                while( _stillInBounds() && (str[ srcIdx ] == CSI) )
                {
                    haveSeenControlSequence = true;
                    int pastSeq = _SkipControlSequence( str, srcIdx ); // TODO: make _SkipControlSequence not assert if first char isn't CSI?
                    sb.Append( str, srcIdx, pastSeq - srcIdx );
                    srcIdx = pastSeq;
                }
                return _stillInBounds();
            }

            void _saveAndResetSgrState()
            {
                if( haveSeenControlSequence && !pushInserted )
                {
                    sb.Append( c_PushAndReset );
                    pushInserted = true;
                }
            }

            void _restoreSgrState()
            {
                if( haveSeenControlSequence )
                {
                    Util.Assert( pushInserted );

                    sb.Append( c_StandalonePop );
                    pushInserted = false;
                }
            }

            void _completeLineAndIndent( int numSpaces, bool isWrap )
            {
                // Save the current SGR (color) state and (temporarily) reset to default,
                // if necessary.
                _saveAndResetSgrState();

                sb.Append( '\n' );
                _insertIndent( numSpaces );

                // Restore the SGR state.
                _restoreSgrState();

                // Need to reset various counters/state:

                spaceToUse = outputWidth - numSpaces - 1; // "- 1" because the newline actually uses a spot.

                Util.Assert( !pushInserted );

                lastSpaceOrTabSrcIdx = -1;
                lastSpaceOrTabDstIdx = -1;

                // This should not be needed... but I'm doing it defensively.
                lastContentDstIdx = -1;

                if( !isWrap )
                {
                    // We're actually on the next line of input.
                    inLeadingSpace = true;
                    leadingSpaceLen = 0;
                }
            }

            void _appendContentChar( char c )
            {
                _rememberLastContentIndex();

                if( Char.IsWhiteSpace( c ) )
                {
                    if( !inLeadingSpace )
                    {
                        _rememberLastSpaceOrTabIndexes();
                    }
                    else
                    {
                        leadingSpaceLen++;
                    }
                }
                else
                {
                    inLeadingSpace = false;
                }

                sb.Append( c );
                spaceToUse--;
            }

            // Returns 'true' if we actually found a newline; false if we hit the end of
            // the string first. Will preserve control sequences.
            bool _seekToNextLine()
            {
                while( _consumeControlSequences() )
                {
                    char c = str[ srcIdx ];

                    if( c == '\r' )
                    {
                        // ignore it
                    }
                    else if( c == '\n' )
                    {
                        return true;
                    }

                    srcIdx++;
                }
                return false;
            }

            if( (options & IndentAndWrapOptions.FirstLineAlreadyIndented) == 0 )
            {
                _insertIndent( indent );
            }

            while( _consumeControlSequences() )
            {
                // We consume control sequences in the "while" condition, so the
                // characters we consider here are purely content.

                char c = str[ srcIdx ];

                if( c == '\r' )
                {
                    // ignore it
                }
                else if( c == '\n' )
                {
                    // We consider the case of a newline char before the
                    // _weHaveConsumedAllAvailableOutputWidth case, because the newline
                    // does not consume available space (but rather resets it).

                    _completeLineAndIndent( indent, isWrap: false );
                }
                else if( _weHaveConsumedAllAvailableOutputWidth() )
                {
                    // Need to decide where to put a newline... was there a space where we
                    // could break up the line, or do we have to just chop right where we
                    // are?

                    int totalIndent = indent;
                    bool backtracked = false;

                    if( Char.IsWhiteSpace( c ) )
                    {
                        // So we can "backtrack" to it.
                        _rememberLastSpaceOrTabIndexes();
                    }

                    bool moreContentAfterTruncation = false;

                    if( truncate )
                    {
                        if( lastContentDstIdx == -1 )
                            throw new Exception( "unexpected" );

                        // Note that we put the ellipsis /after/ resetting SGR (color)
                        // state. This is a stylistic choice.

                        sb.Remove( lastContentDstIdx, 1 ); // make way for the ellipsis.

                        moreContentAfterTruncation = _seekToNextLine();

                        _saveAndResetSgrState();

                        sb.Append( c_ellipsis );
                    }
                    else
                    {
                        bool doNotIndentContinuation = 0 != (options & IndentAndWrapOptions.DoNotIndentContinuationLines);

                        if( doNotIndentContinuation )
                            totalIndent = 0;
                        else
                            totalIndent += addtlContinuationIndent;

                        // Could be a backtrack of 0 chars if the current char is a space.
                        backtracked = _backtrackToLastSpaceOrTab();

                        if( !doNotIndentContinuation &&
                            (options & IndentAndWrapOptions.AddLineLeadingSpaceToAddtlContinuationIndent) != 0 )
                        {
                            totalIndent += leadingSpaceLen;
                        }
                    }

                    if( !truncate || moreContentAfterTruncation )
                    {
                        _completeLineAndIndent( totalIndent, isWrap: !truncate );
                    }
                    else
                    {
                        // Don't forget the last POP.
                        _restoreSgrState();
                    }

                    if( !truncate && !backtracked )
                    {
                        // If we didn't backtrack, then we haven't yet accounted for the
                        // current character--don't lose it!
                        _appendContentChar( c );
                    }
                }
                else
                {
                    _appendContentChar( c );
                }

                srcIdx++;
            } // end while( still more str )

            return sb.ToString();
        } // end _WordWrap()