-
-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[.NET] Removed dependency on System.Text.Json for improved startup ti…
…me (#338)
- Loading branch information
Showing
10 changed files
with
218 additions
and
3,930 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
dotnet/Gherkin.SourceGenerator/Gherkin.SourceGenerator.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<LangVersion>latest</LangVersion> | ||
<Nullable>enable</Nullable> | ||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
<IsRoslynComponent>true</IsRoslynComponent> | ||
<IncludeBuildOutput>false</IncludeBuildOutput> | ||
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<EmbeddedResource Include="..\..\gherkin-languages.json" Link="gherkin-languages.json" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" /> | ||
<!-- Use Newtonsoft because it doesn't need additional dependencies for .NET Standard 2.0 (SourceGenerator) projects --> | ||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" GeneratePathProperty="true" PrivateAssets="all" /> | ||
<PackageReference Include="PolySharp" Version="*"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<Target Name="GetDependencyTargetPaths"> | ||
<!-- Manually include the DLL of each NuGet package that this analyzer uses. --> | ||
<ItemGroup> | ||
<TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" IncludeRuntimeDependency="false" /> | ||
</ItemGroup> | ||
</Target> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace Gherkin.SourceGenerator; | ||
|
||
class GherkinLanguageSetting | ||
{ | ||
public string? Name { get; set; } | ||
public string? Native { get; set; } | ||
public string?[]? Feature { get; set; } | ||
public string?[]? Rule { get; set; } | ||
public string?[]? Background { get; set; } | ||
public string?[]? Scenario { get; set; } | ||
public string?[]? ScenarioOutline { get; set; } | ||
public string?[]? Examples { get; set; } | ||
public string?[]? Given { get; set; } | ||
public string?[]? When { get; set; } | ||
public string?[]? Then { get; set; } | ||
public string?[]? And { get; set; } | ||
public string?[]? But { get; set; } | ||
} |
141 changes: 141 additions & 0 deletions
141
dotnet/Gherkin.SourceGenerator/LanguageDialectGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Text; | ||
using System.Text; | ||
|
||
namespace Gherkin.SourceGenerator; | ||
|
||
[Generator] | ||
public class LanguageDialectGenerator : IIncrementalGenerator | ||
{ | ||
const string GeneratorVersion = "1.0.0"; | ||
record ClassToAddLanguageDialects(string? Namespace, string ClassName); | ||
|
||
public void Initialize(IncrementalGeneratorInitializationContext context) | ||
{ | ||
//System.Diagnostics.Debugger.Launch(); | ||
|
||
context.RegisterPostInitializationOutput(context => context.AddSource( | ||
"LanguageDialectGeneratedAttribute.g.cs", | ||
SourceText.From(""" | ||
[System.AttributeUsage(System.AttributeTargets.Class)] | ||
internal sealed class LanguageDialectGeneratedAttribute : Attribute { } | ||
""", Encoding.UTF8))); | ||
|
||
var pipeline = context.SyntaxProvider.ForAttributeWithMetadataName( | ||
fullyQualifiedMetadataName: "LanguageDialectGeneratedAttribute", | ||
predicate: static (syntaxNode, cancelToken) => syntaxNode is ClassDeclarationSyntax, | ||
transform: static (context, cancelToken) => | ||
{ | ||
var targetSymbol = (INamedTypeSymbol)context.TargetSymbol; | ||
var ns = targetSymbol.ContainingNamespace?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)); | ||
var className = targetSymbol.Name; | ||
return new ClassToAddLanguageDialects(ns, className); | ||
}); | ||
|
||
context.RegisterSourceOutput(pipeline, static (context, classToAdd) => | ||
{ | ||
var allLanguageSettings = LoadLanguageSettings(); | ||
|
||
var sb = new StringBuilder(); | ||
if (classToAdd.Namespace is not null) | ||
sb.AppendLine($"namespace {classToAdd.Namespace};"); | ||
sb.AppendLine($$""" | ||
public partial class {{classToAdd.ClassName}} | ||
{ | ||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Gherkin.SourceGenerator", "{{GeneratorVersion}}")] | ||
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] | ||
private static GherkinDialect TryCreateGherkinDialect(string language) | ||
{ | ||
switch (language) | ||
{ | ||
"""); | ||
foreach (var (language, methodSuffix, _) in allLanguageSettings) | ||
{ | ||
sb.AppendLine($$""" | ||
case {{FormatLiteral(language)}}: | ||
return CreateGherkinDialectFor_{{methodSuffix}}(); | ||
"""); | ||
} | ||
|
||
sb.AppendLine($$""" | ||
default: | ||
return null; | ||
} | ||
} | ||
"""); | ||
foreach (var (language, methodSuffix, languageSettings) in allLanguageSettings) | ||
{ | ||
sb.AppendLine($$""" | ||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Gherkin.SourceGenerator", "{{GeneratorVersion}}")] | ||
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] | ||
private static GherkinDialect CreateGherkinDialectFor_{{methodSuffix}}() | ||
{ | ||
return new GherkinDialect( | ||
{{FormatLiteral(language)}}, | ||
{{FormatListLiteral(languageSettings.Feature)}}, | ||
{{FormatListLiteral(languageSettings.Rule)}}, | ||
{{FormatListLiteral(languageSettings.Background)}}, | ||
{{FormatListLiteral(languageSettings.Scenario)}}, | ||
{{FormatListLiteral(languageSettings.ScenarioOutline)}}, | ||
{{FormatListLiteral(languageSettings.Examples)}}, | ||
{{FormatListLiteral(languageSettings.Given)}}, | ||
{{FormatListLiteral(languageSettings.When)}}, | ||
{{FormatListLiteral(languageSettings.Then)}}, | ||
{{FormatListLiteral(languageSettings.And)}}, | ||
{{FormatListLiteral(languageSettings.But)}}); | ||
} | ||
"""); | ||
} | ||
|
||
sb.AppendLine(@" | ||
}" | ||
); | ||
|
||
context.AddSource($"GherkinDialectProvider.LanguageDialect.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); | ||
}); | ||
} | ||
|
||
static string FormatListLiteral(IEnumerable<string?>? items) | ||
{ | ||
if (items is null) | ||
return "null"; | ||
bool first = true; | ||
var sb = new StringBuilder(); | ||
sb.Append("["); | ||
foreach (var item in items) | ||
{ | ||
if (first) | ||
first = false; | ||
else | ||
sb.Append(", "); | ||
if (item is null) | ||
sb.Append("null"); | ||
else | ||
sb.Append(FormatLiteral(item)); | ||
} | ||
sb.Append("]"); | ||
return sb.ToString(); | ||
} | ||
|
||
static string FormatLiteral(string value) => Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(value, true); | ||
|
||
static List<(string Language, string MethodSuffix, GherkinLanguageSetting Settings)> LoadLanguageSettings() | ||
{ | ||
const string languageFileName = "gherkin-languages.json"; | ||
|
||
var assembly = typeof(LanguageDialectGenerator).Assembly; | ||
var resourceStream = assembly.GetManifestResourceStream("Gherkin.SourceGenerator." + languageFileName); | ||
|
||
if (resourceStream == null) | ||
throw new InvalidOperationException("Gherkin language resource not found: " + languageFileName); | ||
var languagesFileContent = new StreamReader(resourceStream).ReadToEnd(); | ||
|
||
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, GherkinLanguageSetting>>(languagesFileContent); | ||
if (result is null) | ||
throw new InvalidOperationException("Gherkin language resource is empty: " + languageFileName); | ||
return result.OrderBy(x => x.Key).Select(x => (x.Key, x.Key.Replace("-", "_"), x.Value)).ToList(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.