зеркало из https://github.com/microsoft/DbgShell.git
Add CaStringUtil.IndentAndWrap swiss army knife function.
(not currently used anywhere)
This commit is contained in:
Родитель
f651cf18a2
Коммит
aefdc8a9b9
|
@ -626,6 +626,334 @@ namespace MS.Dbg
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum IndentAndWrapOptions
|
||||||
|
{
|
||||||
|
Default = 0,
|
||||||
|
NoWordBreaking = 0x01, // No characters (incl. spaces) are replaced with newlines
|
||||||
|
TruncateInsteadOfWrap = 0x02,
|
||||||
|
FirstLineAlreadyIndented = 0x04,
|
||||||
|
DoNotIndentContinuationLines = 0x08, // Could also be named DoNotIndentWrapLines. Takes
|
||||||
|
// precedence over AddLineLeadingSpaceToAddtlContinuationIndent.
|
||||||
|
AddLineLeadingSpaceToAddtlContinuationIndent = 0x10, // TODO: could this result in a pathological situation if
|
||||||
|
// leading space is longer than the entire outputWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string c_PushAndReset = "\u009b56;0m";
|
||||||
|
private const string c_StandalonePop = "\u009b57m";
|
||||||
|
|
||||||
|
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( (char) 0x2026 ); // 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()
|
||||||
|
|
||||||
|
|
||||||
// TODO: theme support
|
// TODO: theme support
|
||||||
|
@ -800,6 +1128,60 @@ namespace MS.Dbg
|
||||||
} // end class CaStringUtilStripTestCase
|
} // end class CaStringUtilStripTestCase
|
||||||
|
|
||||||
|
|
||||||
|
private class CaStringUtilIndentAndWrapTestCase
|
||||||
|
{
|
||||||
|
public readonly string Input;
|
||||||
|
public readonly int OutputWidth;
|
||||||
|
public readonly IndentAndWrapOptions Options;
|
||||||
|
public readonly int Indent;
|
||||||
|
public readonly int AddtlContinuationIndent;
|
||||||
|
|
||||||
|
public readonly String ExpectedOutput;
|
||||||
|
public readonly Type ExpectedExceptionType;
|
||||||
|
|
||||||
|
private CaStringUtilIndentAndWrapTestCase( string input,
|
||||||
|
int outputWidth )
|
||||||
|
{
|
||||||
|
Input = input;
|
||||||
|
OutputWidth = outputWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CaStringUtilIndentAndWrapTestCase( string input,
|
||||||
|
int outputWidth,
|
||||||
|
string expectedOutput )
|
||||||
|
: this( input, outputWidth )
|
||||||
|
{
|
||||||
|
ExpectedOutput = expectedOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CaStringUtilIndentAndWrapTestCase( string input,
|
||||||
|
int outputWidth,
|
||||||
|
Type expectedExceptionType )
|
||||||
|
: this( input, outputWidth )
|
||||||
|
{
|
||||||
|
ExpectedExceptionType = expectedExceptionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public static string IndentAndWrap( string str,
|
||||||
|
int outputWidth,
|
||||||
|
IndentAndWrapOptions options,
|
||||||
|
int indent,
|
||||||
|
int addtlContinuationIndent ) */
|
||||||
|
|
||||||
|
public CaStringUtilIndentAndWrapTestCase( string input,
|
||||||
|
int outputWidth,
|
||||||
|
IndentAndWrapOptions options,
|
||||||
|
int indent,
|
||||||
|
int addtlContinuationIndent,
|
||||||
|
string expectedOutput )
|
||||||
|
: this( input, outputWidth, expectedOutput )
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
Indent = indent;
|
||||||
|
AddtlContinuationIndent = addtlContinuationIndent;
|
||||||
|
}
|
||||||
|
} // end class CaStringUtilIndentAndWrapTestCase
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static List< CaStringUtilLengthTestCase > sm_lengthTests = new List< CaStringUtilLengthTestCase >()
|
private static List< CaStringUtilLengthTestCase > sm_lengthTests = new List< CaStringUtilLengthTestCase >()
|
||||||
|
@ -1255,6 +1637,140 @@ namespace MS.Dbg
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private static List<CaStringUtilIndentAndWrapTestCase> sm_indentAndWrapTests = new List<CaStringUtilIndentAndWrapTestCase>()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( null,
|
||||||
|
outputWidth: 4,
|
||||||
|
typeof( ArgumentNullException ) ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: "" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: "1 2\n3 4\n5 6\n7 8\n9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( " 1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: " 1\n2 3\n4 5\n6 7\n8 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "12 34 56 78 90",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: "12\n34\n56\n78\n90" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( " 12 34 56 78 90",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: " 12\n34\n56\n78\n90" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "123 456 789 0ab",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: "123\n456\n789\n0ab" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1234 5678 90ab",
|
||||||
|
outputWidth: 4,
|
||||||
|
expectedOutput: "123\n4\n567\n8\n90a\nb" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( " 1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.AddLineLeadingSpaceToAddtlContinuationIndent,
|
||||||
|
indent: 1,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.Default,
|
||||||
|
indent: 2,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( " 1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.Default,
|
||||||
|
indent: 2,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " \n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( " 1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.NoWordBreaking,
|
||||||
|
indent: 2,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n 8\n \n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2 3 4 5 6 7 8 9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.FirstLineAlreadyIndented,
|
||||||
|
indent: 2,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: "1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2\n3 4\n5 6\n7 8\n9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.Default,
|
||||||
|
indent: 0,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: "1 2\n3 4\n5 6\n7 8\n9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2\n3 4\n5 6\n7 8\n9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.Default,
|
||||||
|
indent: 1,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2\n3 4\n5 6\n7 8\n9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.DoNotIndentContinuationLines,
|
||||||
|
indent: 1,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " 1\n2\n 3\n4\n 5\n6\n 7\n8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( " 1 2\n3 4\n5 6\n7 8\n9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.DoNotIndentContinuationLines |
|
||||||
|
IndentAndWrapOptions.AddLineLeadingSpaceToAddtlContinuationIndent,
|
||||||
|
indent: 1,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " 1\n2\n 3\n4\n 5\n6\n 7\n8\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2\n3 4\n5 6\n7 8\n9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.TruncateInsteadOfWrap,
|
||||||
|
indent: 1,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: " 1…\n 3…\n 5…\n 7…\n 9" ),
|
||||||
|
|
||||||
|
new CaStringUtilIndentAndWrapTestCase( "1 2\n\u009b91m3 4\n5 6\n7 8\u009bm\n9",
|
||||||
|
outputWidth: 4,
|
||||||
|
options: IndentAndWrapOptions.TruncateInsteadOfWrap,
|
||||||
|
indent: 1,
|
||||||
|
addtlContinuationIndent: 0,
|
||||||
|
expectedOutput: $" 1…\n \u009b91m3{c_PushAndReset}…\n {c_StandalonePop}5{c_PushAndReset}…\n {c_StandalonePop}7\u009bm{c_PushAndReset}…\n {c_StandalonePop}9" ),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private static string _EscapeStringForDisplay( string s )
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder( s.Length * 2 );
|
||||||
|
|
||||||
|
foreach( char c in s )
|
||||||
|
{
|
||||||
|
if( c == CSI )
|
||||||
|
sb.Append( @"\" ).Append( "u009b" );
|
||||||
|
else if( c == '\r' )
|
||||||
|
sb.Append( @"\" ).Append( 'r' );
|
||||||
|
else if( c == '\n' )
|
||||||
|
sb.Append( @"\" ).Append( 'n' );
|
||||||
|
else
|
||||||
|
sb.Append( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void SelfTest()
|
public static void SelfTest()
|
||||||
|
@ -1422,6 +1938,7 @@ namespace MS.Dbg
|
||||||
}
|
}
|
||||||
} // end foreach( testCase )
|
} // end foreach( testCase )
|
||||||
|
|
||||||
|
|
||||||
if( 0 == stripFailures )
|
if( 0 == stripFailures )
|
||||||
{
|
{
|
||||||
Console.WriteLine( "CaStringUtil StripControlSequences tests passed." );
|
Console.WriteLine( "CaStringUtil StripControlSequences tests passed." );
|
||||||
|
@ -1433,6 +1950,55 @@ namespace MS.Dbg
|
||||||
|
|
||||||
failures += stripFailures;
|
failures += stripFailures;
|
||||||
|
|
||||||
|
|
||||||
|
int indentAndWrapFailures = 0;
|
||||||
|
for( int i = 0; i < sm_indentAndWrapTests.Count; i++ )
|
||||||
|
{
|
||||||
|
var testCase = sm_indentAndWrapTests[ i ];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string output = IndentAndWrap( testCase.Input,
|
||||||
|
testCase.OutputWidth,
|
||||||
|
testCase.Options,
|
||||||
|
testCase.Indent,
|
||||||
|
testCase.AddtlContinuationIndent );
|
||||||
|
|
||||||
|
if( 0 != String.CompareOrdinal( output, testCase.ExpectedOutput ) )
|
||||||
|
{
|
||||||
|
indentAndWrapFailures++;
|
||||||
|
Console.WriteLine( "indentAndWrap test case {0} failed.\n Expected: {1}\n Actual: {2}\n",
|
||||||
|
i,
|
||||||
|
_EscapeStringForDisplay( testCase.ExpectedOutput ),
|
||||||
|
_EscapeStringForDisplay( output ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
|
if( e.GetType() != testCase.ExpectedExceptionType )
|
||||||
|
{
|
||||||
|
indentAndWrapFailures++;
|
||||||
|
Console.WriteLine( "IndentAndWrap Test case {0} failed. Expected exception type: {1}, Actual: {2}.",
|
||||||
|
i,
|
||||||
|
null == testCase.ExpectedExceptionType ?
|
||||||
|
"<none>" :
|
||||||
|
testCase.ExpectedExceptionType.Name,
|
||||||
|
e.GetType().Name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end foreach( testCase )
|
||||||
|
|
||||||
|
if( 0 == indentAndWrapFailures )
|
||||||
|
{
|
||||||
|
Console.WriteLine( "CaStringUtil IndentAndWrap tests passed." );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine( "CaStringUtil IndentAndWrap tests failed ({0} failures).", indentAndWrapFailures );
|
||||||
|
}
|
||||||
|
|
||||||
|
failures += indentAndWrapFailures;
|
||||||
|
|
||||||
|
|
||||||
if( 0 == failures )
|
if( 0 == failures )
|
||||||
{
|
{
|
||||||
Console.WriteLine( "All CaStringUtil tests passed." );
|
Console.WriteLine( "All CaStringUtil tests passed." );
|
||||||
|
|
Загрузка…
Ссылка в новой задаче