Skip to content

Commit c5cfd7b

Browse files
Apply the SourceWriter pattern to the STJ source generator. (#86526)
* Use the SourceWriter pattern in the STJ SG emitter. * Make helper method static. * Minimize the size of the generic helper method.
1 parent b380e2c commit c5cfd7b

8 files changed

+855
-837
lines changed

src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ internal sealed class KnownTypeSymbols(Compilation compilation)
6464
public readonly INamedTypeSymbol? VersionType = compilation!.GetBestTypeByMetadataName(typeof(Version));
6565

6666
// System.Text.Json types
67-
public readonly INamedTypeSymbol? JsonConverterOfTType = compilation!.GetBestTypeByMetadataName("System.Text.Json.Serialization.JsonConverter`1");
68-
public readonly INamedTypeSymbol? JsonConverterFactoryType = compilation!.GetBestTypeByMetadataName("System.Text.Json.Serialization.JsonConverterFactory");
69-
67+
public readonly INamedTypeSymbol? JsonConverterType = compilation!.GetBestTypeByMetadataName("System.Text.Json.Serialization.JsonConverter");
7068
public readonly INamedTypeSymbol? JsonSerializerContextType = compilation.GetBestTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerContext");
7169
public readonly INamedTypeSymbol? JsonSerializableAttributeType = compilation.GetBestTypeByMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute");
7270

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.CodeAnalysis.Text;
5+
using System.Diagnostics;
6+
7+
namespace System.Text.Json.SourceGeneration
8+
{
9+
internal sealed class SourceWriter
10+
{
11+
private readonly StringBuilder _sb = new();
12+
private int _indentation;
13+
14+
public SourceWriter()
15+
{
16+
IndentationChar = ' ';
17+
CharsPerIndentation = 4;
18+
}
19+
20+
public SourceWriter(char indentationChar, int charsPerIndentation)
21+
{
22+
if (!char.IsWhiteSpace(indentationChar))
23+
{
24+
throw new ArgumentOutOfRangeException(nameof(indentationChar));
25+
}
26+
27+
if (charsPerIndentation < 1)
28+
{
29+
throw new ArgumentOutOfRangeException(nameof(charsPerIndentation));
30+
}
31+
32+
IndentationChar = indentationChar;
33+
CharsPerIndentation = charsPerIndentation;
34+
}
35+
36+
public char IndentationChar { get; }
37+
public int CharsPerIndentation { get; }
38+
39+
public int Length => _sb.Length;
40+
public int Indentation
41+
{
42+
get => _indentation;
43+
set
44+
{
45+
if (value < 0)
46+
{
47+
Throw();
48+
static void Throw() => throw new ArgumentOutOfRangeException(nameof(value));
49+
}
50+
51+
_indentation = value;
52+
}
53+
}
54+
55+
public void WriteLine(char value)
56+
{
57+
AddIndentation();
58+
_sb.Append(value);
59+
_sb.AppendLine();
60+
}
61+
62+
public void WriteLine(string text)
63+
{
64+
if (_indentation == 0)
65+
{
66+
_sb.AppendLine(text);
67+
return;
68+
}
69+
70+
bool isFinalLine;
71+
ReadOnlySpan<char> remainingText = text.AsSpan();
72+
do
73+
{
74+
ReadOnlySpan<char> nextLine = GetNextLine(ref remainingText, out isFinalLine);
75+
76+
AddIndentation();
77+
AppendSpan(_sb, nextLine);
78+
_sb.AppendLine();
79+
}
80+
while (!isFinalLine);
81+
}
82+
83+
public void WriteLine() => _sb.AppendLine();
84+
85+
public SourceText ToSourceText()
86+
{
87+
Debug.Assert(_indentation == 0 && _sb.Length > 0);
88+
return SourceText.From(_sb.ToString(), Encoding.UTF8);
89+
}
90+
91+
private void AddIndentation()
92+
=> _sb.Append(IndentationChar, CharsPerIndentation * _indentation);
93+
94+
private static ReadOnlySpan<char> GetNextLine(ref ReadOnlySpan<char> remainingText, out bool isFinalLine)
95+
{
96+
if (remainingText.IsEmpty)
97+
{
98+
isFinalLine = true;
99+
return default;
100+
}
101+
102+
ReadOnlySpan<char> next;
103+
ReadOnlySpan<char> rest;
104+
105+
int lineLength = remainingText.IndexOf('\n');
106+
if (lineLength == -1)
107+
{
108+
lineLength = remainingText.Length;
109+
isFinalLine = true;
110+
rest = default;
111+
}
112+
else
113+
{
114+
rest = remainingText.Slice(lineLength + 1);
115+
isFinalLine = false;
116+
}
117+
118+
if ((uint)lineLength > 0 && remainingText[lineLength - 1] == '\r')
119+
{
120+
lineLength--;
121+
}
122+
123+
next = remainingText.Slice(0, lineLength);
124+
remainingText = rest;
125+
return next;
126+
}
127+
128+
private static unsafe void AppendSpan(StringBuilder builder, ReadOnlySpan<char> span)
129+
{
130+
fixed (char* ptr = span)
131+
{
132+
builder.Append(ptr, span.Length);
133+
}
134+
}
135+
}
136+
}

0 commit comments

Comments
 (0)