diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Clear.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Clear.cs
index db25aef..036afea 100644
--- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Clear.cs
+++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Clear.cs
@@ -17,4 +17,22 @@ public CSharpCodeBuilder Clear()
return this;
}
+
+ ///
+ /// Appends a single indentation unit to the current builder without affecting the indentation level.
+ ///
+ /// The current instance to allow for method chaining.
+ ///
+ /// This method adds one indentation unit directly to the builder based on the setting.
+ /// If is , a tab character is appended.
+ /// If is , four space characters are appended.
+ /// Unlike automatic indentation that occurs at the start of new lines, this method provides manual control
+ /// over indentation placement and does not modify the current indentation level.
+ ///
+ public CSharpCodeBuilder Intend()
+ {
+ _ = _builder.Append(UseTabs ? '\t' : ' ', UseTabs ? 1 : 4);
+
+ return this;
+ }
}
diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs
index 06cde62..7a74544 100644
--- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs
+++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs
@@ -1,4 +1,4 @@
-namespace NetEvolve.CodeBuilder;
+namespace NetEvolve.CodeBuilder;
using System;
@@ -23,6 +23,32 @@ public partial record CSharpCodeBuilder
///
public IDisposable Scope() => new ScopeHandler(this);
+ ///
+ /// Appends a line of text and creates a scope that automatically manages indentation levels with braces.
+ ///
+ /// The string value to append before creating the scope. Can be .
+ /// A that appends an opening brace, increments indentation on creation, and appends a closing brace with decremented indentation on disposal.
+ ///
+ /// This method combines with functionality.
+ /// The returned scope handler implements and is designed for use with the using statement.
+ /// When the scope is created, an opening brace is appended and indentation is incremented by one level.
+ /// When the scope is disposed, indentation is decremented and a closing brace is appended.
+ ///
+ ///
+ ///
+ /// var builder = new CSharpCodeBuilder();
+ /// using (builder.ScopeLine("public class MyClass"))
+ /// {
+ /// builder.AppendLine("public string Name { get; set; }");
+ /// }
+ ///
+ ///
+ public IDisposable ScopeLine(string? value)
+ {
+ _ = AppendLine(value);
+ return new ScopeHandler(this);
+ }
+
///
/// A disposable struct that manages indentation scope for a .
///
diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Clear.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Clear.cs
index 2e76e80..e070f99 100644
--- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Clear.cs
+++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Clear.cs
@@ -93,4 +93,196 @@ public async Task Clear_Should_Preserve_Capacity()
_ = await Assert.That(builder.Capacity).IsEqualTo(originalCapacity);
}
+
+ [Test]
+ public async Task Intend_Should_Append_Single_Indentation()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.Intend().Append("Hello");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo(" Hello");
+ }
+
+ [Test]
+ public async Task Intend_Should_Not_Affect_Indentation_Level()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.Intend().AppendLine("First");
+ _ = builder.Append("Second");
+
+ var result = builder.ToString();
+ // Second line should not be indented because Intend() doesn't change the level
+ _ = await Assert.That(result).IsEqualTo(" First" + Environment.NewLine + "Second");
+ }
+
+ [Test]
+ public async Task Intend_With_Tabs_Should_Append_Tab_Character()
+ {
+ var builder = new CSharpCodeBuilder { UseTabs = true };
+
+ _ = builder.Intend().Append("Hello");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo("\tHello");
+ }
+
+ [Test]
+ public async Task Intend_With_Spaces_Should_Append_Four_Spaces()
+ {
+ var builder = new CSharpCodeBuilder { UseTabs = false };
+
+ _ = builder.Intend().Append("Hello");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo(" Hello");
+ }
+
+ [Test]
+ public async Task Intend_Multiple_Calls_Should_Append_Multiple_Indentations()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.Intend().Intend().Intend().Append("Hello");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo(" Hello"); // 12 spaces (3 * 4)
+ }
+
+ [Test]
+ public async Task Intend_Multiple_With_Tabs_Should_Append_Multiple_Tabs()
+ {
+ var builder = new CSharpCodeBuilder { UseTabs = true };
+
+ _ = builder.Intend().Intend().Intend().Append("Hello");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo("\t\t\tHello");
+ }
+
+ [Test]
+ public async Task Intend_Should_Return_Builder_For_Chaining()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ var result = builder.Intend();
+
+ _ = await Assert.That(result).IsEqualTo(builder);
+ }
+
+ [Test]
+ public async Task Intend_In_Middle_Of_Line_Should_Append_Indentation()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.Append("Hello").Intend().Append("World");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo("Hello World");
+ }
+
+ [Test]
+ public async Task Intend_After_NewLine_Should_Add_Manual_Indentation()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.AppendLine("First");
+ _ = builder.Intend().Append("Second");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo("First" + Environment.NewLine + " Second");
+ }
+
+ [Test]
+ public async Task Intend_With_Automatic_Indentation_Should_Stack()
+ {
+ var builder = new CSharpCodeBuilder();
+ builder.IncrementIndent(); // Set automatic indentation level to 1
+
+ _ = builder.AppendLine().Intend().Append("Hello");
+
+ var result = builder.ToString();
+ // Should have both automatic (4 spaces) and manual (4 spaces) indentation
+ _ = await Assert.That(result).IsEqualTo(Environment.NewLine + " Hello");
+ }
+
+ [Test]
+ public async Task Intend_Multiple_Mixed_With_Content_Should_Work()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder
+ .Intend()
+ .Append("Level 1")
+ .AppendLine()
+ .Intend()
+ .Intend()
+ .Append("Level 2")
+ .AppendLine()
+ .Intend()
+ .Intend()
+ .Intend()
+ .Append("Level 3");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains(" Level 1");
+ _ = await Assert.That(result).Contains(" Level 2");
+ _ = await Assert.That(result).Contains(" Level 3");
+ }
+
+ [Test]
+ public async Task Intend_Should_Work_With_Empty_Builder()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.Intend();
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo(" ");
+ }
+
+ [Test]
+ public async Task Intend_Should_Work_After_Clear()
+ {
+ var builder = new CSharpCodeBuilder();
+ _ = builder.Append("Hello");
+ _ = builder.Clear();
+
+ _ = builder.Intend().Append("World");
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).IsEqualTo(" World");
+ }
+
+ [Test]
+ public async Task Intend_Combined_With_Scope_Should_Add_Extra_Indentation()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.Scope())
+ {
+ _ = builder.Intend().Append("Extra indented");
+ }
+
+ var result = builder.ToString();
+ // Should have both scope indentation (4 spaces) and manual indentation (4 spaces)
+ _ = await Assert.That(result).Contains(" Extra indented"); // 8 spaces
+ }
+
+ [Test]
+ public async Task Intend_Combined_With_ScopeLine_Should_Add_Extra_Indentation()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ _ = builder.Intend().Append("// Extra indented comment");
+ }
+
+ var result = builder.ToString();
+ // Should have both scope indentation and manual indentation
+ _ = await Assert.That(result).Contains(" // Extra indented comment"); // 8 spaces
+ }
}
diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs
new file mode 100644
index 0000000..e730e8f
--- /dev/null
+++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs
@@ -0,0 +1,330 @@
+namespace NetEvolve.CodeBuilder.Tests.Unit;
+
+using System;
+
+public partial class CSharpCodeBuilderTests
+{
+ #region Scope Tests
+
+ [Test]
+ public async Task Scope_Should_Create_Disposable_Handler()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ var scope = builder.Scope();
+
+ _ = await Assert.That(scope).IsNotNull();
+ }
+
+ [Test]
+ public async Task Scope_Should_Append_Opening_Brace()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.Scope())
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("{");
+ }
+
+ [Test]
+ public async Task Scope_Should_Append_Closing_Brace_On_Dispose()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.Scope())
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("}");
+ }
+
+ [Test]
+ public async Task Scope_Should_Contain_Both_Braces()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.Scope())
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("{");
+ _ = await Assert.That(result).Contains("}");
+ }
+
+ [Test]
+ public async Task Scope_Nested_Should_Create_Multiple_Indentation_Levels()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.Scope())
+ {
+ _ = builder.Append("Level 1");
+ using (builder.Scope())
+ {
+ _ = builder.Append("Level 2");
+ }
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains(" Level 1"); // 4 spaces
+ _ = await Assert.That(result).Contains(" Level 2"); // 8 spaces
+ }
+
+ [Test]
+ public async Task Scope_Multiple_Sequential_Should_Reset_Indentation()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.Scope())
+ {
+ _ = builder.Append("First");
+ }
+
+ using (builder.Scope())
+ {
+ _ = builder.Append("Second");
+ }
+
+ var result = builder.ToString();
+ // Both should be at the same indentation level (top level)
+ _ = await Assert.That(result).Contains("{");
+ _ = await Assert.That(result).Contains("}");
+ }
+
+ [Test]
+ public async Task Scope_With_Tabs_Should_Use_Tab_Indentation()
+ {
+ var builder = new CSharpCodeBuilder { UseTabs = true };
+
+ using (builder.Scope())
+ {
+ _ = builder.Append("Hello");
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("\tHello");
+ }
+
+ #endregion
+
+ #region ScopeLine Tests
+
+ [Test]
+ public async Task ScopeLine_Should_Append_Line_Before_Scope()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("public class MyClass");
+ }
+
+ [Test]
+ public async Task ScopeLine_Should_Include_Opening_Brace()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("{");
+ }
+
+ [Test]
+ public async Task ScopeLine_Should_Include_Closing_Brace_On_Dispose()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("}");
+ }
+
+ [Test]
+ public async Task ScopeLine_With_Null_Value_Should_Append_Only_Braces()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine(null))
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("{");
+ _ = await Assert.That(result).Contains("}");
+ }
+
+ [Test]
+ public async Task ScopeLine_With_Empty_String_Should_Append_Only_Braces()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine(string.Empty))
+ {
+ // Empty scope
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("{");
+ _ = await Assert.That(result).Contains("}");
+ }
+
+ [Test]
+ public async Task ScopeLine_With_Content_Should_Format_Class_Structure()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ _ = builder.Append("private int _field;");
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("public class MyClass");
+ _ = await Assert.That(result).Contains("private int _field;");
+ }
+
+ [Test]
+ public async Task ScopeLine_Nested_Should_Create_Class_Hierarchy()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("namespace MyNamespace"))
+ {
+ using (builder.ScopeLine("public class OuterClass"))
+ {
+ _ = builder.Append("public void Method() { }");
+ }
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("namespace MyNamespace");
+ _ = await Assert.That(result).Contains("public class OuterClass");
+ _ = await Assert.That(result).Contains("public void Method() { }");
+ }
+
+ [Test]
+ public async Task ScopeLine_Should_Apply_Proper_Indentation_To_Content()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ _ = builder.Append("public string Name { get; set; }");
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains(" public string Name { get; set; }");
+ }
+
+ [Test]
+ public async Task ScopeLine_Multiple_Sequential_Should_Create_Multiple_Classes()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("public class FirstClass"))
+ {
+ _ = builder.Append("public int Id { get; set; }");
+ }
+
+ _ = builder.AppendLine(); // Add spacing between classes
+
+ using (builder.ScopeLine("public class SecondClass"))
+ {
+ _ = builder.Append("public string Name { get; set; }");
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("public class FirstClass");
+ _ = await Assert.That(result).Contains("public class SecondClass");
+ }
+
+ [Test]
+ public async Task ScopeLine_With_Tabs_Should_Use_Tab_Indentation()
+ {
+ var builder = new CSharpCodeBuilder { UseTabs = true };
+
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ _ = builder.Append("public void Method() { }");
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("\tpublic void Method() { }");
+ }
+
+ [Test]
+ public async Task ScopeLine_Complex_Nested_Structure_Should_Format_Correctly()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ using (builder.ScopeLine("namespace MyApplication"))
+ {
+ using (builder.ScopeLine("public class MyClass"))
+ {
+ using (builder.ScopeLine("public void MyMethod()"))
+ {
+ _ = builder.Append("var x = 10;");
+ }
+ }
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("namespace MyApplication");
+ _ = await Assert.That(result).Contains(" public class MyClass");
+ _ = await Assert.That(result).Contains(" public void MyMethod()");
+ _ = await Assert.That(result).Contains(" var x = 10;");
+ }
+
+ [Test]
+ public async Task ScopeLine_Should_Return_Disposable()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ var scope = builder.ScopeLine("public class MyClass");
+
+ _ = await Assert.That(scope).IsNotNull();
+ scope.Dispose();
+ }
+
+ [Test]
+ public async Task ScopeLine_With_Documentation_Should_Format_Complete_Class()
+ {
+ var builder = new CSharpCodeBuilder();
+
+ _ = builder.AppendXmlDocSummary("Represents a person entity.");
+ using (builder.ScopeLine("public class Person"))
+ {
+ _ = builder.AppendXmlDocSummary("Gets or sets the person's name.");
+ _ = builder.Append("public string Name { get; set; }");
+ }
+
+ var result = builder.ToString();
+ _ = await Assert.That(result).Contains("/// ");
+ _ = await Assert.That(result).Contains("/// Represents a person entity.");
+ _ = await Assert.That(result).Contains("public class Person");
+ _ = await Assert.That(result).Contains("public string Name { get; set; }");
+ }
+
+ #endregion
+}