-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enhancements and fixes in source generators
- Added a new source generator `ConfigurationKeysGenerator` using Roslyn APIs to dynamically generate configuration key classes for annotated classes, including logic for class identification and source code output. - Enhanced documentation in `LiteralGenerator.cs` by adding XML comments to key methods and operators, improving code readability and understanding. - Refined attribute handling in `ParsedGenerator.cs` to include null checks, enhancing code robustness. - Updated `Microsoft.CodeAnalysis.CSharp` to version `4.10.0` in `Radix.Generators.csproj` and commented out an unused property for future use. - Extended `StringExtensions` with a method for capitalizing the first character of a string. - Improved documentation and parameter annotation in `ValidatedGenerator.cs`, ensuring completeness and correctness.
- Loading branch information
1 parent
aafb736
commit 70ed13f
Showing
6 changed files
with
212 additions
and
23 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
| ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Text; | ||
using System.Text; | ||
using System.Diagnostics; | ||
|
||
namespace Radix.Generators; | ||
|
||
|
||
[Generator] | ||
public class ConfigurationKeysGenerator : IIncrementalGenerator | ||
Check warning on line 13 in src/Radix.Generators/ConfigurationKeysGenerator.cs GitHub Actions / Analyze (csharp)
|
||
{ | ||
public void Initialize(IncrementalGeneratorInitializationContext context) | ||
Check warning on line 15 in src/Radix.Generators/ConfigurationKeysGenerator.cs GitHub Actions / Analyze (csharp)
|
||
{ | ||
// Debugger.Launch(); | ||
var classDeclarations = context.SyntaxProvider | ||
.CreateSyntaxProvider( | ||
predicate: (syntaxNode, _) => syntaxNode is ClassDeclarationSyntax classDecl && classDecl.AttributeLists.Count > 0, | ||
transform: (syntaxContext, _) => (classDecl: (ClassDeclarationSyntax)syntaxContext.Node, model: syntaxContext.SemanticModel)) | ||
.Where(pair => HasConfigurationAttribute(pair.classDecl, pair.model)); | ||
|
||
var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); | ||
|
||
context.RegisterSourceOutput(compilationAndClasses, static (sourceProductionContext, source) => | ||
{ | ||
var (compilation, classes) = source; | ||
foreach (var (classSyntax, model) in classes.Distinct()) | ||
{ | ||
var className = classSyntax.Identifier.Text; | ||
var typeSymbol = model.GetDeclaredSymbol(classSyntax); | ||
var namespaceName = typeSymbol?.ContainingNamespace.ToDisplayString() ?? "global"; | ||
var configurationKeysClassName = className + "ConfigurationKeys"; | ||
var hintName = $"{configurationKeysClassName}_{Guid.NewGuid()}.g.cs"; | ||
var sourceCode = GenerateConfigurationKeysClass(compilation, namespaceName, className, configurationKeysClassName, classSyntax, model); | ||
sourceProductionContext.AddSource(hintName, SourceText.From(sourceCode, Encoding.UTF8)); | ||
} | ||
}); | ||
} | ||
|
||
private static bool HasConfigurationAttribute(ClassDeclarationSyntax classSyntax, SemanticModel model) | ||
{ | ||
var classSymbol = model.GetDeclaredSymbol(classSyntax); | ||
return classSymbol is not null ? classSymbol.GetAttributes().Any(ad => ad.AttributeClass is not null ? ad.AttributeClass.ToDisplayString() == "Radix.ConfigurationAttribute" : false) : false; | ||
} | ||
|
||
private static string GenerateConfigurationKeysClass(Compilation compilation, string namespaceName, string className, string configurationKeysClassName, ClassDeclarationSyntax classSyntax, SemanticModel model) | ||
{ | ||
var stringBuilder = new StringBuilder(); | ||
// Header comment indicating the code is auto-generated | ||
stringBuilder.AppendLine("// <auto-generated>"); | ||
stringBuilder.AppendLine("// This code was generated by a tool."); | ||
stringBuilder.AppendLine("// Changes to this file may cause incorrect behavior and will be lost if"); | ||
stringBuilder.AppendLine("// the code is regenerated."); | ||
stringBuilder.AppendLine("// </auto-generated>"); | ||
stringBuilder.AppendLine(); | ||
stringBuilder.AppendLine("using System;"); | ||
stringBuilder.AppendLine("using System.Diagnostics;"); | ||
stringBuilder.AppendLine("using System.CodeDom.Compiler;"); // Include namespace for GeneratedCodeAttribute | ||
stringBuilder.AppendLine(); | ||
stringBuilder.AppendLine($"namespace {namespaceName}"); | ||
stringBuilder.AppendLine("{"); | ||
// Apply DebuggerNonUserCode and GeneratedCodeAttribute to the class | ||
stringBuilder.AppendLine(" [DebuggerNonUserCode]"); | ||
stringBuilder.AppendLine(" [GeneratedCode(\"Radix.Generators.ConfigurationKeysGenerator\", \"1.0\")]"); // Adjust version as appropriate | ||
stringBuilder.AppendLine($" /// <summary>"); | ||
stringBuilder.AppendLine($" /// Provides configuration key paths for the {className} class."); | ||
stringBuilder.AppendLine($" /// </summary>"); | ||
stringBuilder.AppendLine($" public class {configurationKeysClassName}"); | ||
stringBuilder.AppendLine(" {"); | ||
|
||
var classSymbol = model.GetDeclaredSymbol(classSyntax); | ||
|
||
GenerateConfigurationKeysForType(stringBuilder, classSymbol!, className); | ||
|
||
stringBuilder.AppendLine(" }"); | ||
stringBuilder.AppendLine("}"); | ||
string v = stringBuilder.ToString(); | ||
return v; | ||
} | ||
|
||
|
||
private static void GenerateConfigurationKeysForType(StringBuilder stringBuilder, INamedTypeSymbol classSymbol, string parentPath) | ||
{ | ||
foreach (var property in classSymbol.GetMembers().OfType<IPropertySymbol>()) | ||
{ | ||
var propertyName = property.Name; | ||
var propertyPath = $"{parentPath}:{propertyName}"; | ||
|
||
// Add property comment | ||
stringBuilder.AppendLine($" /// <summary>"); | ||
stringBuilder.AppendLine($" /// Configuration key for {propertyName}."); | ||
stringBuilder.AppendLine($" /// </summary>"); | ||
stringBuilder.AppendLine($" public static string {propertyPath.Replace(':', '_')} => \"{propertyPath}\";"); | ||
|
||
if (property.Type.TypeKind == TypeKind.Class && property.Type.SpecialType != SpecialType.System_String) | ||
{ | ||
GenerateConfigurationKeysForType(stringBuilder, (INamedTypeSymbol)property.Type, propertyPath); | ||
} | ||
} | ||
} | ||
|
||
} |
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
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