@@ -4,6 +4,115 @@ namespace System.Text.Control;
44
55public sealed class ControlBuilder
66{
7+ [ EditorBrowsable ( EditorBrowsableState . Never ) ]
8+ [ InterpolatedStringHandler ]
9+ public readonly ref struct PrintInterpolatedStringHandler
10+ {
11+ const int StackBufferSize = 256 ;
12+
13+ readonly ControlBuilder _builder ;
14+
15+ readonly IFormatProvider ? _provider ;
16+
17+ readonly ICustomFormatter ? _formatter ;
18+
19+ public PrintInterpolatedStringHandler (
20+ int literalLength ,
21+ int formattedCount ,
22+ ControlBuilder builder ,
23+ IFormatProvider ? provider = null )
24+ {
25+ _builder = builder ;
26+ _provider = provider ;
27+ _formatter = provider is not CultureInfo ?
28+ ( ICustomFormatter ? ) provider ? . GetFormat ( typeof ( ICustomFormatter ) ) : null ;
29+ }
30+
31+ void AppendSpan ( ReadOnlySpan < char > span )
32+ {
33+ _ = _builder . Print ( span ) ;
34+ }
35+
36+ public void AppendLiteral ( string value )
37+ {
38+ AppendSpan ( value ) ;
39+ }
40+
41+ [ SuppressMessage ( "Style" , "IDE0038" ) ]
42+ public void AppendFormatted < T > ( T value , string ? format = null )
43+ {
44+ if ( _formatter != null )
45+ {
46+ AppendSpan ( _formatter . Format ( format , value , _provider ) ) ;
47+
48+ return ;
49+ }
50+
51+ // Do not use pattern matching here as it results in boxing.
52+ if ( value is IFormattable )
53+ {
54+ if ( value is ISpanFormattable )
55+ {
56+ char [ ] ? rented = null ;
57+ Span < char > span = stackalloc char [ StackBufferSize ] ;
58+
59+ try
60+ {
61+ int written ;
62+
63+ // Try to format the value on the stack; fall back to the heap.
64+ while ( ! ( ( ISpanFormattable ) value ) . TryFormat ( span , out written , format , _provider ) )
65+ {
66+ if ( rented != null )
67+ ArrayPool < char > . Shared . Return ( rented ) ;
68+
69+ var len = span . Length * 2 ;
70+
71+ rented = ArrayPool < char > . Shared . Rent ( len ) ;
72+ span = rented . AsSpan ( 0 , len ) ;
73+ }
74+
75+ AppendSpan ( span [ ..written ] ) ;
76+ }
77+ finally
78+ {
79+ if ( rented != null )
80+ ArrayPool < char > . Shared . Return ( rented ) ;
81+ }
82+ }
83+ else
84+ AppendSpan ( ( ( IFormattable ) value ) . ToString ( format , _provider ) ) ;
85+ }
86+ else
87+ AppendSpan ( value ? . ToString ( ) ) ;
88+ }
89+
90+ public void AppendFormatted ( object ? value , string ? format = null )
91+ {
92+ // This overload is used when a target-typed expression cannot use the generic overload.
93+ AppendFormatted < object ? > ( value , format ) ;
94+ }
95+
96+ public void AppendFormatted ( string ? value )
97+ {
98+ // This overload exists to disambiguate string since it can implicitly convert to both object and
99+ // ReadOnlySpan<char>.
100+ AppendFormatted < string ? > ( value ) ;
101+ }
102+
103+ public unsafe void AppendFormatted ( void * value , string ? format = null )
104+ {
105+ // This overload makes pointer values work in interpolation holes; they cannot be passed as generic type
106+ // arguments currently.
107+ AppendFormatted ( ( nuint ) value , format ) ;
108+ }
109+
110+ public void AppendFormatted ( ReadOnlySpan < char > value )
111+ {
112+ AppendSpan ( value ) ;
113+ }
114+ }
115+
7116 const int StackBufferSize = 32 ;
8117
9118 public ReadOnlySpan < char > Span => _writer . WrittenSpan ;
@@ -47,14 +156,38 @@ public ControlBuilder Print<T>(T value)
47156 return Print ( ( value ? . ToString ( ) ) . AsSpan ( ) ) ;
48157 }
49158
159+ public ControlBuilder Print ( [ InterpolatedStringHandlerArgument ( "" ) ] ref PrintInterpolatedStringHandler handler )
160+ {
161+ return this ;
162+ }
163+
164+ public ControlBuilder Print (
165+ IFormatProvider ? provider ,
166+ [ InterpolatedStringHandlerArgument ( "" , "provider" ) ] ref PrintInterpolatedStringHandler handler )
167+ {
168+ return this ;
169+ }
170+
50171 public ControlBuilder PrintLine ( )
51172 {
52- return PrintLine ( string . Empty ) ;
173+ return Print ( Environment . NewLine ) ;
53174 }
54175
55176 public ControlBuilder PrintLine < T > ( T value )
56177 {
57- return Print ( value ? . ToString ( ) + Environment . NewLine ) ;
178+ return Print ( value ) . PrintLine ( ) ;
179+ }
180+
181+ public ControlBuilder PrintLine ( [ InterpolatedStringHandlerArgument ( "" ) ] ref PrintInterpolatedStringHandler handler )
182+ {
183+ return PrintLine ( ) ;
184+ }
185+
186+ public ControlBuilder PrintLine (
187+ IFormatProvider ? provider ,
188+ [ InterpolatedStringHandlerArgument ( "" , "provider" ) ] ref PrintInterpolatedStringHandler handler )
189+ {
190+ return PrintLine ( ) ;
58191 }
59192
60193 // Keep methods in sync with the ControlSequences class.
0 commit comments