Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Append.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public CSharpCodeBuilder Append(string? value)
return this;
}

if (value is "\n" or "\r")
if (value is "\n" or "\r" or "\r\n")
{
return AppendLine(); // Handle new line characters
}
Expand Down
24 changes: 19 additions & 5 deletions src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ public CSharpCodeBuilder AppendXmlDocRemarks(string? remarks)

return EnsureNewLineForXmlDoc()
.AppendLine("/// <remarks>")
.AppendLine($"/// {remarks}")
.Append("/// ")
.AppendLine(remarks)
.AppendLine("/// </remarks>");
}

Expand Down Expand Up @@ -176,6 +177,17 @@ public CSharpCodeBuilder AppendXmlDocException(string? exceptionType, string? de
.AppendLine($"/// <exception cref=\"{exceptionType}\">{description}</exception>");
}

/// <summary>
/// Appends an XML documentation exception element.
/// </summary>
/// <param name="description">The description of when the exception is thrown.</param>
/// <typeparam name="TException">The type of exception that can be thrown. Must be a subclass of <see cref="Exception"/>.</typeparam>
/// <returns>The current <see cref="CSharpCodeBuilder"/> instance to allow for method chaining.</returns>
/// <remarks>If the exception type or description is null or empty, the method returns without appending anything.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CSharpCodeBuilder AppendXmlDocException<TException>(string? description)
where TException : Exception => AppendXmlDocException(typeof(TException).Name, description);

/// <summary>
/// Appends multiple XML documentation exception elements.
/// </summary>
Expand Down Expand Up @@ -246,32 +258,34 @@ public CSharpCodeBuilder AppendXmlDocExample(IEnumerable<string>? exampleLines)
/// Appends an XML documentation see element for cross-references.
/// </summary>
/// <param name="cref">The cross-reference to another member or type.</param>
/// <param name="isHref">If set to <c>true</c>, uses 'href' instead of 'cref' for external links.</param>
/// <returns>The current <see cref="CSharpCodeBuilder"/> instance to allow for method chaining.</returns>
/// <remarks>If the cref is null or empty, the method returns without appending anything.</remarks>
public CSharpCodeBuilder AppendXmlDocSee(string? cref)
public CSharpCodeBuilder AppendXmlDocSee(string? cref, bool isHref = false)
{
if (string.IsNullOrEmpty(cref))
{
return this;
}

return EnsureNewLineForXmlDoc().AppendLine($"/// <see cref=\"{cref}\"/>");
return EnsureNewLineForXmlDoc().AppendLine($"/// <see {(isHref ? "href" : "cref")}=\"{cref}\"/>");
}

/// <summary>
/// Appends an XML documentation seealso element for see-also references.
/// </summary>
/// <param name="cref">The cross-reference to another member or type.</param>
/// <param name="isHref">If set to <c>true</c>, uses 'href' instead of 'cref' for external links.</param>
/// <returns>The current <see cref="CSharpCodeBuilder"/> instance to allow for method chaining.</returns>
/// <remarks>If the cref is null or empty, the method returns without appending anything.</remarks>
public CSharpCodeBuilder AppendXmlDocSeeAlso(string? cref)
public CSharpCodeBuilder AppendXmlDocSeeAlso(string? cref, bool isHref = false)
{
if (string.IsNullOrEmpty(cref))
{
return this;
}

return EnsureNewLineForXmlDoc().AppendLine($"/// <seealso cref=\"{cref}\"/>");
return EnsureNewLineForXmlDoc().AppendLine($"/// <seealso {(isHref ? "href" : "cref")}=\"{cref}\"/>");
}

/// <summary>
Expand Down
91 changes: 91 additions & 0 deletions src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
namespace NetEvolve.CodeBuilder;

using System;

public partial record CSharpCodeBuilder
{
/// <summary>
/// Creates a scope that automatically manages indentation levels.
/// </summary>
/// <returns>A <see cref="ScopeHandler"/> that increments indentation on creation and decrements on disposal.</returns>
/// <remarks>
/// The returned scope handler implements <see cref="IDisposable"/> and is designed for use with
/// the 'using' statement. When the scope is created, indentation is incremented by one level.
/// When the scope is disposed (at the end of the using block), indentation is decremented.
/// </remarks>
/// <example>
/// <code>
/// using (builder.Scope())
/// {
/// builder.AppendLine("return true;");
/// }
/// </code>
/// </example>
public IDisposable Scope() => new ScopeHandler(this);

/// <summary>
/// A disposable struct that manages indentation scope for a <see cref="CSharpCodeBuilder"/>.
/// </summary>
/// <remarks>
/// This struct increments the indentation level when created and decrements it when disposed.
/// It is designed to work with the 'using' statement to provide automatic indentation management.
/// </remarks>
private readonly struct ScopeHandler : IDisposable, IEquatable<ScopeHandler>
{
private readonly CSharpCodeBuilder _builder;

/// <summary>
/// Initializes a new instance of the <see cref="ScopeHandler"/> struct and increments the indentation level.
/// </summary>
/// <param name="builder">The <see cref="CSharpCodeBuilder"/> instance to manage indentation for.</param>
internal ScopeHandler(CSharpCodeBuilder builder)
{
_builder = builder;
_ = _builder.Append('{');
}

/// <summary>
/// Decrements the indentation level when the scope is disposed.
/// </summary>
/// <remarks>
/// This method is called automatically when the 'using' statement block ends.
/// </remarks>
public void Dispose() => _ = _builder.Append('}');

/// <summary>
/// Determines whether the specified object is equal to the current instance.
/// </summary>
/// <param name="obj">The object to compare with the current instance.</param>
/// <returns>Always returns <c>false</c> since ScopeHandler instances should not be compared.</returns>
public override readonly bool Equals(object? obj) => false;

/// <summary>
/// Determines whether the specified ScopeHandler is equal to the current instance.
/// </summary>
/// <param name="other">The ScopeHandler to compare with the current instance.</param>
/// <returns>Always returns <c>false</c> since ScopeHandler instances should not be compared.</returns>
public readonly bool Equals(ScopeHandler other) => false;

/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A hash code based on the internal builder reference.</returns>
public override readonly int GetHashCode() => _builder?.GetHashCode() ?? 0;

/// <summary>
/// Determines whether two ScopeHandler instances are equal.
/// </summary>
/// <param name="_">The first instance to compare.</param>
/// <param name="__">The second instance to compare.</param>
/// <returns>Always returns <c>false</c> since ScopeHandler instances should not be compared.</returns>
public static bool operator ==(ScopeHandler _, ScopeHandler __) => false;

/// <summary>
/// Determines whether two ScopeHandler instances are not equal.
/// </summary>
/// <param name="_">The first instance to compare.</param>
/// <param name="__">The second instance to compare.</param>
/// <returns>Always returns <c>true</c> since ScopeHandler instances should not be compared.</returns>
public static bool operator !=(ScopeHandler _, ScopeHandler __) => true;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace NetEvolve.CodeBuilder.Tests.Integration;
namespace NetEvolve.CodeBuilder.Tests.Integration;

public partial class CSharpCodeBuilderTests
{
Expand Down Expand Up @@ -114,4 +114,30 @@ public async Task GenerateEnum_WithDocumentation_Should_ProduceCorrectOutput()

_ = await Verify(result);
}

[Test]
public async Task Scopes_in_wild_nature()
{
var builder = new CSharpCodeBuilder()
.AppendLine("namespace Hello.World;")
.AppendLine()
.AppendXmlDocSummary("Represents the status of an order.")
.AppendLine("public enum Weekday");

using (builder.Scope())
{
_ = builder
.AppendLine("Monday,")
.AppendLine("Tuesday,")
.AppendLine("Wednesday,")
.AppendLine("Thursday,")
.AppendLine("Friday,")
.AppendLine("Saturday,")
.AppendLine("Sunday,");
}

var result = builder.ToString();

_ = await Verify(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Hello.World;

/// <summary>
/// Represents the status of an order.
/// </summary>
public enum Weekday
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
Loading