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
Original file line number Diff line number Diff line change
@@ -1,120 +1,129 @@
namespace System.CommandLine.Extensions.SourceGenerators
namespace System.CommandLine.Extensions.SourceGenerators;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Text;

[Generator]
public sealed class CommandLineApplicationOnExecuteMethodsGenerator : IIncrementalGenerator
{
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Text;
private const int NumOverloads = 8;

[Generator]
public sealed class CommandLineApplicationOnExecuteMethodsGenerator : IIncrementalGenerator
{
private const int NumOverloads = 8;
private const string NamespaceName = "System.CommandLine.Extensions";
private const string TypeName = "CommandLineApplication";

private const string NamespaceName = "System.CommandLine.Extensions";
private const string TypeName = "CommandLineApplication";
private static readonly IEnumerable<string> UsingList = new List<string>
{
"Collections.Generic",
"System.Linq",
"System.Threading.Tasks"
};

private static readonly IEnumerable<string> UsingList = new List<string>
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(static ctx =>
{
"Collections.Generic",
"System.CommandLine.Binding",
"System.Linq",
"System.Threading.Tasks"
};
var sourceText = GenerateSource();
ctx.AddSource(nameof(CommandLineApplicationOnExecuteMethodsGenerator), sourceText);
});
}

public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(static ctx =>
{
var sourceText = GenerateSource();
ctx.AddSource(nameof(CommandLineApplicationOnExecuteMethodsGenerator), sourceText);
});
}
private static SourceText GenerateSource()
{
var builder = new StringBuilder();

private static SourceText GenerateSource()
builder.AppendLine($"namespace {NamespaceName}");
builder.AppendBlock(ns =>
{
var builder = new StringBuilder();

builder.AppendLine($"namespace {NamespaceName}");
builder.AppendBlock(ns =>
foreach (var importedNamespace in UsingList.OrderBy(s => s))
{
foreach (var importedNamespace in UsingList.OrderBy(s => s)) ns.AppendLine($"using {importedNamespace};");
ns.AppendLine($"using {importedNamespace};");
}

ns.AppendLine($"partial class {TypeName}");
ns.AppendBlock(t =>
ns.AppendLine($"partial class {TypeName}");
ns.AppendBlock(t =>
{
for (var i = 1; i <= NumOverloads; i++)
{
for (var i = 1; i <= NumOverloads; i++)
{
var methodArgumentsCount = i;
var typeArgumentsString = string.Join(", ", Enumerable.Range(1, methodArgumentsCount).Select(typeParameterIndex => $"T{typeParameterIndex}"));
var methodArgumentsCount = i;
var typeArgumentsString = string.Join(", ", Enumerable.Range(1, methodArgumentsCount).Select(typeParameterIndex => $"T{typeParameterIndex}"));

t.AppendLine($"public void OnExecute<{typeArgumentsString}>(Func<{typeArgumentsString}, Task<int>> execute)");
t.AppendBlock(methodBody =>
t.AppendLine($"public void OnExecute<{typeArgumentsString}>(Func<{typeArgumentsString}, Task<int>> execute)");
t.AppendBlock(methodBody =>
{
for (var j = 0; j < methodArgumentsCount; j++)
{
methodBody.AppendLine("IReadOnlyList<Option> options = this.command.Options.ToList();");
var parameterIndex = j + 1;
methodBody.AppendLine($"var option{parameterIndex} = this.FindValueDescriptorForOption<T{parameterIndex}>({j});");
}

var parameters = new List<string>();
methodBody.AppendLine("this.command.SetAction(async (parseResult, token) =>");
methodBody.AppendBlock(actionBody =>
{
var values = new List<string>();
for (var j = 0; j < methodArgumentsCount; j++)
{
var parameterIndex = j + 1;
var variableName = $"descriptor{parameterIndex}";
methodBody.AppendLine($"IValueDescriptor<T{parameterIndex}> {variableName} = this.FindValueDescriptorForOption<T{parameterIndex}>({j});");
parameters.Add(variableName);
actionBody.AppendLine($"var value{parameterIndex} = parseResult.GetValue(option{parameterIndex});");
values.Add($"value{parameterIndex}!");
}

var parametersString = string.Join(", ", parameters);
methodBody.AppendLine($"this.command.SetHandler(execute, {parametersString});");
var parametersString = string.Join(", ", values);
actionBody.AppendLine($"return await execute({parametersString});");
});
}
});
methodBody.AppendLine(");");
});
}
});

ns.AppendLine("partial class CommandOptionExtensions");
ns.AppendBlock(t =>
ns.AppendLine("partial class CommandOptionExtensions");
ns.AppendBlock(t =>
{
for (var i = 1; i <= NumOverloads; i++)
{
for (var i = 1; i <= NumOverloads; i++)
{
var methodArgumentsCount = i;
var typeArgumentsString = string.Join(", ", Enumerable.Range(1, methodArgumentsCount).Select(typeParameterIndex => $"T{typeParameterIndex}"));
var methodArgumentsCount = i;
var typeArgumentsString = string.Join(", ", Enumerable.Range(1, methodArgumentsCount).Select(typeParameterIndex => $"T{typeParameterIndex}"));

t.AppendLine($"public static void OnExecute<{typeArgumentsString}>(this CommandOption commandOption, Func<{typeArgumentsString}, Task<int>> execute) => commandOption.Command.OnExecute(execute);");
}
});
t.AppendLine($"public static void OnExecute<{typeArgumentsString}>(this CommandOption commandOption, Func<{typeArgumentsString}, Task<int>> execute) => commandOption.Command.OnExecute(execute);");
}
});
});

return SourceText.From(builder.ToString(), Encoding.UTF8);
}
return SourceText.From(builder.ToString(), Encoding.UTF8);
}
}

internal static class StringBuilderExtensions
internal static class StringBuilderExtensions
{
public static void AppendBlock(this StringBuilder builder, Action<IndentedStringWriter> blockWriter)
{
public static void AppendBlock(this StringBuilder builder, Action<IndentedStringWriter> blockWriter)
{
builder.AppendLine("{");
var writer = new IndentedStringWriter(builder, 1);
blockWriter(writer);
builder.AppendLine("}");
}
builder.AppendLine("{");
var writer = new IndentedStringWriter(builder, 1);
blockWriter(writer);
builder.AppendLine("}");
}
}

internal sealed class IndentedStringWriter(StringBuilder stringBuilder, int indentionLevel)
{
private readonly string indentionString = new(' ', indentionLevel * 4);
internal sealed class IndentedStringWriter(StringBuilder stringBuilder, int indentionLevel)
{
private readonly string indentionString = new(' ', indentionLevel * 4);

public void AppendBlock(Action<IndentedStringWriter> blockWriter)
{
stringBuilder.Append(this.indentionString);
stringBuilder.AppendLine("{");
public void AppendBlock(Action<IndentedStringWriter> blockWriter)
{
stringBuilder.Append(this.indentionString);
stringBuilder.AppendLine("{");

var writer = new IndentedStringWriter(stringBuilder, indentionLevel + 1);
blockWriter(writer);
var writer = new IndentedStringWriter(stringBuilder, indentionLevel + 1);
blockWriter(writer);

stringBuilder.Append(this.indentionString);
stringBuilder.AppendLine("}");
stringBuilder.AppendLine();
}
stringBuilder.Append(this.indentionString);
stringBuilder.AppendLine("}");
stringBuilder.AppendLine();
}

public void AppendLine(string text)
{
stringBuilder.Append(this.indentionString);
stringBuilder.AppendLine(text);
}
public void AppendLine(string text)
{
stringBuilder.Append(this.indentionString);
stringBuilder.AppendLine(text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.6.0"/>
</ItemGroup>

</Project>
Loading