diff --git a/EasyCodeBuilder.Test/Csharp/ControlFlowTests.cs b/EasyCodeBuilder.Test/Csharp/ControlFlowTests.cs index 899de2f..42023c8 100644 --- a/EasyCodeBuilder.Test/Csharp/ControlFlowTests.cs +++ b/EasyCodeBuilder.Test/Csharp/ControlFlowTests.cs @@ -1,282 +1,476 @@ -using Fengb3.EasyCodeBuilder.Csharp; -using Xunit.Abstractions; - -namespace EasyCodeBuilder.Test.Csharp; - -public class ControlFlowTests(ITestOutputHelper output) -{ - private static string Norm(string text) => text.Replace("\r\n", "\n").Trim(); - - [Fact] - public void IfStatement() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Check") - .WithReturnType("void") - .If(@if => - { - @if.Condition = "x > 0"; - @if.AppendLine("Console.WriteLine(x);"); - }) - .Build(); - - var expected = """ - public void Check() - { - if (x > 0) - { - Console.WriteLine(x); - } - } - """; - - Assert.Equal(Norm(expected), Norm(code)); - output.WriteLine(code); - } - - [Fact] - public void IfElseStatement() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Check") - .WithReturnType("void") - .If(@if => - { - @if.Condition = "x > 0"; - @if.AppendLine("Console.WriteLine(\"positive\");"); - }) - .Else(@else => - { - @else.AppendLine("Console.WriteLine(\"non-positive\");"); - }) - .Build(); - - var expected = """ - public void Check() - { - if (x > 0) - { - Console.WriteLine("positive"); - } - else - { - Console.WriteLine("non-positive"); - } - } - """; - - Assert.Equal(Norm(expected), Norm(code)); - output.WriteLine(code); - } - - [Fact] - public void IfElseIfElseStatement() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Classify") - .WithReturnType("void") - .If(@if => - { - @if.Condition = "x > 0"; - @if.AppendLine("Console.WriteLine(\"positive\");"); - }) - .ElseIf(@elseIf => - { - @elseIf.Condition = "x < 0"; - @elseIf.AppendLine("Console.WriteLine(\"negative\");"); - }) - .Else(@else => - { - @else.AppendLine("Console.WriteLine(\"zero\");"); - }) - .Build(); - - // Verify structural output contains all branches - Assert.Contains("if (x > 0)", code); - Assert.Contains("else if (x < 0)", code); - Assert.Contains("else", code); - Assert.Contains("Console.WriteLine(\"positive\");", code); - Assert.Contains("Console.WriteLine(\"negative\");", code); - Assert.Contains("Console.WriteLine(\"zero\");", code); - output.WriteLine(code); - } - - [Fact] - public void NestedIfStatement() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Check") - .WithReturnType("void") - .If(outer => - { - outer.Condition = "x > 0"; - outer.AddChild(inner => - { - inner.Condition = "x > 100"; - inner.AppendLine("Console.WriteLine(\"large\");"); - }); - }) - .Build(); - - Assert.Contains("if (x > 0)", code); - Assert.Contains("if (x > 100)", code); - Assert.Contains("Console.WriteLine(\"large\");", code); - // Verify nesting: inner if should be indented more - Assert.Contains(" if (x > 100)", code.Replace("\r\n", "\n")); - output.WriteLine(code); - } - - [Fact] - public void WhileLoop() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Loop") - .WithReturnType("void") - .While(@while => - { - @while.Condition = "running"; - @while.AppendLine("DoWork();"); - }) - .Build(); - - var expected = """ - public void Loop() - { - while (running) - { - DoWork(); - } - } - """; - - Assert.Equal(Norm(expected), Norm(code)); - output.WriteLine(code); - } - - [Fact] - public void DoWhileLoop() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Loop") - .WithReturnType("void") - .DoWhile(@do => - { - @do.Condition = "running"; - @do.AppendLine("DoWork();"); - }) - .Build(); - - var expected = """ - public void Loop() - { - do - { - DoWork(); - }while (running); - } - """; - - Assert.Equal(Norm(expected), Norm(code)); - output.WriteLine(code); - } - - [Fact] - public void ForeachLoop() - { - var code = new MethodOption() - .WithKeyword("public") - .WithName("Iterate") - .WithReturnType("void") - .Foreach(@foreach => - { - @foreach.WithVariable("var", "item", "items"); - @foreach.AppendLine("Console.WriteLine(item);"); - }) - .Build(); - - var expected = """ - public void Iterate() - { - foreach (var item in items) - { - Console.WriteLine(item); - } - } - """; - - Assert.Equal(Norm(expected), Norm(code)); - output.WriteLine(code); - } - - [Fact] - public void ForLoopStandalone() - { - var code = new MethodOption() - .WithKeyword("private") - .WithName("Count") - .WithReturnType("void") - .For(@for => - { - @for.WithInitializer("int i = 0"); - @for.WithCondition("i < 5"); - @for.WithIterator("i++"); - @for.AppendLine("sum += i;"); - }) - .Build(); - - var expected = """ - private void Count() - { - for (int i = 0; i < 5; i++) - { - sum += i; - } - } - """; - - Assert.Equal(Norm(expected), Norm(code)); - output.WriteLine(code); - } - - [Fact] - public void SwitchWithExpressionBody() - { - var method = new MethodOption() - .WithKeyword("public") - .WithName("GetLabel") - .WithReturnType("string") - .WithParameters("int value"); - - method.Switch(@switch => - { - @switch.Expression = "value"; - @switch.Case(@case => - { - @case.Value = "1"; - @case.AppendLine("return \"One\";"); - }); - @switch.Case(@case => - { - @case.Value = "2"; - @case.AppendLine("return \"Two\";"); - }); - @switch.Default(@default => @default.AppendLine("return \"Unknown\";")); - }); - - var code = method.Build(); - - Assert.Contains("switch (value)", code); - Assert.Contains("case 1:", code); - Assert.Contains("case 2:", code); - Assert.Contains("default:", code); - Assert.Contains("return \"One\";", code); - Assert.Contains("return \"Unknown\";", code); - output.WriteLine(code); - } -} +using Fengb3.EasyCodeBuilder.Csharp; +using Xunit.Abstractions; + +namespace EasyCodeBuilder.Test.Csharp; + +public class ControlFlowTests(ITestOutputHelper output) +{ + private static string Norm(string text) => text.Replace("\r\n", "\n").Trim(); + + [Fact] + public void IfStatement() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Check") + .WithReturnType("void") + .If(@if => + { + @if.Condition = "x > 0"; + @if.AppendLine("Console.WriteLine(x);"); + }) + .Build(); + + var expected = """ + public void Check() + { + if (x > 0) + { + Console.WriteLine(x); + } + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void IfElseStatement() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Check") + .WithReturnType("void") + .If(@if => + { + @if.Condition = "x > 0"; + @if.AppendLine("Console.WriteLine(\"positive\");"); + }) + .Else(@else => + { + @else.AppendLine("Console.WriteLine(\"non-positive\");"); + }) + .Build(); + + var expected = """ + public void Check() + { + if (x > 0) + { + Console.WriteLine("positive"); + } + else + { + Console.WriteLine("non-positive"); + } + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void IfElseIfElseStatement() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Classify") + .WithReturnType("void") + .If(@if => + { + @if.Condition = "x > 0"; + @if.AppendLine("Console.WriteLine(\"positive\");"); + }) + .ElseIf(@elseIf => + { + @elseIf.Condition = "x < 0"; + @elseIf.AppendLine("Console.WriteLine(\"negative\");"); + }) + .Else(@else => + { + @else.AppendLine("Console.WriteLine(\"zero\");"); + }) + .Build(); + + // Verify structural output contains all branches + Assert.Contains("if (x > 0)", code); + Assert.Contains("else if (x < 0)", code); + Assert.Contains("else", code); + Assert.Contains("Console.WriteLine(\"positive\");", code); + Assert.Contains("Console.WriteLine(\"negative\");", code); + Assert.Contains("Console.WriteLine(\"zero\");", code); + output.WriteLine(code); + } + + [Fact] + public void NestedIfStatement() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Check") + .WithReturnType("void") + .If(outer => + { + outer.Condition = "x > 0"; + outer.AddChild(inner => + { + inner.Condition = "x > 100"; + inner.AppendLine("Console.WriteLine(\"large\");"); + }); + }) + .Build(); + + Assert.Contains("if (x > 0)", code); + Assert.Contains("if (x > 100)", code); + Assert.Contains("Console.WriteLine(\"large\");", code); + // Verify nesting: inner if should be indented more + Assert.Contains(" if (x > 100)", code.Replace("\r\n", "\n")); + output.WriteLine(code); + } + + [Fact] + public void WhileLoop() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Loop") + .WithReturnType("void") + .While(@while => + { + @while.Condition = "running"; + @while.AppendLine("DoWork();"); + }) + .Build(); + + var expected = """ + public void Loop() + { + while (running) + { + DoWork(); + } + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void DoWhileLoop() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Loop") + .WithReturnType("void") + .DoWhile(@do => + { + @do.Condition = "running"; + @do.AppendLine("DoWork();"); + }) + .Build(); + + var expected = """ + public void Loop() + { + do + { + DoWork(); + }while (running); + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void ForeachLoop() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Iterate") + .WithReturnType("void") + .Foreach(@foreach => + { + @foreach.WithVariable("var", "item", "items"); + @foreach.AppendLine("Console.WriteLine(item);"); + }) + .Build(); + + var expected = """ + public void Iterate() + { + foreach (var item in items) + { + Console.WriteLine(item); + } + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void ForLoopStandalone() + { + var code = new MethodOption() + .WithKeyword("private") + .WithName("Count") + .WithReturnType("void") + .For(@for => + { + @for.WithInitializer("int i = 0"); + @for.WithCondition("i < 5"); + @for.WithIterator("i++"); + @for.AppendLine("sum += i;"); + }) + .Build(); + + var expected = """ + private void Count() + { + for (int i = 0; i < 5; i++) + { + sum += i; + } + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void SwitchWithExpressionBody() + { + var method = new MethodOption() + .WithKeyword("public") + .WithName("GetLabel") + .WithReturnType("string") + .WithParameters("int value"); + + method.Switch(@switch => + { + @switch.Expression = "value"; + @switch.Case(@case => + { + @case.Value = "1"; + @case.AppendLine("return \"One\";"); + }); + @switch.Case(@case => + { + @case.Value = "2"; + @case.AppendLine("return \"Two\";"); + }); + @switch.Default(@default => @default.AppendLine("return \"Unknown\";")); + }); + + var code = method.Build(); + + Assert.Contains("switch (value)", code); + Assert.Contains("case 1:", code); + Assert.Contains("case 2:", code); + Assert.Contains("default:", code); + Assert.Contains("return \"One\";", code); + Assert.Contains("return \"Unknown\";", code); + output.WriteLine(code); + } + + [Fact] + public void ForContainingSwitch_Issue11() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Parse") + .WithReturnType("void") + .WithParameters("string[] args") + .For(f => + { + f.WithInitializer("int i = 0") + .WithCondition("i < args.Length") + .WithIterator("i++"); + + f.Switch(s => + { + s.WithExpression("args[i]"); + s.Case(c => + { + c.Value = "\"--x\""; + c.AppendLine("x = int.Parse(args[++i]);"); + c.AppendLine("break;"); + }); + s.Default(d => + { + d.AppendLine("break;"); + }); + }); + }) + .Build(); + + var expected = """ + public void Parse(string[] args) + { + for (int i = 0; i < args.Length; i++) + { + switch (args[i]) + { + case "--x": + { + x = int.Parse(args[++i]); + break; + } + default: + { + break; + } + } + } + } + """; + + Assert.Equal(Norm(expected), Norm(code)); + output.WriteLine(code); + } + + [Fact] + public void DeepNesting_ForSwitchCaseIf() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Deep") + .WithReturnType("void") + .For(f => + { + f.WithInitializer("int i = 0") + .WithCondition("i < items.Count") + .WithIterator("i++"); + + f.Switch(s => + { + s.WithExpression("items[i].Type"); + s.Case(c => + { + c.Value = "\"A\""; + c.If(@if => + { + @if.Condition = "items[i].Value > 0"; + @if.AppendLine("Process(items[i]);"); + }); + }); + }); + }) + .Build(); + + Assert.Contains("for (int i = 0; i < items.Count; i++)", code); + Assert.Contains("switch (items[i].Type)", code); + Assert.Contains("case \"A\":", code); + Assert.Contains("if (items[i].Value > 0)", code); + Assert.Contains("Process(items[i]);", code); + output.WriteLine(code); + } + + [Fact] + public void WhileContainingFor() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Retry") + .WithReturnType("void") + .While(w => + { + w.Condition = "retry"; + w.For(f => + { + f.WithInitializer("int i = 0") + .WithCondition("i < retries") + .WithIterator("i++"); + f.AppendLine("Attempt(i);"); + }); + }) + .Build(); + + Assert.Contains("while (retry)", code); + Assert.Contains("for (int i = 0; i < retries; i++)", code); + Assert.Contains("Attempt(i);", code); + output.WriteLine(code); + } + + [Fact] + public void TryInsideFor() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("SafeIterate") + .WithReturnType("void") + .WithParameters("List items") + .For(f => + { + f.WithInitializer("int i = 0") + .WithCondition("i < items.Count") + .WithIterator("i++"); + + f.Try(t => + { + t.AppendLine("Process(items[i]);"); + t.Catch(c => + { + c.WithExceptionType("Exception") + .WithVariableName("ex"); + c.AppendLine("Log(ex);"); + }); + }); + }) + .Build(); + + Assert.Contains("for (int i = 0; i < items.Count; i++)", code); + Assert.Contains("try", code); + Assert.Contains("Process(items[i]);", code); + Assert.Contains("catch (Exception ex)", code); + Assert.Contains("Log(ex);", code); + output.WriteLine(code); + } + + [Fact] + public void IfElseIfElseInsideFor() + { + var code = new MethodOption() + .WithKeyword("public") + .WithName("Classify") + .WithReturnType("void") + .For(f => + { + f.WithInitializer("int i = 0") + .WithCondition("i < values.Length") + .WithIterator("i++"); + + f.If(@if => + { + @if.Condition = "values[i] > 0"; + @if.AppendLine("Console.WriteLine(\"positive\");"); + }) + .ElseIf(elseIf => + { + elseIf.Condition = "values[i] < 0"; + elseIf.AppendLine("Console.WriteLine(\"negative\");"); + }) + .Else(@else => + { + @else.AppendLine("Console.WriteLine(\"zero\");"); + }); + }) + .Build(); + + Assert.Contains("for (int i = 0; i < values.Length; i++)", code); + Assert.Contains("if (values[i] > 0)", code); + Assert.Contains("else if (values[i] < 0)", code); + Assert.Contains("else", code); + Assert.Contains("Console.WriteLine(\"positive\");", code); + Assert.Contains("Console.WriteLine(\"negative\");", code); + Assert.Contains("Console.WriteLine(\"zero\");", code); + output.WriteLine(code); + } +} diff --git a/EasyCodeBuilder/Csharp/Code.cs b/EasyCodeBuilder/Csharp/Code.cs index e432128..dfa1741 100644 --- a/EasyCodeBuilder/Csharp/Code.cs +++ b/EasyCodeBuilder/Csharp/Code.cs @@ -1,203 +1,284 @@ -using System; -using System.Linq; - -namespace Fengb3.EasyCodeBuilder.Csharp; - -/// -/// Provides the entry point and extension methods for building C# code using a configuration-based API. -/// -public static partial class Code -{ - /// - /// Creates a new root to begin building code. - /// - /// A new instance. - public static CodeOption Create() - { - return new CodeOption(); - } - - /// - /// Backward-compatible API: add using directives by namespace names. - /// - public static CodeOption Using(this CodeOption option, params string[] usings) - { - foreach (var u in usings) - { - option.AddChild(uo => { uo.Name = u; }); - } - - return option; - } - - /// - /// Add a single using directive with full configuration. - /// - public static CodeOption Using(this CodeOption option, Action configure) - => option.AddChild(configure); - - /// - /// using static {typeOrNamespace}; - /// - public static CodeOption UsingStatic(this CodeOption option, string typeOrNamespace) - => option.AddChild(uo => - { - uo.Name = typeOrNamespace; - uo.IsStatic = true; - }); - - /// - /// using {alias} = {typeOrNamespace}; - /// - public static CodeOption UsingAlias(this CodeOption option, string alias, string typeOrNamespace) - => option.AddChild(uo => - { - uo.Alias = alias; - uo.Name = typeOrNamespace; - }); - - /// - /// global using {typeOrNamespace}; - /// - public static CodeOption GlobalUsing(this CodeOption option, string typeOrNamespace) - => option.AddChild(uo => - { - uo.Keywords.Add("global"); - uo.Name = typeOrNamespace; - }); - - /// - /// global using static {typeOrNamespace}; - /// - public static CodeOption GlobalUsingStatic(this CodeOption option, string typeOrNamespace) - => option.AddChild(uo => - { - uo.Keywords.Add("global"); - uo.Name = typeOrNamespace; - uo.IsStatic = true; - }); - - /// - /// Creates a new child option, configures it via , - /// and attaches its to the parent's . - /// - /// The parent code option. - /// Action to configure the new child. - /// Parent option type. - /// Child option type. - /// The parent option, for fluent chaining. - public static TParent AddChild(this TParent parent, Action configureChild) - where TParent : CodeOption where TChild : CodeOption, new() - { - var child = new TChild(); - configureChild?.Invoke(child); - return parent.AddChild(child); - } - - /// - /// Attaches an existing child option's to the parent's . - /// - /// The parent code option. - /// The child code option to attach. - /// Parent option type. - /// Child option type. - /// The parent option, for fluent chaining. - public static TParent AddChild(this TParent parent, TChild child) - where TParent : CodeOption where TChild : CodeOption - { - parent.OnChildren += child.Build; - return parent; - } - - /// - /// 添加一行或多行代码 - /// - /// 代码选项 - /// 代码行 - /// 代码选项 - public static CodeOption AppendLine(this CodeOption option, params string[] lines) - { - option.OnChildren += cb => cb.AppendLines(lines); - return option; - } - - /// - /// Add a namespace to the code. - /// - /// The root code option. - /// Action to configure the namespace. - /// The root code option, for fluent chaining. - public static CodeOption Namespace(this CodeOption option, Action configure) - => option.AddChild(configure); - - /// - /// Add a class to the code. - /// - /// The root code option. - /// Action to configure the class. - /// The root code option, for fluent chaining. - public static CodeOption Class(this CodeOption option, Action configure) - => option.AddChild(configure); - - - /// - /// add attribute to an element - /// - /// The code option to add attributes to. - /// Attribute declarations (e.g. "Serializable", "Obsolete"). - /// The option type. - /// The option, for fluent chaining. - public static T WithAttributes(this T option, params string[] attributes) where T : CodeOption - { - option.BeforeChildren += cb => cb.AppendLines(attributes.Select(attr => $"[{attr}]")); - return option; - } - - /// - /// add XML doc to an element - /// - /// The code option to add XML documentation to. - /// Action to configure the XML doc. - /// The option type. - /// The option, for fluent chaining. - public static T WithXmlDoc(this T option, Action configure) where T : CodeOption - { - option.BeforeChildren += cb => - { - var xmlDocOption = new XmlDocOption(); - configure(xmlDocOption); - xmlDocOption.Build(cb); - return cb; - }; - return option; - } - - - /// - /// Add single or multiple lines to a code block - /// - /// The code option. - /// The lines of code to add. - /// The option type. - /// The option, for fluent chaining. - public static T AppendLines(this T option, params string[] lines) where T : CodeOption - { - foreach (var line in lines) - option.AppendLine(line); - - return option; - } - - /// - /// 构建代码 - /// - /// 根选项 - /// 代码构建器 - /// 生成的代码字符串 - public static string Build(this CodeOption root, CodeBuilder? cb = null) - { - cb ??= new CodeBuilder(' ', 2, "\n{", "}", 1024); - root.Build(cb); - return cb.ToString(); - } +using System; +using System.Linq; + +namespace Fengb3.EasyCodeBuilder.Csharp; + +/// +/// Provides the entry point and extension methods for building C# code using a configuration-based API. +/// +public static partial class Code +{ + /// + /// Creates a new root to begin building code. + /// + /// A new instance. + public static CodeOption Create() + { + return new CodeOption(); + } + + /// + /// Backward-compatible API: add using directives by namespace names. + /// + public static CodeOption Using(this CodeOption option, params string[] usings) + { + foreach (var u in usings) + { + option.AddChild(uo => { uo.Name = u; }); + } + + return option; + } + + /// + /// Add a single using directive with full configuration. + /// + public static CodeOption Using(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// using static {typeOrNamespace}; + /// + public static CodeOption UsingStatic(this CodeOption option, string typeOrNamespace) + => option.AddChild(uo => + { + uo.Name = typeOrNamespace; + uo.IsStatic = true; + }); + + /// + /// using {alias} = {typeOrNamespace}; + /// + public static CodeOption UsingAlias(this CodeOption option, string alias, string typeOrNamespace) + => option.AddChild(uo => + { + uo.Alias = alias; + uo.Name = typeOrNamespace; + }); + + /// + /// global using {typeOrNamespace}; + /// + public static CodeOption GlobalUsing(this CodeOption option, string typeOrNamespace) + => option.AddChild(uo => + { + uo.Keywords.Add("global"); + uo.Name = typeOrNamespace; + }); + + /// + /// global using static {typeOrNamespace}; + /// + public static CodeOption GlobalUsingStatic(this CodeOption option, string typeOrNamespace) + => option.AddChild(uo => + { + uo.Keywords.Add("global"); + uo.Name = typeOrNamespace; + uo.IsStatic = true; + }); + + /// + /// Creates a new child option, configures it via , + /// and attaches its to the parent's . + /// + /// The parent code option. + /// Action to configure the new child. + /// Parent option type. + /// Child option type. + /// The parent option, for fluent chaining. + public static TParent AddChild(this TParent parent, Action configureChild) + where TParent : CodeOption where TChild : CodeOption, new() + { + var child = new TChild(); + configureChild?.Invoke(child); + return parent.AddChild(child); + } + + /// + /// Attaches an existing child option's to the parent's . + /// + /// The parent code option. + /// The child code option to attach. + /// Parent option type. + /// Child option type. + /// The parent option, for fluent chaining. + public static TParent AddChild(this TParent parent, TChild child) + where TParent : CodeOption where TChild : CodeOption + { + parent.OnChildren += child.Build; + return parent; + } + + /// + /// 添加一行或多行代码 + /// + /// 代码选项 + /// 代码行 + /// 代码选项 + public static CodeOption AppendLine(this CodeOption option, params string[] lines) + { + option.OnChildren += cb => cb.AppendLines(lines); + return option; + } + + /// + /// Add an if statement to the code block. + /// + /// The code option. + /// Action to configure the if statement. + /// The code option, for fluent chaining. + public static CodeOption If(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add an else statement to the code block. + /// + /// The code option. + /// Action to configure the else statement. + /// The code option, for fluent chaining. + public static CodeOption Else(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add an else-if statement to the code block. + /// + /// The code option. + /// Action to configure the else-if statement. + /// The code option, for fluent chaining. + public static CodeOption ElseIf(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a for loop to the code block. + /// + /// The code option. + /// Action to configure the for loop. + /// The code option, for fluent chaining. + public static CodeOption For(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a while loop to the code block. + /// + /// The code option. + /// Action to configure the while loop. + /// The code option, for fluent chaining. + public static CodeOption While(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a do-while loop to the code block. + /// + /// The code option. + /// Action to configure the do-while loop. + /// The code option, for fluent chaining. + public static CodeOption DoWhile(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a foreach loop to the code block. + /// + /// The code option. + /// Action to configure the foreach loop. + /// The code option, for fluent chaining. + public static CodeOption Foreach(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a switch statement to the code block. + /// + /// The code option. + /// Action to configure the switch statement. + /// The code option, for fluent chaining. + public static CodeOption Switch(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a try-catch-finally statement to the code block. + /// + /// The code option. + /// Action to configure the try statement. + /// The code option, for fluent chaining. + public static CodeOption Try(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a namespace to the code. + /// + /// The root code option. + /// Action to configure the namespace. + /// The root code option, for fluent chaining. + public static CodeOption Namespace(this CodeOption option, Action configure) + => option.AddChild(configure); + + /// + /// Add a class to the code. + /// + /// The root code option. + /// Action to configure the class. + /// The root code option, for fluent chaining. + public static CodeOption Class(this CodeOption option, Action configure) + => option.AddChild(configure); + + + /// + /// add attribute to an element + /// + /// The code option to add attributes to. + /// Attribute declarations (e.g. "Serializable", "Obsolete"). + /// The option type. + /// The option, for fluent chaining. + public static T WithAttributes(this T option, params string[] attributes) where T : CodeOption + { + option.BeforeChildren += cb => cb.AppendLines(attributes.Select(attr => $"[{attr}]")); + return option; + } + + /// + /// add XML doc to an element + /// + /// The code option to add XML documentation to. + /// Action to configure the XML doc. + /// The option type. + /// The option, for fluent chaining. + public static T WithXmlDoc(this T option, Action configure) where T : CodeOption + { + option.BeforeChildren += cb => + { + var xmlDocOption = new XmlDocOption(); + configure(xmlDocOption); + xmlDocOption.Build(cb); + return cb; + }; + return option; + } + + + /// + /// Add single or multiple lines to a code block + /// + /// The code option. + /// The lines of code to add. + /// The option type. + /// The option, for fluent chaining. + public static T AppendLines(this T option, params string[] lines) where T : CodeOption + { + foreach (var line in lines) + option.AppendLine(line); + + return option; + } + + /// + /// 构建代码 + /// + /// 根选项 + /// 代码构建器 + /// 生成的代码字符串 + public static string Build(this CodeOption root, CodeBuilder? cb = null) + { + cb ??= new CodeBuilder(' ', 2, "\n{", "}", 1024); + root.Build(cb); + return cb.ToString(); + } } \ No newline at end of file