1010namespace SmartFormat . Core . Parsing ;
1111
1212/// <summary>
13- /// Handles escaped literals, like \\ or \n
13+ /// Handles escaped literals, like \\ \{ \n \u2022
1414/// </summary>
1515public static class EscapedLiteral
1616{
1717 private static readonly Dictionary < char , char > GeneralLookupTable = new ( ) {
18- // General
18+ // Escaping the escape character itself
1919 { '\\ ' , '\\ ' } ,
20+ // Placeholder related
2021 { '{' , '{' } ,
2122 { '}' , '}' } ,
23+ // escaped colons can be used anywhere in the format string
24+ { ':' , ':' }
25+ } ;
26+
27+ private static readonly Dictionary < char , char > CharLiteralLookupTable = new ( ) {
2228 { '0' , '\0 ' } ,
2329 { 'a' , '\a ' } ,
2430 { 'b' , '\b ' } ,
2531 { 'f' , '\f ' } ,
2632 { 'n' , '\n ' } ,
2733 { 'r' , '\r ' } ,
2834 { 't' , '\t ' } ,
29- { 'v' , '\v ' } ,
30- { ':' , ':' } // escaped colons can be used anywhere in the format string
35+ { 'v' , '\v ' }
3136 } ;
3237
3338 private static readonly Dictionary < char , char > FormatterOptionsLookupTable = new ( ) {
34- // Smart.Format characters used in formatter options
39+ // characters used in formatter options
3540 { '(' , '(' } ,
3641 { ')' , ')' }
3742 } ;
@@ -42,13 +47,13 @@ public static class EscapedLiteral
4247 /// <param name="input">The input character.</param>
4348 /// <param name="result">The matching character.</param>
4449 /// <param name="includeFormatterOptionChars">If <see langword="true"/>, (){}: will be escaped, else not.</param>
50+ /// <param name="includeCharacterLiterals">If <see langword="true"/>, \n, \t etc. will be escaped, else not.</param>
4551 /// <returns><see langword="true"/>, if a matching character was found.</returns>
46- public static bool TryGetChar ( char input , out char result , bool includeFormatterOptionChars )
52+ public static bool TryGetChar ( char input , out char result , bool includeFormatterOptionChars , bool includeCharacterLiterals = true )
4753 {
48- return includeFormatterOptionChars
49- ? GeneralLookupTable . TryGetValue ( input , out result ) ||
50- FormatterOptionsLookupTable . TryGetValue ( input , out result )
51- : GeneralLookupTable . TryGetValue ( input , out result ) ;
54+ return GeneralLookupTable . TryGetValue ( input , out result ) ||
55+ ( includeCharacterLiterals && CharLiteralLookupTable . TryGetValue ( input , out result ) ) ||
56+ ( includeFormatterOptionChars && FormatterOptionsLookupTable . TryGetValue ( input , out result ) ) ;
5257 }
5358
5459 private static char GetUnicode ( ReadOnlySpan < char > input , int startIndex )
@@ -74,9 +79,10 @@ private static char GetUnicode(ReadOnlySpan<char> input, int startIndex)
7479 /// <param name="escapingSequenceStart"></param>
7580 /// <param name="input"></param>
7681 /// <param name="includeFormatterOptionChars">If <see langword="true"/>, (){}: will be escaped, else not.</param>
82+ /// <param name="includeCharacterLiterals">If <see langword="true"/>, \n \t etc. will be escaped, else not.</param>
7783 /// <param name="resultBuffer">The buffer to fill. It's enough to have a buffer with the same size as the input length.</param>
7884 /// <returns>The input having escaped characters replaced with their real value.</returns>
79- public static ReadOnlySpan < char > UnEscapeCharLiterals ( char escapingSequenceStart , ReadOnlySpan < char > input , bool includeFormatterOptionChars , Span < char > resultBuffer )
85+ public static ReadOnlySpan < char > UnEscapeCharLiterals ( char escapingSequenceStart , ReadOnlySpan < char > input , bool includeFormatterOptionChars , bool includeCharacterLiterals , Span < char > resultBuffer )
8086 {
8187 var max = input . Length ;
8288 var resultIndex = 0 ;
@@ -104,7 +110,7 @@ public static ReadOnlySpan<char> UnEscapeCharLiterals(char escapingSequenceStart
104110 resultBuffer [ resultIndex ++ ] = GetUnicode ( input , nextInputIndex + 1 ) ;
105111 inputIndex += 6 ; // move to last unicode character
106112 }
107- else if ( TryGetChar ( input [ nextInputIndex ] , out var realChar , includeFormatterOptionChars ) )
113+ else if ( TryGetChar ( input [ nextInputIndex ] , out var realChar , includeFormatterOptionChars , includeCharacterLiterals ) )
108114 {
109115 resultBuffer [ resultIndex ++ ] = realChar ;
110116 inputIndex += 2 ;
@@ -132,8 +138,9 @@ public static ReadOnlySpan<char> UnEscapeCharLiterals(char escapingSequenceStart
132138 /// <param name="startIndex"></param>
133139 /// <param name="length"></param>
134140 /// <param name="includeFormatterOptionChars"><see langword="true"/>, if characters for formatter options should be included. Default is <see langword="false"/>.</param>
141+ /// <param name="includeCharLiterals"><see langword="true"/>, if character literals should be included. Default is <see langword="true"/>.</param>
135142 /// <returns>Returns the escaped characters.</returns>
136- public static IEnumerable < char > EscapeCharLiterals ( char escapeSequenceStart , string input , int startIndex , int length , bool includeFormatterOptionChars )
143+ public static IEnumerable < char > EscapeCharLiterals ( char escapeSequenceStart , string input , int startIndex , int length , bool includeFormatterOptionChars , bool includeCharLiterals = true )
137144 {
138145 var max = startIndex + length ;
139146 for ( var index = startIndex ; index < max ; index ++ )
@@ -146,6 +153,13 @@ public static IEnumerable<char> EscapeCharLiterals(char escapeSequenceStart, str
146153 continue ;
147154 }
148155
156+ if ( CharLiteralLookupTable . ContainsValue ( c ) )
157+ {
158+ yield return escapeSequenceStart ;
159+ yield return CharLiteralLookupTable . First ( kv => kv . Value == c ) . Key ;
160+ continue ;
161+ }
162+
149163 if ( includeFormatterOptionChars && FormatterOptionsLookupTable . ContainsValue ( c ) )
150164 {
151165 yield return escapeSequenceStart ;
0 commit comments