Skip to content

Commit e6fb3db

Browse files
committed
Clean up generator code a bit
1 parent beca492 commit e6fb3db

10 files changed

Lines changed: 1559 additions & 899 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace Laylua.Tests;
4+
5+
public sealed class GeneratorSourceFormattingTests
6+
{
7+
[Test]
8+
public void GeneratorSource_DoesNotUseAdjacentNonBlankWriterLineCalls()
9+
{
10+
// Arrange
11+
var repositoryRoot = FindRepositoryRoot();
12+
var toolsDirectory = Path.Combine(repositoryRoot, "tools");
13+
var offenders = new List<string>();
14+
15+
// Act
16+
foreach (var path in Directory.EnumerateFiles(toolsDirectory, "*.cs", SearchOption.AllDirectories))
17+
{
18+
var lines = File.ReadAllLines(path);
19+
for (var lineIndex = 0; lineIndex < lines.Length - 1; lineIndex++)
20+
{
21+
if (IsNonBlankWriterLineCall(lines[lineIndex]) && IsNonBlankWriterLineCall(lines[lineIndex + 1]))
22+
{
23+
offenders.Add($"{Path.GetRelativePath(repositoryRoot, path)}:{lineIndex + 1}");
24+
}
25+
}
26+
}
27+
28+
// Assert
29+
Assert.That(offenders, Is.Empty, string.Join(Environment.NewLine, offenders));
30+
}
31+
32+
[Test]
33+
public void GeneratorCodeSnippets_DoNotRelyOnExternalCloseBraceForSnippetOwnedBlocks()
34+
{
35+
// Arrange
36+
var repositoryRoot = FindRepositoryRoot();
37+
var toolsDirectory = Path.Combine(repositoryRoot, "tools");
38+
var snippetPattern = new Regex(
39+
"(?<receiver>\\w+)\\.Code\\(\\$*\"\"\"\\r?\\n(?<body>.*?)\\r?\\n\\s*\"\"\"\\);",
40+
RegexOptions.Singleline | RegexOptions.CultureInvariant);
41+
var offenders = new List<string>();
42+
43+
// Act
44+
foreach (var path in Directory.EnumerateFiles(toolsDirectory, "*.cs", SearchOption.AllDirectories))
45+
{
46+
var text = File.ReadAllText(path);
47+
foreach (Match match in snippetPattern.Matches(text))
48+
{
49+
if (CountStructuralBraceBalance(match.Groups["body"].Value) <= 0)
50+
{
51+
continue;
52+
}
53+
54+
var receiver = match.Groups["receiver"].Value;
55+
var remainingText = text[(match.Index + match.Length)..];
56+
var nextNonBlankLine = remainingText
57+
.Split(["\r\n", "\n", "\r"], StringSplitOptions.None)
58+
.Select(static line => line.Trim())
59+
.FirstOrDefault(static line => line.Length > 0);
60+
if (nextNonBlankLine == $"{receiver}.CloseBrace();")
61+
{
62+
var lineNumber = text[..match.Index].Count(static character => character == '\n') + 1;
63+
offenders.Add($"{Path.GetRelativePath(repositoryRoot, path)}:{lineNumber}");
64+
}
65+
}
66+
}
67+
68+
// Assert
69+
Assert.That(offenders, Is.Empty, string.Join(Environment.NewLine, offenders));
70+
}
71+
72+
[Test]
73+
public void GeneratorCodeSnippets_StandaloneClosingBraces_AreVisuallyDedented()
74+
{
75+
// Arrange
76+
var repositoryRoot = FindRepositoryRoot();
77+
var toolsDirectory = Path.Combine(repositoryRoot, "tools");
78+
var snippetPattern = new Regex(
79+
"(?<receiver>\\w+)\\.Code\\(\\$*\"\"\"\\r?\\n(?<body>.*?)\\r?\\n\\s*\"\"\"\\);",
80+
RegexOptions.Singleline | RegexOptions.CultureInvariant);
81+
var offenders = new List<string>();
82+
83+
// Act
84+
foreach (var path in Directory.EnumerateFiles(toolsDirectory, "*.cs", SearchOption.AllDirectories))
85+
{
86+
var text = File.ReadAllText(path);
87+
foreach (Match match in snippetPattern.Matches(text))
88+
{
89+
var bodyLines = match.Groups["body"].Value.Split(["\r\n", "\n", "\r"], StringSplitOptions.None);
90+
for (var lineIndex = 0; lineIndex < bodyLines.Length - 1; lineIndex++)
91+
{
92+
if (!IsStatementLikeLine(bodyLines[lineIndex]))
93+
{
94+
continue;
95+
}
96+
97+
if (bodyLines[lineIndex + 1].Trim() != "}")
98+
{
99+
continue;
100+
}
101+
102+
if (GetIndentation(bodyLines[lineIndex + 1]) >= GetIndentation(bodyLines[lineIndex]))
103+
{
104+
var fileLineNumber = text[..match.Index].Count(static character => character == '\n') + lineIndex + 2;
105+
offenders.Add($"{Path.GetRelativePath(repositoryRoot, path)}:{fileLineNumber}");
106+
}
107+
}
108+
}
109+
}
110+
111+
// Assert
112+
Assert.That(offenders, Is.Empty, string.Join(Environment.NewLine, offenders));
113+
}
114+
115+
private static string FindRepositoryRoot()
116+
{
117+
var directory = new DirectoryInfo(AppContext.BaseDirectory);
118+
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "Laylua.slnx")))
119+
{
120+
directory = directory.Parent;
121+
}
122+
123+
Assert.That(directory, Is.Not.Null, "Repository root could not be found from the test output directory.");
124+
125+
return directory!.FullName;
126+
}
127+
128+
private static bool IsNonBlankWriterLineCall(string line)
129+
{
130+
var trimmedLine = line.Trim();
131+
var dotIndex = trimmedLine.IndexOf('.');
132+
if (dotIndex <= 0 || !trimmedLine.EndsWith(");"))
133+
{
134+
return false;
135+
}
136+
137+
return trimmedLine[(dotIndex + 1)..].StartsWith("Line(")
138+
&& trimmedLine[(dotIndex + 1)..] != "Line();";
139+
}
140+
141+
private static int CountStructuralBraceBalance(string text)
142+
{
143+
var balance = 0;
144+
foreach (var character in text)
145+
{
146+
if (character == '{')
147+
{
148+
balance++;
149+
}
150+
else if (character == '}')
151+
{
152+
balance--;
153+
}
154+
}
155+
156+
return balance;
157+
}
158+
159+
private static bool IsStatementLikeLine(string line)
160+
{
161+
var trimmedLine = line.Trim();
162+
if (trimmedLine.Length == 0)
163+
{
164+
return false;
165+
}
166+
167+
return trimmedLine is not "{" and not "}" and not "else" and not "catch" and not "finally";
168+
}
169+
170+
private static int GetIndentation(string line)
171+
{
172+
return line.Length - line.TrimStart().Length;
173+
}
174+
}

0 commit comments

Comments
 (0)