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
6 changes: 3 additions & 3 deletions .csharpierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
**/nuget.config
**/_snapshots/
**/_snapshot/
**/[Nn]u[Gg]et.config
**/*.verified.*
**/*.received.*
23 changes: 21 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,22 @@ generated_code = true
# XML project files
[*.{slnx,csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,nativeproj,locproj}]
indent_size = 2
max_line_length = 160
max_line_length = 200

# Xml build files
[*.builds]
indent_size = 2
max_line_length = 200

# Xml files
[*.{xml,stylecop,resx,ruleset}]
indent_size = 2
max_line_length = 200

# XML config files
[*.{props,targets,ruleset,config,nuspec,vsixmanifest,vsct}]
indent_size = 2
max_line_length = 200

# JSON files
[*.json]
Expand Down Expand Up @@ -128,7 +131,7 @@ dotnet_naming_style.all_const.capitalization = pascal
dotnet_naming_symbols.all_const.applicable_kinds = field
dotnet_naming_symbols.all_const.required_modifiers = const
dotnet_naming_rule.all_const.severity = error
dotnet_naming_rule.all_const.style = all_const
dotnet_naming_rule.all_const.style = all_elements
dotnet_naming_rule.all_const.symbols = all_const

dotnet_naming_style.all_static_readonly.capitalization = pascal_case
Expand Down Expand Up @@ -267,10 +270,26 @@ dotnet_diagnostic.IDE0046.severity = sugges
csharp_style_prefer_primary_constructors = false
dotnet_diagnostic.IDE0290.severity = suggestion

# IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0060.severity = warning
dotnet_diagnostic.RCS1163.severity = none
dotnet_code_quality_unused_parameters = all

# [CSharpier] Incompatible rules deactivated
# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules
dotnet_diagnostic.IDE0055.severity = none

# Support for NetEvolve.Arguments Methods
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1062#null-check-validation-methods
dotnet_code_quality.CA1062.null_check_validation_methods = M:NetEvolve.Arguments.Argument.ThrowIfNull(System.Object,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNull(System.Void*,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrEmpty(System.String,System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrEmpty``1(System.Collections.Generic.IEnumerable{``0},System.String)|M:NetEvolve.Arguments.Argument.ThrowIfNullOrWhiteSpace(System.String,System.String)

# Disable all style rules for generated code
[*.{received,verified}.*]
generated_code = true
# Disable all style rules for migrations
dotnet_analyzer_diagnostic.severity = none

[**/Migrations/*.{cs,csx,vb,vbx}]
generated_code = true
# Disable all style rules for migrations
dotnet_analyzer_diagnostic.severity = none
1 change: 0 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
* text=auto eol=lf
* text eol=lf

# (binary is a macro for -text -diff)
*.png binary
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,13 @@ MigrationBackup/
# Prevent nested .editorconfig files - only root .editorconfig should be used
**/.editorconfig
!/.editorconfig

# MemPalace per-project files
.mempalace/
mempalace.yaml
entities.json

# Beads / Dolt files (added by bd init)
.dolt/
*.db
.beads-credential-key
9 changes: 7 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
</PropertyGroup>
<ItemGroup>
<GlobalPackageReference Include="CSharpier.MSBuild" Version="1.2.6" />
<GlobalPackageReference Include="GitVersion.MsBuild" Version="6.7.0" />
<GlobalPackageReference Include="Meziantou.Analyzer" Version="3.0.77" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.203" />
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.203" />
<GlobalPackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15" />
<GlobalPackageReference Include="NetEvolve.Defaults" Version="2.2.0" />
<GlobalPackageReference Include="NetEvolve.Defaults" Version="2.6.19" />
<GlobalPackageReference Include="Roslynator.Analyzers" Version="4.15.0" />
<GlobalPackageReference Include="Roslynator.Formatting.Analyzers" Version="4.15.0" />
<GlobalPackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.15.0" />
<GlobalPackageReference Include="Roslynator.CodeFixes" Version="4.15.0" />
<GlobalPackageReference Include="Roslynator.Refactorings" Version="4.15.0" />
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="10.25.0.139117" />
</ItemGroup>
<ItemGroup>
Expand Down
130 changes: 4 additions & 126 deletions src/NetEvolve.CodeBuilder/CSharpCodeBuilder.AppendInterpolated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public partial class CSharpCodeBuilder
/// </code>
/// </example>
public CSharpCodeBuilder AppendInterpolated(
#pragma warning disable IDE0060 // Remove unused parameter
[InterpolatedStringHandlerArgument("")] ref CSharpInterpolatedStringHandler handler
#pragma warning restore IDE0060 // Remove unused parameter
) => this;

/// <summary>
Expand All @@ -31,7 +33,9 @@ public CSharpCodeBuilder AppendInterpolated(
/// </code>
/// </example>
public CSharpCodeBuilder AppendLineInterpolated(
#pragma warning disable IDE0060 // Remove unused parameter
[InterpolatedStringHandlerArgument("")] ref CSharpInterpolatedStringHandler handler
#pragma warning restore IDE0060 // Remove unused parameter
) => AppendLine();

internal void HandlerEnsureIndented() => EnsureIndented();
Expand All @@ -52,130 +56,4 @@ internal void HandlerRawAppend(ReadOnlySpan<char> value)
}
}
}

/// <summary>
/// Custom interpolated string handler for <see cref="CSharpCodeBuilder"/>.
/// </summary>
/// <remarks>
/// This handler is instantiated by the compiler when an interpolated string is passed to
/// <see cref="CSharpCodeBuilder.AppendInterpolated"/> or <see cref="CSharpCodeBuilder.AppendLineInterpolated"/>.
/// It appends each literal and formatted part directly to the builder, applying indentation before
/// the first non-empty part on a new line.
/// </remarks>
[InterpolatedStringHandler]
public ref struct CSharpInterpolatedStringHandler
{
private readonly CSharpCodeBuilder _owner;
private bool _indentEnsured;

/// <summary>
/// Initializes a new instance of the <see cref="CSharpInterpolatedStringHandler"/> struct.
/// </summary>
/// <param name="literalLength">The total length of all literal parts (hint for capacity).</param>
/// <param name="formattedCount">The number of formatted holes in the interpolated string.</param>
/// <param name="builder">The <see cref="CSharpCodeBuilder"/> to append to.</param>
public CSharpInterpolatedStringHandler(int literalLength, int formattedCount, CSharpCodeBuilder builder)
{
_owner = builder;
_indentEnsured = false;
}

private void EnsureIndented()
{
if (!_indentEnsured)
{
_owner.HandlerEnsureIndented();
_indentEnsured = true;
}
}

/// <summary>Appends a literal string part of the interpolated string.</summary>
/// <param name="value">The literal string to append.</param>
public void AppendLiteral(string? value)
{
if (string.IsNullOrEmpty(value))
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(value);
}

/// <summary>Appends a formatted value from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format.</typeparam>
/// <param name="value">The value to append.</param>
public void AppendFormatted<T>(T value)
{
var str = value?.ToString();
if (string.IsNullOrEmpty(str))
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a formatted value with a format string from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format. Must implement <see cref="System.IFormattable"/>.</typeparam>
/// <param name="value">The value to append.</param>
/// <param name="format">The format string.</param>
public void AppendFormatted<T>(T value, string? format)
where T : System.IFormattable
{
var str = value?.ToString(format, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(str))
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a formatted value with alignment from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format.</typeparam>
/// <param name="value">The value to append.</param>
/// <param name="alignment">Minimum width; negative values left-align.</param>
public void AppendFormatted<T>(T value, int alignment)
{
var str = value?.ToString();
if (str is null)
{
return;
}

str = alignment >= 0 ? str.PadLeft(alignment) : str.PadRight(-alignment);
EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a formatted value with alignment and format string from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format. Must implement <see cref="System.IFormattable"/>.</typeparam>
/// <param name="value">The value to append.</param>
/// <param name="alignment">Minimum width; negative values left-align.</param>
/// <param name="format">The format string.</param>
public void AppendFormatted<T>(T value, int alignment, string? format)
where T : System.IFormattable
{
var str = value?.ToString(format, CultureInfo.InvariantCulture) ?? string.Empty;
str = alignment >= 0 ? str.PadLeft(alignment) : str.PadRight(-alignment);
EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a <see cref="ReadOnlySpan{T}"/> value from the interpolated string.</summary>
/// <param name="value">The span to append.</param>
public void AppendFormatted(ReadOnlySpan<char> value)
{
if (value.IsEmpty)
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(value);
}
}
#endif
132 changes: 132 additions & 0 deletions src/NetEvolve.CodeBuilder/CSharpInterpolatedStringHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#if NET6_0_OR_GREATER
namespace NetEvolve.CodeBuilder;

using System.Globalization;
using System.Runtime.CompilerServices;

/// <summary>
/// Custom interpolated string handler for <see cref="CSharpCodeBuilder"/>.
/// </summary>
/// <remarks>
/// This handler is instantiated by the compiler when an interpolated string is passed to
/// <see cref="CSharpCodeBuilder.AppendInterpolated"/> or <see cref="CSharpCodeBuilder.AppendLineInterpolated"/>.
/// It appends each literal and formatted part directly to the builder, applying indentation before
/// the first non-empty part on a new line.
/// </remarks>
[InterpolatedStringHandler]
public ref struct CSharpInterpolatedStringHandler
{
private readonly CSharpCodeBuilder _owner;
private bool _indentEnsured;

/// <summary>
/// Initializes a new instance of the <see cref="CSharpInterpolatedStringHandler"/> struct.
/// </summary>
/// <param name="literalLength">The total length of all literal parts (hint for capacity).</param>
/// <param name="formattedCount">The number of formatted holes in the interpolated string.</param>
/// <param name="builder">The <see cref="CSharpCodeBuilder"/> to append to.</param>
public CSharpInterpolatedStringHandler(int literalLength, int formattedCount, CSharpCodeBuilder builder)
{
_owner = builder;
_indentEnsured = false;
}

private void EnsureIndented()
{
if (!_indentEnsured)
{
_owner.HandlerEnsureIndented();
_indentEnsured = true;
}
}

/// <summary>Appends a literal string part of the interpolated string.</summary>
/// <param name="value">The literal string to append.</param>
public void AppendLiteral(string? value)
{
if (string.IsNullOrEmpty(value))
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(value);
}

/// <summary>Appends a formatted value from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format.</typeparam>
/// <param name="value">The value to append.</param>
public void AppendFormatted<T>(T value)
{
var str = value?.ToString();
if (string.IsNullOrEmpty(str))
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a formatted value with a format string from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format. Must implement <see cref="System.IFormattable"/>.</typeparam>
/// <param name="value">The value to append.</param>
/// <param name="format">The format string.</param>
public void AppendFormatted<T>(T value, string? format)
where T : System.IFormattable
{
var str = value?.ToString(format, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(str))
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a formatted value with alignment from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format.</typeparam>
/// <param name="value">The value to append.</param>
/// <param name="alignment">Minimum width; negative values left-align.</param>
public void AppendFormatted<T>(T value, int alignment)
{
var str = value?.ToString();
if (str is null)
{
return;
}

str = alignment >= 0 ? str.PadLeft(alignment) : str.PadRight(-alignment);
EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a formatted value with alignment and format string from the interpolated string.</summary>
/// <typeparam name="T">The type of the value to format. Must implement <see cref="System.IFormattable"/>.</typeparam>
/// <param name="value">The value to append.</param>
/// <param name="alignment">Minimum width; negative values left-align.</param>
/// <param name="format">The format string.</param>
public void AppendFormatted<T>(T value, int alignment, string? format)
where T : System.IFormattable
{
var str = value?.ToString(format, CultureInfo.InvariantCulture) ?? string.Empty;
str = alignment >= 0 ? str.PadLeft(alignment) : str.PadRight(-alignment);
EnsureIndented();
_owner.HandlerRawAppend(str);
}

/// <summary>Appends a <see cref="ReadOnlySpan{T}"/> value from the interpolated string.</summary>
/// <param name="value">The span to append.</param>
public void AppendFormatted(ReadOnlySpan<char> value)
{
if (value.IsEmpty)
{
return;
}

EnsureIndented();
_owner.HandlerRawAppend(value);
}
}
#endif
Loading
Loading