From 68929877d8aebaa5ce10c1be9b9bae7370ce6ab4 Mon Sep 17 00:00:00 2001 From: tesar-tech Date: Fri, 22 Nov 2024 11:39:45 +0100 Subject: [PATCH] Properties and method from inheritance chain. Making it work with extensions. --- .../Components/ComponentApiDocs.razor | 4 +- .../Docs/Components/Texts/TextEditPage.razor | 2 +- .../Extensions/Snackbar/SnackbarPage.razor | 3 + .../Blazorise.Snackbar.csproj | 2 + .../Blazorise.ApiDocsDtos/ApiDocsDtos.cs | 66 ++++++++++- .../Blazorise.ApiDocsDtos.csproj | 1 - .../ComponentsApiDocsSource.cs | 45 +++++++ .../ComponentsApiDocsGenerator.cs | 112 ++++++++++++------ .../Dtos/ApiDocsDtos.cs | 8 +- .../Extensions/Extensions.cs | 2 +- 10 files changed, 199 insertions(+), 46 deletions(-) create mode 100644 Source/SourceGenerators/Blazorise.ApiDocsDtos/ComponentsApiDocsSource.cs diff --git a/Documentation/Blazorise.Docs/Components/ComponentApiDocs.razor b/Documentation/Blazorise.Docs/Components/ComponentApiDocs.razor index 3553d19398..c3f24c6a31 100644 --- a/Documentation/Blazorise.Docs/Components/ComponentApiDocs.razor +++ b/Documentation/Blazorise.Docs/Components/ComponentApiDocs.razor @@ -3,7 +3,7 @@ @{ - IEnumerable apiDocsForComponents = ComponentTypes.Select(x => ComponentsApiDocsSource.Components.GetValueOrDefault(x)).Where(x => x is not null); + IEnumerable apiDocsForComponents = ComponentTypes.Select(x => ComponentsApiDocsSource.Instance.Components.GetValueOrDefault(x)).Where(x => x is not null); var hasComponentTypes = ComponentTypes.Any(); var multipleComponentTypes = ComponentTypes.Count > 1; var hasMethods = apiDocsForComponents.Any(x => x.Methods.Any()); @@ -58,7 +58,7 @@ @foreach (ApiDocsForComponentMethod method in apiDocsForComponent.Methods) { $"{x.TypeName} {x.Name}") ))"> - @method.Description + @((MarkupString)method.Description) } diff --git a/Documentation/Blazorise.Docs/Pages/Docs/Components/Texts/TextEditPage.razor b/Documentation/Blazorise.Docs/Pages/Docs/Components/Texts/TextEditPage.razor index 1f1ad02593..bc3ce66de7 100644 --- a/Documentation/Blazorise.Docs/Pages/Docs/Components/Texts/TextEditPage.razor +++ b/Documentation/Blazorise.Docs/Pages/Docs/Components/Texts/TextEditPage.razor @@ -175,7 +175,7 @@ When working with email and password fields, in some cases browsers might automatically autofill them with the values from user system. To prevent it you can define an autocomplete attribute, eg. autocomplete="new-password" on an input field. - + API diff --git a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/Snackbar/SnackbarPage.razor b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/Snackbar/SnackbarPage.razor index 1e2fd965bc..a54e2cba61 100644 --- a/Documentation/Blazorise.Docs/Pages/Docs/Extensions/Snackbar/SnackbarPage.razor +++ b/Documentation/Blazorise.Docs/Pages/Docs/Extensions/Snackbar/SnackbarPage.razor @@ -84,6 +84,9 @@ + + + API diff --git a/Source/Extensions/Blazorise.Snackbar/Blazorise.Snackbar.csproj b/Source/Extensions/Blazorise.Snackbar/Blazorise.Snackbar.csproj index 941c24466a..17b86ade2a 100644 --- a/Source/Extensions/Blazorise.Snackbar/Blazorise.Snackbar.csproj +++ b/Source/Extensions/Blazorise.Snackbar/Blazorise.Snackbar.csproj @@ -13,6 +13,8 @@ + + diff --git a/Source/SourceGenerators/Blazorise.ApiDocsDtos/ApiDocsDtos.cs b/Source/SourceGenerators/Blazorise.ApiDocsDtos/ApiDocsDtos.cs index 0b06d79713..f8053f5e20 100644 --- a/Source/SourceGenerators/Blazorise.ApiDocsDtos/ApiDocsDtos.cs +++ b/Source/SourceGenerators/Blazorise.ApiDocsDtos/ApiDocsDtos.cs @@ -7,17 +7,73 @@ namespace Blazorise; /// This almost keeps parity with ApiDocsDtos in sg project. public class ApiDocsForComponent { - public ApiDocsForComponent( Type type,string typeName, List properties, List methods ) + public ApiDocsForComponent( Type type,string typeName, List properties, List methods, List inheritsFromChain) { Type = type; TypeName = typeName; - Properties = properties; - Methods = methods; + OwnProperties = properties; + OwnMethods = methods; + InheritsFromChain = inheritsFromChain; } public Type Type { get; set; } public string TypeName { get; } - public List Properties { get; set; } - public List Methods { get; set; } + + /// + /// properties form the component and its parents + /// + public List Properties + { + get + { + if(_properties!=null) return _properties; + _properties = new List(); + _properties.AddRange(OwnProperties); + foreach ( Type typeInheritFrom in InheritsFromChain ) + { + bool success = ComponentsApiDocsSource.Instance.Components.TryGetValue(typeInheritFrom, out var component); + if(!success || component is null) continue; + _properties.AddRange(component.OwnProperties); + } + return _properties; + } + } + + private List? _properties; + + /// + /// only properties from the component, not from parents + /// + public List OwnProperties { get; set; } + /// + /// Methods from the component and its parents + /// + public List Methods + { + get + { + if (_methods != null) return _methods; + _methods = new List(); + _methods.AddRange(OwnMethods); + foreach (Type typeInheritFrom in InheritsFromChain) + { + bool success = ComponentsApiDocsSource.Instance.Components.TryGetValue(typeInheritFrom, out var component); + if (!success || component is null) continue; + _methods.AddRange(component.OwnMethods); + } + return _methods; + } + } + + private List? _methods; + + /// + /// Only methods from the component, not from parents + /// + public List OwnMethods { get; set; } + + + //chain of inherited classes from component to BaseComponent + public List InheritsFromChain { get; set; } } diff --git a/Source/SourceGenerators/Blazorise.ApiDocsDtos/Blazorise.ApiDocsDtos.csproj b/Source/SourceGenerators/Blazorise.ApiDocsDtos/Blazorise.ApiDocsDtos.csproj index d8a0638386..4f7696045d 100644 --- a/Source/SourceGenerators/Blazorise.ApiDocsDtos/Blazorise.ApiDocsDtos.csproj +++ b/Source/SourceGenerators/Blazorise.ApiDocsDtos/Blazorise.ApiDocsDtos.csproj @@ -7,7 +7,6 @@ - true diff --git a/Source/SourceGenerators/Blazorise.ApiDocsDtos/ComponentsApiDocsSource.cs b/Source/SourceGenerators/Blazorise.ApiDocsDtos/ComponentsApiDocsSource.cs new file mode 100644 index 0000000000..749c0ecf68 --- /dev/null +++ b/Source/SourceGenerators/Blazorise.ApiDocsDtos/ComponentsApiDocsSource.cs @@ -0,0 +1,45 @@ +namespace Blazorise; + + +public interface IComponentsApiDocsSource +{ + Dictionary Components { get; } +} + +public class ComponentsApiDocsSource +{ + public Dictionary Components { get; } + + private static readonly Lazy instance = new(() => new ComponentsApiDocsSource()); + public static ComponentsApiDocsSource Instance => instance.Value; + + private ComponentsApiDocsSource() + { + Components = new Dictionary(); + + // Find all types implementing IComponentsApiDocsSource + var sources = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(assembly => assembly.GetTypes()) + .Where(type => typeof(IComponentsApiDocsSource).IsAssignableFrom(type) && type is { IsInterface: false, IsAbstract: false }); + + foreach (var sourceType in sources) + { + // Create an instance of the source type + if ( Activator.CreateInstance( sourceType ) is not IComponentsApiDocsSource sourceInstance ) + continue; + // Merge Components dictionary + foreach (var component in sourceInstance.Components) + { + if (!Components.ContainsKey(component.Key)) + { + Components[component.Key] = component.Value; + } + else + { + // Handle duplicates if needed, e.g., log or overwrite + } + } + } + } +} + diff --git a/Source/SourceGenerators/Blazorise.ApiDocsGenerator/ComponentsApiDocsGenerator.cs b/Source/SourceGenerators/Blazorise.ApiDocsGenerator/ComponentsApiDocsGenerator.cs index ae6fb34ca8..7658873222 100644 --- a/Source/SourceGenerators/Blazorise.ApiDocsGenerator/ComponentsApiDocsGenerator.cs +++ b/Source/SourceGenerators/Blazorise.ApiDocsGenerator/ComponentsApiDocsGenerator.cs @@ -11,22 +11,24 @@ namespace Blazorise.ApiDocsGenerator; + + [Generator] public class ComponentsApiDocsGenerator : IIncrementalGenerator { public void Initialize( IncrementalGeneratorInitializationContext context ) { var componentProperties = context.CompilationProvider - .Select( ( compilation, _ ) => ( compilation, components: GetComponentProperties( compilation ).ToImmutableArray() ) ); + .Select( ( compilation, _ ) => ( compilation, components: GetComponentProperties( compilation,GetNamespaceToSearch( compilation) ).ToImmutableArray() ) ); context.RegisterSourceOutput( componentProperties, ( ctx, source ) => { Logger.LogAlways( DateTime.Now.ToLongTimeString() ); - var (compilation, components) = source; - var sourceText = GenerateComponentsApiSource( compilation, components ); + INamespaceSymbol namespaceToSearch = GetNamespaceToSearch( compilation); + var sourceText = GenerateComponentsApiSource( compilation, components, namespaceToSearch ); ctx.AddSource( "ComponentsApiSource.g.cs", SourceText.From( sourceText, Encoding.UTF8 ) ); ctx.AddSource( "Log.txt", SourceText.From( Logger.LogMessages, Encoding.UTF8 ) ); @@ -34,8 +36,27 @@ public void Initialize( IncrementalGeneratorInitializationContext context ) } ); } - private static IEnumerable<(INamedTypeSymbol ComponentType, IEnumerable Properties, IEnumerable Methods)> GetComponentProperties( Compilation compilation ) + private INamespaceSymbol GetNamespaceToSearch( Compilation compilation ) { + var blazoriseNamespace = compilation.GlobalNamespace + .GetNamespaceMembers() + .FirstOrDefault( ns => ns.Name == $"Blazorise" ); + + + if ( blazoriseNamespace is null ) return null; + + var namespaceToSearch = compilation.Assembly.Name == "Blazorise" ? blazoriseNamespace + : blazoriseNamespace.GetNamespaceMembers().FirstOrDefault( ns => ns.Name == compilation.Assembly.Name.Split( '.' ).Last() ); + + return namespaceToSearch; + + } + + private static IEnumerable< FoundComponent> GetComponentProperties( Compilation compilation, INamespaceSymbol namespaceToSearch ) + { + + Logger.LogAlways( $"Local Namespace:{compilation.Assembly.Name} " ); + var parameterAttributeSymbol = compilation.GetTypeByMetadataName( "Microsoft.AspNetCore.Components.ParameterAttribute" ); if ( parameterAttributeSymbol == null ) yield break; @@ -44,17 +65,20 @@ public void Initialize( IncrementalGeneratorInitializationContext context ) if ( baseComponentSymbol == null ) yield break; - var blazoriseNamespace = compilation.GlobalNamespace - .GetNamespaceMembers() - .FirstOrDefault( ns => ns.Name == "Blazorise" ); - - if ( blazoriseNamespace is null ) + + Logger.LogAlways( $"To look for {namespaceToSearch} " ); + if ( namespaceToSearch is null ) yield break; - foreach ( var type in blazoriseNamespace.GetTypeMembers().OfType() ) + foreach ( var type in namespaceToSearch.GetTypeMembers().OfType() ) { - if ( type.TypeKind != TypeKind.Class || !InheritsFrom( type, baseComponentSymbol ) ) + + if ( type.TypeKind != TypeKind.Class ) continue; + var (inheritsFromBaseComponent, inheritsFromChain) = InheritsFrom( type, baseComponentSymbol ) ; + if(!inheritsFromBaseComponent) continue; + + Logger.LogAlways( $"Type {type.Name} base {type.BaseType}" ); var parameterProperties = type .GetMembers() .OfType() @@ -70,45 +94,52 @@ public void Initialize( IncrementalGeneratorInitializationContext context ) m.MethodKind == MethodKind.Ordinary ); - yield return ( type, parameterProperties, publicMethods ); + yield return new FoundComponent + ( + Type : type, + PublicMethods : publicMethods, + Properties : parameterProperties, + InheritsFromChain : inheritsFromChain + ); } } - private static bool InheritsFrom( INamedTypeSymbol type, INamedTypeSymbol baseType ) + private static (bool, IEnumerable) InheritsFrom( INamedTypeSymbol type, INamedTypeSymbol baseType ) { + List inheritsFromChain = []; while ( type != null ) { if ( SymbolEqualityComparer.Default.Equals( type.BaseType, baseType ) ) - return true; + return ( true, inheritsFromChain ); type = type.BaseType; + inheritsFromChain.Add( type ); } - return false; + return ( false, [] ); } - private static string GenerateComponentsApiSource( Compilation compilation, ImmutableArray<(INamedTypeSymbol ComponentType, IEnumerable Properties, IEnumerable Methods)> components ) - + private static string GenerateComponentsApiSource( Compilation compilation, ImmutableArray components, INamespaceSymbol namespaceToSearch ) { + Logger.LogAlways( components.Count().ToString() ); IEnumerable componentsData = components.Select( component => { - // var componentType = component.ComponentType; - string componentType = component.ComponentType.ToStringWithGenerics(); - string componentTypeName = OtherHelpers.GetSimplifiedTypeName( component.ComponentType ); - Logger.IsOn = component.ComponentType.Name == "Button"; - Logger.Log( component.ComponentType.Name ); + string componentType = component.Type.ToStringWithGenerics(); + string componentTypeName = OtherHelpers.GetSimplifiedTypeName( component.Type ); + Logger.IsOn = component.Type.Name == "Button"; + Logger.Log( component.Type.Name ); var propertiesData = component.Properties.Select( property => InfoExtractor.GetPropertyDetails( compilation, property ) ); - if ( component.Methods.Any() ) - Logger.LogAlways( $"{component.Methods.Count()} commp . {component.ComponentType.Name}" ); - var methodsData = component.Methods.Select( method => + + var methodsData = component.PublicMethods.Select( method => InfoExtractor.GetMethodDetails( compilation, method ) ); ApiDocsForComponent comp = new(type: componentType, typeName: componentTypeName, - properties: propertiesData, methods: methodsData); - + properties: propertiesData, methods: methodsData, + inheritsFromChain: component.InheritsFromChain.Select( type => type.ToStringWithGenerics() ) ); + return comp; } ); @@ -123,9 +154,10 @@ private static string GenerateComponentsApiSource( Compilation compilation, Immu namespace Blazorise.Docs; - public static class ComponentsApiDocsSource + public class ComponentApiSource_ForNamespace_{{namespaceToSearch.Name}}:IComponentsApiDocsSource { - public static readonly Dictionary Components = new Dictionary + public Dictionary Components { get; } = + new Dictionary { {{componentsData.Where( comp => comp is not null ).Select( comp => { @@ -138,7 +170,7 @@ public static class ComponentsApiDocsSource $""" new ("{prop.Name}",typeof({prop.Type}), "{prop.TypeName}", {prop.DefaultValue},{prop.DefaultValueString}, "{prop.Description}", {( prop.IsBlazoriseEnum ? "true" : "false" )}), - """ ).StringJoin( "\n" ) + """ ).StringJoin( " " ) }}}, new List{ {{ @@ -152,12 +184,14 @@ public static class ComponentsApiDocsSource $""" new ("{param.Name}","{param.TypeName}" ), """ - ).StringJoin( "\n" ) + ).StringJoin( " " ) }} }), - """ ).StringJoin( "\n" ) - }} - - } + """ ).StringJoin( " " ) + }} + }, + new List{ + {{comp.InheritsFromChain.Select(x=> $"typeof({x})").StringJoin(",") }} + } )}, """; @@ -167,4 +201,12 @@ public static class ComponentsApiDocsSource } """; } +} + +public record FoundComponent( INamedTypeSymbol Type, IEnumerable Properties, IEnumerable PublicMethods, IEnumerable InheritsFromChain ) +{ + public INamedTypeSymbol Type { get; } = Type; + public IEnumerable Properties { get; } = Properties; + public IEnumerable PublicMethods { get; } = PublicMethods; + public IEnumerable InheritsFromChain { get; } = InheritsFromChain; } \ No newline at end of file diff --git a/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Dtos/ApiDocsDtos.cs b/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Dtos/ApiDocsDtos.cs index 965e6ad6a2..ee408b0b4f 100644 --- a/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Dtos/ApiDocsDtos.cs +++ b/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Dtos/ApiDocsDtos.cs @@ -10,12 +10,16 @@ namespace Blazorise.ApiDocsGenerator.Dtos; /// public class ApiDocsForComponent { - public ApiDocsForComponent( string type, string typeName, IEnumerable properties, IEnumerable methods ) + public ApiDocsForComponent( string type, string typeName, + IEnumerable properties, + IEnumerable methods, + IEnumerable inheritsFromChain ) { Type = type; TypeName = typeName; Properties = properties; Methods = methods; + InheritsFromChain = inheritsFromChain; } //here it is different @@ -24,6 +28,8 @@ public ApiDocsForComponent( string type, string typeName, IEnumerable Properties { get; set; } public IEnumerable Methods { get; set; } + + public IEnumerable InheritsFromChain { get; set; } } public class ApiDocsForComponentProperty diff --git a/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Extensions/Extensions.cs b/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Extensions/Extensions.cs index f818ff8f77..9ca5455bc1 100644 --- a/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Extensions/Extensions.cs +++ b/Source/SourceGenerators/Blazorise.ApiDocsGenerator/Extensions/Extensions.cs @@ -18,7 +18,7 @@ public static string StringJoin(this IEnumerable source, string separato { try { - return source == null ? "" : string.Join(" ", source); + return source == null ? "" : string.Join(separator, source); } catch ( Exception e ) {