-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
Copy pathSourceWriter.cs
129 lines (111 loc) · 3.69 KB
/
SourceWriter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed class SourceWriter
{
private readonly StringBuilder _sb = new();
private int _indentationLevel;
public int Length => _sb.Length;
public int IndentationLevel => _indentationLevel;
private static readonly char[] s_newLine = Environment.NewLine.ToCharArray();
public void WriteBlockStart(string? declaration = null)
{
if (declaration is not null)
{
WriteLine(declaration);
}
WriteLine("{");
_indentationLevel++;
}
public void WriteBlockEnd(string? extra = null)
{
_indentationLevel--;
Debug.Assert(_indentationLevel > -1);
WriteLine($"}}{extra}");
}
public void WriteLine(string source)
{
_sb.Append(' ', 4 * _indentationLevel);
_sb.AppendLine(source);
}
public unsafe void WriteLine(ReadOnlySpan<char> source)
{
_sb.Append(' ', 4 * _indentationLevel);
fixed (char* ptr = source)
{
_sb.Append(ptr, source.Length);
WriteBlankLine();
}
}
public void WriteBlock(string source)
{
bool isFinalLine;
ReadOnlySpan<char> remainingText = source.AsSpan();
do
{
ReadOnlySpan<char> line = GetNextLine(ref remainingText, out isFinalLine);
switch (line)
{
case "{":
{
WriteBlockStart();
}
break;
case "}":
{
WriteBlockEnd();
}
break;
default:
{
WriteLine(line);
}
break;
}
} while (!isFinalLine);
}
public void WriteBlankLine() => _sb.AppendLine();
public void RemoveBlankLine()
{
int newLineLength = s_newLine.Length;
int lastNewLineStartIndex = Length - newLineLength;
_sb.Remove(lastNewLineStartIndex, newLineLength);
}
public SourceText ToSourceText()
{
Debug.Assert(_indentationLevel == 0 && _sb.Length > 0);
return SourceText.From(_sb.ToString(), Encoding.UTF8);
}
private static ReadOnlySpan<char> GetNextLine(ref ReadOnlySpan<char> remainingText, out bool isFinalLine)
{
if (remainingText.IsEmpty)
{
isFinalLine = true;
return default;
}
ReadOnlySpan<char> next;
ReadOnlySpan<char> rest;
remainingText = remainingText.Trim();
int lineLength = remainingText.IndexOf(s_newLine);
if (lineLength == -1)
{
lineLength = remainingText.Length;
isFinalLine = true;
rest = default;
}
else
{
rest = remainingText.Slice(lineLength + 1);
isFinalLine = false;
}
next = remainingText.Slice(0, lineLength);
remainingText = rest;
return next;
}
}
}