22using Microsoft . CodeAnalysis . Text ;
33using System ;
44using System . Collections . Generic ;
5+ using System . Collections . Immutable ;
56using System . Linq ;
67using System . Text ;
7- using System . Threading . Tasks ;
88using Microsoft . CodeAnalysis . CSharp . Syntax ;
9- using DotNurse . CodeAnalysis ;
10- using AutoFilterer . Generators . Extensions ;
119using Microsoft . CodeAnalysis . CSharp ;
12- using System . Diagnostics ;
1310
1411namespace AutoFilterer . Generators ;
1512
13+ using Extensions ;
14+
1615[ Generator ]
17- public class FilterGenerator : ISourceGenerator
16+ public class FilterGenerator : IIncrementalGenerator
1817{
19- public void Initialize ( GeneratorInitializationContext context )
18+ public void Initialize ( IncrementalGeneratorInitializationContext context )
2019 {
21- context . RegisterForSyntaxNotifications ( ( ) => new AttributeSyntaxReceiver < GenerateAutoFilterAttribute > ( ) ) ;
20+ var classDeclarations = context . SyntaxProvider
21+ . CreateSyntaxProvider (
22+ predicate : static ( s , _ ) => s is ClassDeclarationSyntax { AttributeLists . Count : > 0 } ,
23+ transform : static ( ctx , _ ) => GetSemanticTargetForGeneration ( ctx ) )
24+ . Where ( static m => m is not null ) ;
25+
26+ var compilationAndClasses = context . CompilationProvider . Combine ( classDeclarations . Collect ( ) ) ;
27+
28+ context . RegisterSourceOutput ( compilationAndClasses , ( spc , source ) => Execute ( source . Left , source . Right , spc ) ) ;
2229 }
2330
24- public void Execute ( GeneratorExecutionContext context )
31+ private static ClassDeclarationSyntax GetSemanticTargetForGeneration ( GeneratorSyntaxContext context )
2532 {
26- if ( ! ( context . SyntaxReceiver is AttributeSyntaxReceiver < GenerateAutoFilterAttribute > receiver ) )
27- return ;
33+ var classDeclaration = ( ClassDeclarationSyntax ) context . Node ;
2834
29- //if (!Debugger.IsAttached)
30- //{
31- // Debugger.Launch();
32- //}
33-
34- foreach ( var classSyntax in receiver . Classes )
35+ foreach ( var attributeList in classDeclaration . AttributeLists )
3536 {
36- var attribute = classSyntax . AttributeLists . SelectMany ( sm => sm . Attributes ) . FirstOrDefault ( x => x . Name . ToString ( ) . EnsureEndsWith ( "Attribute" ) . Equals ( typeof ( GenerateAutoFilterAttribute ) . Name ) ) ;
37- var namespaceParam = attribute . ArgumentList ? . Arguments . FirstOrDefault ( ) ; // Temprorary... Attribute has only one argument for now.
38-
39- var model = context . Compilation . GetSemanticModel ( classSyntax . SyntaxTree ) ;
40- var symbol = model . GetDeclaredSymbol ( classSyntax ) ;
41- var attrs = symbol . GetAttributes ( ) ;
42-
43- var realNamespace = GetNamespaceRecursively ( symbol . ContainingNamespace ) ;
37+ foreach ( var attribute in attributeList . Attributes )
38+ {
39+ var symbolInfo = context . SemanticModel . GetSymbolInfo ( attribute ) ;
40+ var attributeSymbol = symbolInfo . Symbol as IMethodSymbol ;
4441
45- var properties = symbol . GetMembers ( ) . OfType < IPropertySymbol > ( )
46- . Where ( x => ! x . IsStatic && ! x . ContainingType . IsGenericType && x . Kind == SymbolKind . Property ) ;
42+ var fullName = attributeSymbol ? . ContainingType . ToDisplayString ( )
43+ ?? attribute . Name . ToString ( ) ;
4744
48- context . AddSource ( $ "{ symbol . Name } FilterDto.g.cs",
49- SourceText . From ( GetFilterDtoCode ( symbol . Name , properties , namespaceParam ? . ToString ( ) . Trim ( '\" ' ) ?? realNamespace ) , Encoding . UTF8 ) ) ;
45+ if ( fullName . EnsureEndsWith ( "Attribute" ) . EndsWith ( nameof ( GenerateAutoFilterAttribute ) ) )
46+ {
47+ return classDeclaration ;
48+ }
49+ }
5050 }
51+
52+ return null ;
5153 }
5254
53- private static Dictionary < INamedTypeSymbol , IList < AttributeData > > GetClassAttributePairs ( GeneratorExecutionContext context ,
54- SyntaxReceiver receiver )
55+ private static void Execute ( Compilation compilation , ImmutableArray < ClassDeclarationSyntax > classes , SourceProductionContext context )
5556 {
56- var compilation = context . Compilation ;
57- var classSymbols = new Dictionary < INamedTypeSymbol , IList < AttributeData > > ( ) ;
58- foreach ( var clazz in receiver . CandidateClasses )
57+ if ( classes . IsDefaultOrEmpty ) {
58+ return ;
59+ }
60+
61+ foreach ( var classSyntax in classes )
5962 {
60- var model = compilation . GetSemanticModel ( clazz . SyntaxTree ) ;
61- var classSymbol = model . GetDeclaredSymbol ( clazz ) ;
62- var attributes = classSymbol . GetAttributes ( ) ;
63- if ( attributes . Any ( ad => ad . AttributeClass . Name == nameof ( GenerateAutoFilterAttribute ) ) )
63+ var model = compilation . GetSemanticModel ( classSyntax . SyntaxTree ) ;
64+ if ( model . GetDeclaredSymbol ( classSyntax ) is not { } symbol )
6465 {
65- classSymbols . Add ( ( INamedTypeSymbol ) classSymbol , attributes . ToList ( ) ) ;
66+ continue ;
6667 }
67- }
6868
69- return classSymbols ;
69+ var attribute = symbol . GetAttributes ( )
70+ . FirstOrDefault ( a => a . AttributeClass ? . Name == nameof ( GenerateAutoFilterAttribute ) ) ;
71+ var namespaceParam = attribute ? . ConstructorArguments . FirstOrDefault ( ) . Value ? . ToString ( ) . Trim ( '\" ' ) ; // Temprorary... Attribute has only one argument for now.
72+ var realNamespace = GetNamespaceRecursively ( symbol . ContainingNamespace ) ;
73+
74+ var properties = symbol . GetMembers ( ) . OfType < IPropertySymbol > ( )
75+ . Where ( x => ! x . IsStatic && ! x . ContainingType . IsGenericType && x . Kind == SymbolKind . Property ) ;
76+
77+ var sourceCode = GetFilterDtoCode ( symbol . Name , properties , namespaceParam ?? realNamespace ) ;
78+
79+ context . AddSource ( $ "{ symbol . Name } FilterDto.g.cs", SourceText . From ( sourceCode , Encoding . UTF8 ) ) ;
80+ }
7081 }
7182
72- internal string GetFilterDtoCode ( string className , IEnumerable < IPropertySymbol > properties ,
83+ private static string GetFilterDtoCode ( string className , IEnumerable < IPropertySymbol > properties ,
7384 string @namespace = null )
7485 {
7586 var start = $@ "
@@ -88,10 +99,11 @@ public partial class {className}Filter : PaginationFilterBase
8899
89100 foreach ( var property in properties )
90101 {
91- string propertyType = property . Type . ToDisplayString ( NullableFlowState . None ) ;
102+ var propertyType = property . Type . ToDisplayString ( NullableFlowState . None ) ;
92103
93- if ( TypeMapping . Mappings . TryGetValue ( propertyType , out var mapped ) )
104+ if ( TypeMapping . Mappings . TryGetValue ( propertyType , out var mapped ) ) {
94105 propertyType = mapped ;
106+ }
95107
96108 if ( propertyType . Equals ( nameof ( String ) , StringComparison . InvariantCultureIgnoreCase ) )
97109 {
@@ -100,13 +112,10 @@ public partial class {className}Filter : PaginationFilterBase
100112 body . AppendLine ( $ "\t \t public virtual { propertyType } { property . Name } {{ get; set; }}") ;
101113 }
102114
103- var end = "\t }\n }" ;
104-
105-
106- return start + body . ToString ( ) + end ;
115+ return start + body + "\t }\n }" ;
107116 }
108117
109- private string GetNamespaceRecursively ( INamespaceSymbol symbol )
118+ private static string GetNamespaceRecursively ( INamespaceSymbol symbol )
110119 {
111120 if ( symbol . ContainingNamespace == null )
112121 {
@@ -115,4 +124,4 @@ private string GetNamespaceRecursively(INamespaceSymbol symbol)
115124
116125 return ( GetNamespaceRecursively ( symbol . ContainingNamespace ) + "." + symbol . Name ) . Trim ( '.' ) ;
117126 }
118- }
127+ }
0 commit comments