Skip to content

Commit

Permalink
Add parser to GqlModelGenerator and also GqlModel Namespace options
Browse files Browse the repository at this point in the history
  • Loading branch information
NeonGraal committed Feb 15, 2025
1 parent c38c41e commit cd236f8
Show file tree
Hide file tree
Showing 27 changed files with 300 additions and 705 deletions.
48 changes: 38 additions & 10 deletions src/GqlPlus.Generators/GqlModelGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
using System.Collections.Immutable;
using System.Text;
using System.Threading;
using GqlPlus.Abstractions.Schema;
using GqlPlus.Parsing;
using GqlPlus.Parsing.Schema;
using GqlPlus.Result;
using GqlPlus.Token;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace GqlPlus;

Expand All @@ -10,36 +17,57 @@ public class GqlModelGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
IncrementalValueProvider<GqlModelOptions> gqlModelOptions = context.AnalyzerConfigOptionsProvider.Select(MakeGqlModelOptions);

IncrementalValueProvider<ImmutableArray<AdditionalText>> samples = context.AdditionalTextsProvider
.Where(text => Path.GetExtension(text.Path).Equals(".graphql+", StringComparison.OrdinalIgnoreCase))
.Collect();

context.RegisterSourceOutput(samples, GenerateCode);
context.RegisterSourceOutput(samples.Combine(gqlModelOptions), GenerateCode);
}

private static GqlModelOptions MakeGqlModelOptions(AnalyzerConfigOptionsProvider provider, CancellationToken _)
{
if (!provider.GlobalOptions.TryGetValue("build_property.GqlPlus_BaseNamespace", out string? baseNamespace)) {
baseNamespace = "GqlPlus";
}

return new GqlModelOptions(baseNamespace);
}

private void GenerateCode(SourceProductionContext context, ImmutableArray<AdditionalText> array)
private void GenerateCode(SourceProductionContext context, (ImmutableArray<AdditionalText> Left, GqlModelOptions Right) tuple)
{
if (array.IsDefaultOrEmpty) {
(ImmutableArray<AdditionalText> array, GqlModelOptions? options) = tuple;

if (array.IsDefaultOrEmpty || options is null) {
return;
}

IServiceProvider services = new ServiceCollection()
.AddCommonParsers()
.AddSchemaParsers()
.BuildServiceProvider();

Parser<IGqlpSchema>.L schemaParser = services.GetRequiredService<Parser<IGqlpSchema>.D>();

foreach (AdditionalText text in array) {
string file = Path.GetFileNameWithoutExtension(text.Path);
StringBuilder builder = new("// Generated from ");
builder.AppendLine(text.Path);
builder.AppendLine("\n/*");

TextLineCollection? lines = text.GetText()?.Lines;
string? lines = text.GetText()?.ToString();
if (lines is not null) {
foreach (TextLine line in lines) {
builder.AppendLine(line.ToString());
Tokenizer tokens = new(lines);

IGqlpSchema ast = schemaParser.Parse(tokens, "Schema").Required();
foreach (IGqlpDeclaration item in ast.Declarations) {
builder.AppendLine(item.Label + " - " + item.Name);
}
}

builder.AppendLine("*/\n");
builder.AppendLine("namespace GqlPlus;");
builder.AppendLine();
builder.AppendLine("public class Model_" + file + " {}");
builder.AppendLine($"namespace {options.BaseNamespace}.Model_" + file + " {}");

context.AddSource("Model_" + file + ".gen.cs", builder.ToString());
}
Expand Down
5 changes: 5 additions & 0 deletions src/GqlPlus.Generators/GqlModelGenerator.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<ItemGroup>
<CompilerVisibleProperty Include="GqlPlus_BaseNamespace" />
</ItemGroup>
</Project>
18 changes: 18 additions & 0 deletions src/GqlPlus.Generators/GqlModelOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

namespace GqlPlus;

public sealed class GqlModelOptions(string baseNamespace)
: IEquatable<GqlModelOptions>
{
public string BaseNamespace { get; } = baseNamespace;

public bool Equals(GqlModelOptions other)
=> BaseNamespace.Equals(other?.BaseNamespace, StringComparison.Ordinal);

public override bool Equals(object obj)
=> obj is GqlModelOptions options
? Equals(options)
: base.Equals(obj);
public override int GetHashCode()
=> HashCode.Combine(BaseNamespace);
}
9 changes: 9 additions & 0 deletions src/GqlPlus.Generators/GqlPlus.Generators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\GqlPlus.Parser\GqlPlus.Parser.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="GqlModelGenerator.props" Pack="true" PackagePath="build" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/GqlPlus.Parser/Token/Tokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ static Tokenizer()

internal bool IgnoreSeparators { get; set; }

internal Tokenizer(string operation)
public Tokenizer(string operation)
{
_operation = operation.AsMemory();
_len = _operation.Length;
Expand Down
8 changes: 7 additions & 1 deletion test/GqlPlus.GeneratorsTestBase/TestGeneratorsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using DiffEngine;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace GqlPlus;
Expand All @@ -11,7 +12,7 @@ public static class TestGeneratorsHelper
static TestGeneratorsHelper()
=> DiffRunner.MaxInstancesToLaunch(20);

public static GeneratorDriver Generate(this IIncrementalGenerator generator, ImmutableArray<AdditionalText> additionalPaths)
public static GeneratorDriver Generate(this IIncrementalGenerator generator, ImmutableArray<AdditionalText> additionalPaths, AnalyzerConfigOptionsProvider? configOptions = null)
{
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText("");

Expand All @@ -26,6 +27,11 @@ public static GeneratorDriver Generate(this IIncrementalGenerator generator, Imm
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator)
.AddAdditionalTexts(additionalPaths);

if (configOptions is not null) {
driver = driver.WithUpdatedAnalyzerConfigOptions(configOptions);
}


return driver.RunGenerators(compilation);
}

Expand Down
26 changes: 26 additions & 0 deletions test/GqlPlus.GeneratorsTests/GqlModelConfigOptionsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace GqlPlus;

internal sealed class GqlModelConfigOptionsProvider : AnalyzerConfigOptionsProvider
{
public override GqlModelConfigOptions GlobalOptions { get; }
= new(new() { ["build_property.GqlPlus_BaseNamespace"] = "GqlTest" });

public override GqlModelConfigOptions GetOptions(SyntaxTree tree)
=> GqlModelConfigOptions.Empty;
public override GqlModelConfigOptions GetOptions(AdditionalText textFile)
=> GqlModelConfigOptions.Empty;
}

internal sealed class GqlModelConfigOptions(
Map<string> values
) : AnalyzerConfigOptions
{
public static GqlModelConfigOptions Empty { get; } = new([]);

public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value)
=> values.TryGetValue(key, out value);
}
4 changes: 3 additions & 1 deletion test/GqlPlus.GeneratorsTests/GqlModelGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ public async Task ModelSchema(string sample)
{
string schema = await ReadSchema(sample);

GqlModelConfigOptionsProvider options = new();

GeneratorDriver driver = new GqlModelGenerator()
.Generate((sample + ".graphql+").AdditionalString(schema));
.Generate((sample + ".graphql+").AdditionalString(schema), options);

await Verifier.Verify(driver, CustomSettings("Schema", "Model", sample));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\GqlPlus.Generators\GqlPlus.Generators.csproj" />
<ProjectReference Include="..\..\src\GqlPlus.Generators\GqlPlus.Generators.csproj" PrivateAssets="contentfiles;build" />
<ProjectReference Include="..\GqlPlus.ComponentTestBase\GqlPlus.ComponentTestBase.csproj" />
<ProjectReference Include="..\GqlPlus.GeneratorsTestBase\GqlPlus.GeneratorsTestBase.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,14 @@
//HintName: Model_Intro_Built-In.gen.cs
// Generated from Intro_Built-In.graphql+
/*
output _Constant {
| _Simple
| _ConstantList
| _ConstantMap
}
output _Simple {
| Boolean
| _DomainValue<_DomainKind.Number Number>
| _DomainValue<_DomainKind.String String>
| _EnumValue
}
output _ConstantList {
| _Constant[]
}
output _ConstantMap {
| _Constant[Simple]
}
output _Collections {
| _Modifier<_ModifierKind.List>
| _ModifierKeyed<_ModifierKind.Dictionary>
| _ModifierKeyed<_ModifierKind.TypeParam>
}
output _ModifierKeyed<$kind> {
: _Modifier<$kind>
by: _TypeSimple
optional: Boolean
}
output _Modifiers {
| _Modifier<_ModifierKind.Optional>
| _Collections
}
enum _ModifierKind { Opt[Optional] List Dict[Dictionary] Param[TypeParam] }
output _Modifier<$kind> {
modifierKind: $kind
}
Output - _Constant
Output - _Simple
Output - _ConstantList
Output - _ConstantMap
Output - _Collections
Output - _ModifierKeyed
Output - _Modifiers
Enum - _ModifierKind
Output - _Modifier
*/
namespace GqlPlus;
public class Model_Intro_Built-In {}
namespace GqlTest.Model_Intro_Built-In {}
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
//HintName: Model_Intro_Category.gen.cs
// Generated from Intro_Category.graphql+
/*
output _Categories {
category: _Category
type: _Type
| _Category
| _Type
}
output _Category {
: _Aliased
resolution: _Resolution
output: _TypeRef<_TypeKind.Output>
modifiers: _Modifiers[]
}
enum _Resolution { Parallel Sequential Single }
Output - _Categories
Output - _Category
Enum - _Resolution
*/
namespace GqlPlus;
public class Model_Intro_Category {}
namespace GqlTest.Model_Intro_Category {}
Original file line number Diff line number Diff line change
@@ -1,41 +1,13 @@
//HintName: Model_Intro_Common.gen.cs
// Generated from Intro_Common.graphql+
/*
output _Type {
| _BaseType<_TypeKind.Basic>
| _BaseType<_TypeKind.Internal>
| _TypeDual
| _TypeEnum
| _TypeInput
| _TypeOutput
| _TypeDomain
| _TypeUnion
}
output _BaseType<$kind> {
: _Aliased
typeKind: $kind
}
output _ChildType<$kind $parent> {
: _BaseType<$kind>
parent: $parent
}
output _ParentType<$kind $item $allItem> {
: _ChildType<$kind _Identifier>
items: $item[]
allItems: $allItem[]
}
enum _SimpleKind { Basic Enum Internal Domain Union }
enum _TypeKind { :_SimpleKind Dual Input Output }
output _TypeRef<$kind> {
typeKind: $kind
name: _Identifier
}
output _TypeSimple {
| _TypeRef<_TypeKind.Basic>
| _TypeRef<_TypeKind.Enum>
| _TypeRef<_TypeKind.Domain>
| _TypeRef<_TypeKind.Union>
}
Output - _Type
Output - _BaseType
Output - _ChildType
Output - _ParentType
Enum - _SimpleKind
Enum - _TypeKind
Output - _TypeRef
Output - _TypeSimple
*/
namespace GqlPlus;
public class Model_Intro_Common {}
namespace GqlTest.Model_Intro_Common {}
Loading

0 comments on commit cd236f8

Please sign in to comment.