@@ -20,7 +20,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
2020 return x . Display ? . EndsWith ( "Microsoft.Extensions.DependencyInjection.Abstractions.dll" ) ?? false ;
2121 } )
2222 . Collect ( )
23- . Select ( ( x , ct ) => x . Any ( ) ) ;
23+ . Select ( ( x , ct ) => x . Length != 0 ) ;
2424
2525 context . RegisterSourceOutput ( hasDependencyInjection , EmitConsoleAppCreateConfigure ) ;
2626
@@ -99,16 +99,20 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
9999 . Select ( ( x , ct ) => new CollectBuilderContext ( x , ct ) )
100100 . WithTrackingName ( "ConsoleApp.Builder.2_Collect" ) ;
101101
102- // WIP
103- //var registerCommands = context.SyntaxProvider.ForAttributeWithMetadataName("ConsoleAppFramework.RegisterCommandsAttribute",
104- // (node, token) => true,
105- // (ctx, token) => ctx)
106- // .Collect()
107- // .Select((x, token) => new CollectBuilderContext(default, token));
102+ var registerCommands = context . SyntaxProvider . ForAttributeWithMetadataName ( "ConsoleAppFramework.RegisterCommandsAttribute" ,
103+ ( node , token ) => true ,
104+ ( ctx , token ) => ctx )
105+ . Collect ( ) ;
108106
109- //var lr = builderSource.Combine(registerCommands); // combine with registerCommands
107+ var combined = builderSource . Combine ( registerCommands )
108+ . Select ( ( tuple , token ) =>
109+ {
110+ var ( context , commands ) = tuple ;
111+ context . AddRegisterAttributes ( commands ) ;
112+ return context ;
113+ } ) ;
110114
111- context . RegisterSourceOutput ( builderSource , EmitConsoleAppBuilder ) ;
115+ context . RegisterSourceOutput ( combined , EmitConsoleAppBuilder ) ;
112116 }
113117
114118 public const string ConsoleAppBaseCode = """
@@ -175,6 +179,22 @@ public CommandAttribute(string command)
175179 }
176180}
177181
182+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
183+ internal sealed class RegisterCommandsAttribute : Attribute
184+ {
185+ public string CommandPath { get; }
186+
187+ public RegisterCommandsAttribute()
188+ {
189+ this.CommandPath = "";
190+ }
191+
192+ public RegisterCommandsAttribute(string commandPath)
193+ {
194+ this.CommandPath = commandPath;
195+ }
196+ }
197+
178198internal static partial class ConsoleApp
179199{
180200 public static IServiceProvider? ServiceProvider { get; set; }
@@ -746,12 +766,14 @@ public bool Equals(CommanContext other)
746766
747767 class CollectBuilderContext : IEquatable < CollectBuilderContext >
748768 {
749- public Command [ ] Commands { get ; } = [ ] ;
769+ public Command [ ] Commands { get ; private set ; } = [ ] ;
750770 public DiagnosticReporter DiagnosticReporter { get ; }
751771 public CancellationToken CancellationToken { get ; }
752772 public bool HasRun { get ; }
753773 public bool HasRunAsync { get ; }
754774
775+ FilterInfo [ ] ? globalFilters { get ; }
776+
755777 public CollectBuilderContext ( ImmutableArray < BuilderContext > contexts , CancellationToken cancellationToken )
756778 {
757779 this . DiagnosticReporter = new DiagnosticReporter ( ) ;
@@ -781,7 +803,7 @@ public CollectBuilderContext(ImmutableArray<BuilderContext> contexts, Cancellati
781803 return x . Name ;
782804 } ) ;
783805
784- var globalFilters = methodGroup [ "UseFilter" ]
806+ globalFilters = methodGroup [ "UseFilter" ]
785807 . OrderBy ( x => x . Node . GetLocation ( ) . SourceSpan ) // sort by line number
786808 . Select ( x =>
787809 {
@@ -805,6 +827,7 @@ public CollectBuilderContext(ImmutableArray<BuilderContext> contexts, Cancellati
805827 // don't emit if exists failure
806828 if ( DiagnosticReporter . HasDiagnostics )
807829 {
830+ globalFilters = null ;
808831 return ;
809832 }
810833
@@ -862,46 +885,46 @@ public CollectBuilderContext(ImmutableArray<BuilderContext> contexts, Cancellati
862885 // from ForAttributeWithMetadataName
863886 public void AddRegisterAttributes ( ImmutableArray < GeneratorAttributeSyntaxContext > contexts )
864887 {
865- if ( contexts . Length == 0 )
888+ if ( contexts . Length == 0 || DiagnosticReporter . HasDiagnostics )
866889 {
867890 return ;
868891 }
869892
870- // var names = new HashSet<string>();
871-
872- // var commands2 = methodGroup["Add<T>"]
873- // .SelectMany(x =>
874- // {
875- // var wellKnownTypes = new WellKnownTypes(x.Model.Compilation) ;
876- // var parser = new Parser(DiagnosticReporter, x.Node, x.Model, wellKnownTypes, DelegateBuildType.None, globalFilters);
877- // var commands = parser.ParseAndValidateForBuilderClassRegistration();
878-
879- // // validation command name duplicate
880- // foreach (var command in commands)
881- // {
882- // if (command != null && !names.Add(command.Name))
883- // {
884- // DiagnosticReporter.ReportDiagnostic(DiagnosticDescriptors.DuplicateCommandName, x.Node.GetLocation(), command!.Name);
885- // return [null] ;
886- // }
887- // }
888-
889- // return commands;
890- // });
891-
892- //if (DiagnosticReporter.HasDiagnostics)
893- //{
894- // return ;
895- // }
896-
897- //// set properties
898- //this.Commands = commands1.Concat(commands2!).ToArray()!; // not null if no diagnostics
899- //this.HasRun = methodGroup["Run"].Any();
900- //this.HasRunAsync = methodGroup["RunAsync"].Any();
901-
902- //var model = contexts[0].Model;
903- //var serviceCollectionSymbol = model.Compilation.GetTypeByMetadataName("Microsoft.Extensions.DependencyInjection.ServiceCollection");
904- //this.HasDependencyInjectionReference = serviceCollectionSymbol != null ;
893+ var names = new HashSet < string > ( Commands . Select ( x => x . Name ) ) ;
894+
895+ var list = new List < Command > ( ) ;
896+ foreach ( var ctx in contexts )
897+ {
898+ string ? commandPath = null ;
899+ var attrData = ctx . Attributes [ 0 ] ; // AllowMultiple = false
900+ if ( attrData . ConstructorArguments . Length != 0 )
901+ {
902+ commandPath = attrData . ConstructorArguments [ 0 ] . Value as string ;
903+ }
904+
905+ var wellKnownTypes = new WellKnownTypes ( ctx . SemanticModel . Compilation ) ;
906+ var parser = new Parser ( DiagnosticReporter , ctx . TargetNode , ctx . SemanticModel , wellKnownTypes , DelegateBuildType . None , globalFilters ?? [ ] ) ;
907+
908+ var commands = parser . CreateCommandsFromType ( ( ITypeSymbol ) ctx . TargetSymbol , commandPath ) ;
909+
910+ foreach ( var command in commands )
911+ {
912+ if ( command != null )
913+ {
914+ if ( ! names . Add ( command . Name ) )
915+ {
916+ DiagnosticReporter . ReportDiagnostic ( DiagnosticDescriptors . DuplicateCommandName , ctx . TargetNode . GetLocation ( ) , command ! . Name ) ;
917+ break ;
918+ }
919+ else
920+ {
921+ list . Add ( command ) ;
922+ }
923+ }
924+ }
925+ }
926+
927+ Commands = Commands . Concat ( list ) . ToArray ( ) ;
905928 }
906929
907930 public bool Equals ( CollectBuilderContext other )
0 commit comments