Skip to content

Commit e3b7211

Browse files
committed
Add CsharpCode code generation utilities
Introduces the CsharpCode class and related options for programmatic C# code generation, including support for namespaces, types, and using directives. Updates tests to use the new CsharpCode API. Refactors CodeBuilder to make CodeBlock methods public for broader utility.
1 parent 57a5318 commit e3b7211

5 files changed

Lines changed: 183 additions & 47 deletions

File tree

EasyCodeBuilder.Test/CsharpCodeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public void Test()
9090
//
9191
// #endregion
9292
//
93-
// #region Basic Extension Methods Tests
93+
// #region Basic CsharpCode Methods Tests
9494
//
9595
// [Fact]
9696
// public void AppendWhen_ConditionTrue_AppendsLine()

EasyCodeBuilder.Test/TestRecord.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,45 @@
11
using System;
2+
using Fengb3.EasyCodeBuilder.Csharp;
3+
using static Fengb3.EasyCodeBuilder.Csharp.CsharpCode;
24
using Xunit.Abstractions;
35

46
namespace EasyCodeBuilder.Test;
57

6-
public class TestRecord
8+
public class TestRecord(ITestOutputHelper testOutputHelper)
79
{
8-
private readonly ITestOutputHelper _testOutputHelper;
9-
public TestRecord(ITestOutputHelper testOutputHelper)
10-
{
11-
_testOutputHelper = testOutputHelper;
12-
}
13-
14-
private record Something(string name)
10+
[Fact]
11+
public void TestNamespace()
1512
{
16-
public string Name { get; set; } = name;
13+
var op = Create()
14+
.Using("System")
15+
.Using("System.Linq", "System.Collections.Generic")
16+
.Namespace(@namespace => {
17+
@namespace.Name = "MyNamespace";
18+
})
19+
.Build();
20+
21+
testOutputHelper.WriteLine(op);
1722
}
1823

1924
[Fact]
20-
public void Test()
25+
public void TestType()
2126
{
22-
void Action(Something something)
23-
{
24-
something.Name = "hello";
25-
}
26-
27-
var something = new Something("world");
28-
Action(something);
29-
30-
_testOutputHelper.WriteLine(something.Name);
27+
var op = Create()
28+
.Namespace(@namespace => {
29+
@namespace.Name = "MyNamespace";
30+
@namespace.Type(type => {
31+
type.TypeKind = TypeOption.Type.Record;
32+
type.Keywords.Add("public");
33+
type.OnBegin += cb => cb.AppendLine("MyRecord(int Id, string Name);");
34+
});
35+
@namespace.Class(cls => {
36+
cls.Name = "MyClass";
37+
cls.Keywords.Add("public");
38+
cls.OnBegin += cb => cb.AppendLine("// Class body");
39+
});
40+
})
41+
.Build();
42+
43+
testOutputHelper.WriteLine(op);
3144
}
3245
}

EasyCodeBuilder/CSharpCodeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
//
5656
// #endregion
5757
//
58-
// #region Basic Extension Methods
58+
// #region Basic CsharpCode Methods
5959
//
6060
// /// <summary>
6161
// /// Conditionally adds a line of code

EasyCodeBuilder/CodeBuilder.cs

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -247,33 +247,10 @@ private void AppendSingleLine(string line)
247247
}
248248
}
249249

250-
// /// <summary>
251-
// /// 添加代码块(使用配置的代码块符号)
252-
// /// </summary>
253-
// public T CodeBlock(Func<T, T> func)
254-
// {
255-
// // 将调用重定向到带前缀的版本,prefix 传递 null
256-
// return CodeBlock(func, null);
257-
// }
258-
//
259-
// /// <summary>
260-
// ///
261-
// /// </summary>
262-
// /// <param name="action"></param>
263-
// /// <returns></returns>
264-
// public T CodeBlock(Action<T> action)
265-
// {
266-
// return CodeBlock((cb) =>
267-
// {
268-
// action(cb);
269-
// return cb;
270-
// }, null);
271-
// }
272-
273250
/// <summary>
274251
/// 添加代码块
275252
/// </summary>
276-
protected CodeBuilder CodeBlock(Func<CodeBuilder, CodeBuilder> action, string? prefix = null)
253+
public CodeBuilder CodeBlock(Func<CodeBuilder, CodeBuilder> func, string? prefix = null)
277254
{
278255
string header;
279256
if (prefix is null)
@@ -294,7 +271,7 @@ protected CodeBuilder CodeBlock(Func<CodeBuilder, CodeBuilder> action, string? p
294271

295272
using (Indent)
296273
{
297-
action(this);
274+
func(this);
298275
}
299276

300277
if (!string.IsNullOrEmpty(_blockEnd))
@@ -308,7 +285,7 @@ protected CodeBuilder CodeBlock(Func<CodeBuilder, CodeBuilder> action, string? p
308285
/// <summary>
309286
/// 添加代码块
310287
/// </summary>
311-
protected CodeBuilder CodeBlock(Action<CodeBuilder> action, string? prefix = null)
288+
public CodeBuilder CodeBlock(Action<CodeBuilder> action, string? prefix = null)
312289
{
313290
return CodeBlock(
314291
cb =>
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Fengb3.EasyCodeBuilder.Csharp;
6+
7+
/// <summary>
8+
///
9+
/// </summary>
10+
public delegate CodeBuilder CodeRenderFragment(CodeBuilder builder);
11+
12+
public class CodeOption
13+
{
14+
public CodeRenderFragment? OnBegin;
15+
public CodeRenderFragment? OnEnd;
16+
public CodeRenderFragment? OnChildren;
17+
}
18+
19+
public class NamespaceOption : CodeOption
20+
{
21+
public string Name { get; set; } = "";
22+
23+
public NamespaceOption()
24+
{
25+
IDisposable? indenter = null;
26+
OnBegin += cb => {
27+
cb.AppendLines($"namespace {Name}", "{");
28+
indenter = cb.Indent;
29+
return cb;
30+
};
31+
32+
OnEnd += cb => {
33+
indenter?.Dispose();
34+
cb.AppendLine("}");
35+
return cb;
36+
};
37+
}
38+
}
39+
40+
public class TypeOption : CodeOption
41+
{
42+
public enum Type
43+
{
44+
Class,
45+
Struct,
46+
Interface,
47+
Enum,
48+
Record
49+
}
50+
51+
public Type TypeKind { get; set; } = Type.Class;
52+
53+
public ICollection<string> Keywords { get; set; } = [];
54+
55+
public string Name { get; set; }
56+
57+
public TypeOption()
58+
{
59+
var indenterStack = new Stack<IDisposable>();
60+
OnBegin += cb => {
61+
var keywords = string.Join(" ", Keywords);
62+
var typeKeyword = TypeKind.ToString().ToLower();
63+
cb.AppendLines($"{keywords} {typeKeyword} {Name}", "{");
64+
indenterStack.Push(cb.Indent);
65+
return cb;
66+
};
67+
68+
OnEnd += cb => {
69+
if (indenterStack.Count > 0)
70+
{
71+
indenterStack.Pop().Dispose();
72+
}
73+
cb.AppendLine("}");
74+
return cb;
75+
};
76+
}
77+
}
78+
79+
/// <summary>
80+
///
81+
/// </summary>
82+
public static class CsharpCode
83+
{
84+
/// <summary>
85+
///
86+
/// </summary>
87+
/// <returns></returns>
88+
public static CodeOption Create()
89+
{
90+
return new();
91+
}
92+
93+
public static CodeOption Using(this CodeOption option, params string[] usings)
94+
{
95+
option.OnBegin += cb => {
96+
foreach (var u in usings)
97+
{
98+
cb.AppendLine($"using {u};");
99+
}
100+
cb.AppendLine();
101+
return cb;
102+
};
103+
return option;
104+
}
105+
106+
public static CodeOption Namespace(this CodeOption root, Action<NamespaceOption> configure)
107+
{
108+
var option = new NamespaceOption();
109+
configure(option);
110+
root.OnBegin += option.OnBegin;
111+
root.OnEnd += option.OnEnd;
112+
root.OnChildren += option.OnChildren;
113+
return root;
114+
}
115+
116+
public static CodeOption Type(this CodeOption @namespace, Action<TypeOption> configure)
117+
{
118+
var option = new TypeOption();
119+
configure(option);
120+
121+
@namespace.OnChildren += option.OnBegin;
122+
@namespace.OnChildren += option.OnChildren;
123+
@namespace.OnChildren += option.OnEnd;
124+
125+
return @namespace;
126+
}
127+
128+
public static CodeOption Class(this CodeOption @namespace, Action<TypeOption> configure)
129+
{
130+
return @namespace.Type(option => {
131+
option.TypeKind = TypeOption.Type.Class;
132+
configure(option);
133+
});
134+
}
135+
136+
public static string Build(this CodeOption root)
137+
{
138+
var cb = new CodeBuilder(" ", 2, "{", "}", 1024);
139+
140+
root.OnBegin?.Invoke(cb);
141+
root.OnChildren?.Invoke(cb);
142+
root.OnEnd?.Invoke(cb);
143+
144+
return cb.ToString();
145+
}
146+
}

0 commit comments

Comments
 (0)