Editor/Projection/CSharp/CSharpApplier.cs
using System.Collections.Generic;
using System.Text;
namespace Grains.RazorDesigner.Projection.CSharp;
public static class CSharpApplier
{
private const string IndentUnit = " "; // 4 spaces
// Apply the full op list to a fresh StringBuilder and return the file text.
public static string Apply( IReadOnlyList<CSharpOp> ops )
{
var sb = new StringBuilder();
int indent = 0;
foreach ( var op in ops )
ApplyOp( sb, ref indent, op );
return sb.ToString();
}
public static void ApplyOp( StringBuilder sb, ref int indent, CSharpOp op )
{
switch ( op )
{
case HeaderBanner h:
sb.AppendLine( "// <auto-generated by Grains.RazorDesigner — DO NOT EDIT —" );
sb.AppendLine( $"// source-of-truth: {h.ClassName}.razor.json" );
sb.AppendLine( $"// hand-author additions in {h.ClassName}.User.cs (partial sibling)." );
sb.AppendLine( "// />" );
sb.AppendLine( "#nullable disable" );
break;
case UsingDirective u:
sb.AppendLine( $"using {u.Namespace};" );
break;
case NamespaceOpen n:
sb.AppendLine();
sb.AppendLine( $"namespace {n.Namespace};" );
sb.AppendLine();
break;
case ClassOpen c:
sb.AppendLine( Indent( indent ) + $"public partial class {c.ClassName} : {c.BaseClass}" );
sb.AppendLine( Indent( indent ) + "{" );
indent++;
break;
case ClassClose:
indent--;
sb.AppendLine( Indent( indent ) + "}" );
break;
case FieldDecl f when f.IsParameter:
sb.AppendLine( Indent( indent ) +
$"[Parameter] public {f.Type} {f.Name} {{ get; set; }} = {f.InitialExpr};" );
break;
case FieldDecl f when f.IsProperty:
sb.AppendLine( Indent( indent ) +
$"{f.Visibility} {f.Type} {f.Name} {{ get; set; }} = {f.InitialExpr};" );
break;
case FieldDecl f:
sb.AppendLine( Indent( indent ) +
$"{f.Visibility} {f.Type} {f.Name} = {f.InitialExpr};" );
break;
case MethodOpen m:
{
var overrideKw = m.IsOverride ? "override " : "";
var asyncKw = m.IsAsync ? "async " : "";
sb.AppendLine();
sb.AppendLine( Indent( indent ) +
$"{m.Visibility} {overrideKw}{asyncKw}{m.ReturnType} {m.Name}( {m.ParameterList} )" );
sb.AppendLine( Indent( indent ) + "{" );
indent++;
break;
}
case MethodClose:
indent--;
sb.AppendLine( Indent( indent ) + "}" );
break;
case Statement s:
sb.AppendLine( Indent( indent ) + s.Code + ";" );
break;
case BlockOpen b:
sb.AppendLine( Indent( indent ) + b.Header );
sb.AppendLine( Indent( indent ) + "{" );
indent++;
break;
case BlockClose:
indent--;
sb.AppendLine( Indent( indent ) + "}" );
break;
case BlankLine:
sb.AppendLine();
break;
case Comment cm:
sb.AppendLine( Indent( indent ) + "// " + cm.Text );
break;
default:
throw new System.Diagnostics.UnreachableException(
$"CSharpApplier: unhandled CSharpOp variant '{op?.GetType().Name}'. " +
"Add the arm here AND add the variant to CSharpOpExhaustivenessTest's inline array in the same commit." );
}
}
private static string Indent( int level ) =>
level <= 0 ? "" : new string( ' ', level * 4 );
// Used by CSharpOpExhaustivenessTest.
public static void ApplyOpToScratch( CSharpOp op )
{
var sb = new StringBuilder();
int indent = 0;
ApplyOp( sb, ref indent, op );
}
}