diff --git a/bunit.sln b/bunit.sln index e5b5294d4..f782ca9f9 100644 --- a/bunit.sln +++ b/bunit.sln @@ -62,46 +62,136 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.tests", "tests\bunit. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.generators.tests", "tests\bunit.generators.tests\bunit.generators.tests.csproj", "{D08F7F1D-74B1-4A76-86A2-94918863740C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bunit.anglesharp", "src\bunit.anglesharp\bunit.anglesharp.csproj", "{4591042C-410C-4BA1-A075-653F77891EDA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|x64.Build.0 = Debug|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|x86.Build.0 = Debug|Any CPU {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|Any CPU.Build.0 = Release|Any CPU - {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|x64.ActiveCfg = Release|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|x64.Build.0 = Release|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|x86.ActiveCfg = Release|Any CPU + {1DA6EFDE-81A1-4324-A56C-40BEE14A75BA}.Release|x86.Build.0 = Release|Any CPU {6127D121-9387-451B-B15D-8350A32D3001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Debug|x64.ActiveCfg = Debug|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Debug|x64.Build.0 = Debug|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Debug|x86.ActiveCfg = Debug|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Debug|x86.Build.0 = Debug|Any CPU {6127D121-9387-451B-B15D-8350A32D3001}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Release|x64.ActiveCfg = Release|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Release|x64.Build.0 = Release|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Release|x86.ActiveCfg = Release|Any CPU + {6127D121-9387-451B-B15D-8350A32D3001}.Release|x86.Build.0 = Release|Any CPU {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|x64.ActiveCfg = Debug|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|x64.Build.0 = Debug|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|x86.ActiveCfg = Debug|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Debug|x86.Build.0 = Debug|Any CPU {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|Any CPU.ActiveCfg = Release|Any CPU {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|Any CPU.Build.0 = Release|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|x64.ActiveCfg = Release|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|x64.Build.0 = Release|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|x86.ActiveCfg = Release|Any CPU + {7972A80F-30DC-4EF4-9294-7D4DD2965882}.Release|x86.Build.0 = Release|Any CPU {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|x64.ActiveCfg = Debug|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|x64.Build.0 = Debug|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|x86.ActiveCfg = Debug|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Debug|x86.Build.0 = Debug|Any CPU {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|Any CPU.Build.0 = Release|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|x64.ActiveCfg = Release|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|x64.Build.0 = Release|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|x86.ActiveCfg = Release|Any CPU + {0FF92169-7D8F-46A2-8327-A2F028CB426F}.Release|x86.Build.0 = Release|Any CPU {DE975A0C-0672-4248-913E-D267C1001801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE975A0C-0672-4248-913E-D267C1001801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Debug|x64.ActiveCfg = Debug|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Debug|x64.Build.0 = Debug|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Debug|x86.ActiveCfg = Debug|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Debug|x86.Build.0 = Debug|Any CPU {DE975A0C-0672-4248-913E-D267C1001801}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE975A0C-0672-4248-913E-D267C1001801}.Release|Any CPU.Build.0 = Release|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Release|x64.ActiveCfg = Release|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Release|x64.Build.0 = Release|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Release|x86.ActiveCfg = Release|Any CPU + {DE975A0C-0672-4248-913E-D267C1001801}.Release|x86.Build.0 = Release|Any CPU {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|x64.ActiveCfg = Debug|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|x64.Build.0 = Debug|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|x86.ActiveCfg = Debug|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Debug|x86.Build.0 = Debug|Any CPU {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|Any CPU.Build.0 = Release|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|x64.ActiveCfg = Release|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|x64.Build.0 = Release|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|x86.ActiveCfg = Release|Any CPU + {AE3DFB52-2BF4-4806-AD82-7FB7B38AC17F}.Release|x86.Build.0 = Release|Any CPU {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|x64.Build.0 = Debug|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Debug|x86.Build.0 = Debug|Any CPU {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|Any CPU.Build.0 = Release|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|x64.ActiveCfg = Release|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|x64.Build.0 = Release|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|x86.ActiveCfg = Release|Any CPU + {A7C6A2AA-FF8F-4ED1-8590-5324FC566059}.Release|x86.Build.0 = Release|Any CPU {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|x64.ActiveCfg = Debug|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|x64.Build.0 = Debug|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|x86.ActiveCfg = Debug|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Debug|x86.Build.0 = Debug|Any CPU {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|Any CPU.ActiveCfg = Release|Any CPU {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|Any CPU.Build.0 = Release|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|x64.ActiveCfg = Release|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|x64.Build.0 = Release|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|x86.ActiveCfg = Release|Any CPU + {56889DE7-5E66-4E9C-815B-CBCFC9961612}.Release|x86.Build.0 = Release|Any CPU {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|x64.ActiveCfg = Debug|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|x64.Build.0 = Debug|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Debug|x86.Build.0 = Debug|Any CPU {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|Any CPU.Build.0 = Release|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|x64.ActiveCfg = Release|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|x64.Build.0 = Release|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|x86.ActiveCfg = Release|Any CPU + {D08F7F1D-74B1-4A76-86A2-94918863740C}.Release|x86.Build.0 = Release|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Debug|x64.ActiveCfg = Debug|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Debug|x64.Build.0 = Debug|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Debug|x86.ActiveCfg = Debug|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Debug|x86.Build.0 = Debug|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Release|Any CPU.Build.0 = Release|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Release|x64.ActiveCfg = Release|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Release|x64.Build.0 = Release|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Release|x86.ActiveCfg = Release|Any CPU + {4591042C-410C-4BA1-A075-653F77891EDA}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -116,6 +206,7 @@ Global {A7C6A2AA-FF8F-4ED1-8590-5324FC566059} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} {56889DE7-5E66-4E9C-815B-CBCFC9961612} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520} {D08F7F1D-74B1-4A76-86A2-94918863740C} = {6EA09ED4-B714-4E6F-B0E1-4D987F8AE520} + {4591042C-410C-4BA1-A075-653F77891EDA} = {9A2B3B34-D41C-43E8-BC7D-246BEBE48D59} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {24106918-1C86-4769-BDA6-9C80E64CD260} diff --git a/src/bunit.anglesharp/Directory.Build.props b/src/bunit.anglesharp/Directory.Build.props new file mode 100644 index 000000000..6c80b1779 --- /dev/null +++ b/src/bunit.anglesharp/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + true + + diff --git a/src/bunit/Extensions/Internal/AngleSharpWrapperExtensions.cs b/src/bunit.anglesharp/Extensions/AngleSharpWrapperExtensions.cs similarity index 91% rename from src/bunit/Extensions/Internal/AngleSharpWrapperExtensions.cs rename to src/bunit.anglesharp/Extensions/AngleSharpWrapperExtensions.cs index ffb6f391d..77a019de5 100644 --- a/src/bunit/Extensions/Internal/AngleSharpWrapperExtensions.cs +++ b/src/bunit.anglesharp/Extensions/AngleSharpWrapperExtensions.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using AngleSharp.Dom; namespace Bunit.Web.AngleSharp; @@ -5,7 +6,7 @@ namespace Bunit.Web.AngleSharp; /// /// Extensions for wrapped inside types. /// -internal static class AngleSharpWrapperExtensions +public static class AngleSharpWrapperExtensions { /// /// Unwraps a wrapped AngleSharp object, if it has been wrapped. diff --git a/src/bunit.anglesharp/Placeholder.cs b/src/bunit.anglesharp/Placeholder.cs new file mode 100644 index 000000000..2b88fcebe --- /dev/null +++ b/src/bunit.anglesharp/Placeholder.cs @@ -0,0 +1,7 @@ +// This file is a placeholder to ensure the project compiles +namespace Bunit.Web.AngleSharp +{ + internal static class Placeholder + { + } +} diff --git a/src/bunit.anglesharp/bunit.anglesharp.csproj b/src/bunit.anglesharp/bunit.anglesharp.csproj new file mode 100644 index 000000000..9ffaebade --- /dev/null +++ b/src/bunit.anglesharp/bunit.anglesharp.csproj @@ -0,0 +1,18 @@ + + + net8.0;net9.0;net10.0 + Bunit.Web.AngleSharp + false + disable + + + + + + + + + + + diff --git a/src/bunit.generators.internal/Web.AngleSharp/WrapperElementGenerator.cs b/src/bunit.generators.internal/Web.AngleSharp/WrapperElementGenerator.cs index 81d0039e7..be5968ea6 100644 --- a/src/bunit.generators.internal/Web.AngleSharp/WrapperElementGenerator.cs +++ b/src/bunit.generators.internal/Web.AngleSharp/WrapperElementGenerator.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -10,7 +11,10 @@ internal static class WrapperElementGenerator { internal static string GenerateWrapperTypeSource(StringBuilder source, INamedTypeSymbol elm) { - var name = $"{elm.Name.Substring(1)}Wrapper"; + // Element interface names start with 'I' (e.g., IElement -> ElementWrapper) + var name = elm.Name.Length > 1 && elm.Name.StartsWith("I", StringComparison.Ordinal) + ? $"{elm.Name[1..]}Wrapper" + : $"{elm.Name}Wrapper"; var wrappedTypeName = elm.ToDisplayString(GeneratorConfig.SymbolFormat); source.AppendLine("#nullable enable"); diff --git a/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs b/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs index facb2e810..be0950b2a 100644 --- a/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs +++ b/src/bunit.generators.internal/Web.AngleSharp/WrapperElementsGenerator.cs @@ -1,7 +1,9 @@ +#nullable enable using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text; @@ -14,26 +16,41 @@ public class WrapperElementsGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { // Finds the AngleSharp assembly referenced by the target project - // This should prevent the source generator from running unless a - // new symbol is returned. - var angleSharpAssemblyReference = context + // and collects element interface type names into cacheable records. + var elementInterfaces = context .CompilationProvider .Select((compilation, cancellationToken) => { var meta = compilation.References.FirstOrDefault(x => x.Display?.EndsWith($"{Path.DirectorySeparatorChar}AngleSharp.dll", StringComparison.Ordinal) ?? false); - return compilation.GetAssemblyOrModuleSymbol(meta); + var assembly = compilation.GetAssemblyOrModuleSymbol(meta); + + if (assembly is not IAssemblySymbol angleSharpAssembly) + return null; + + var elementInterfaceTypes = FindElementInterfaces(angleSharpAssembly); + // Create cacheable records with just the essential info needed for generation + // Store metadata names instead of symbols for cacheability + return new ElementInterfacesData( + elementInterfaceTypes.Select(t => new ElementTypeInfo( + t.Name, + t.ToDisplayString(GeneratorConfig.SymbolFormat), + GetMetadataName(t) + )).ToImmutableArray()); }); + // Combine with compilation to retrieve symbols during execution + var elementInterfacesWithCompilation = elementInterfaces.Combine(context.CompilationProvider); + // Output the hardcoded source files - context.RegisterSourceOutput(angleSharpAssemblyReference, GenerateStaticContent); + context.RegisterSourceOutput(elementInterfaces, GenerateStaticContent); // Output the generated wrapper types - context.RegisterSourceOutput(angleSharpAssemblyReference, GenerateWrapperTypes); + context.RegisterSourceOutput(elementInterfacesWithCompilation, GenerateWrapperTypes); } - private static void GenerateStaticContent(SourceProductionContext context, ISymbol assembly) + private static void GenerateStaticContent(SourceProductionContext context, ElementInterfacesData? data) { - if (assembly is not IAssemblySymbol) + if (data is null) return; context.AddSource("IElementWrapperFactory.g.cs", ReadEmbeddedResource("Bunit.Web.AngleSharp.IElementWrapperFactory.cs")); @@ -41,15 +58,28 @@ private static void GenerateStaticContent(SourceProductionContext context, ISymb context.AddSource("WrapperBase.g.cs", ReadEmbeddedResource("Bunit.Web.AngleSharp.WrapperBase.cs")); } - private static void GenerateWrapperTypes(SourceProductionContext context, ISymbol assembly) + private static void GenerateWrapperTypes(SourceProductionContext context, (ElementInterfacesData? data, Compilation compilation) input) { + var (data, compilation) = input; + if (data is null) + return; + + // Find the AngleSharp assembly in the compilation + var meta = compilation.References.FirstOrDefault(x => x.Display?.EndsWith($"{Path.DirectorySeparatorChar}AngleSharp.dll", StringComparison.Ordinal) ?? false); + var assembly = compilation.GetAssemblyOrModuleSymbol(meta); + if (assembly is not IAssemblySymbol angleSharpAssembly) return; - var elementInterfacetypes = FindElementInterfaces(angleSharpAssembly); + // Retrieve the actual symbols from the assembly for code generation + var elementSymbols = data.ElementTypes + .Select(t => angleSharpAssembly.GetTypeByMetadataName(t.MetadataName)) + .Where(s => s is not null) + .Cast() + .ToList(); var source = new StringBuilder(); - foreach (var elm in elementInterfacetypes) + foreach (var elm in elementSymbols) { source.Clear(); var name = WrapperElementGenerator.GenerateWrapperTypeSource(source, elm); @@ -57,11 +87,11 @@ private static void GenerateWrapperTypes(SourceProductionContext context, ISymbo } source.Clear(); - GenerateWrapperFactory(source, elementInterfacetypes); + GenerateWrapperFactory(source, data.ElementTypes); context.AddSource($"WrapperExtensions.g.cs", SourceText.From(source.ToString(), Encoding.UTF8)); } - private static void GenerateWrapperFactory(StringBuilder source, IEnumerable elementInterfacetypes) + private static void GenerateWrapperFactory(StringBuilder source, ImmutableArray elementTypes) { source.AppendLine("""namespace Bunit.Web.AngleSharp;"""); source.AppendLine(); @@ -78,10 +108,13 @@ private static void GenerateWrapperFactory(StringBuilder source, IEnumerable(this global::AngleSharp.Dom.IElement element, TElementFactory elementFactory) where TElementFactory : Bunit.Web.AngleSharp.IElementWrapperFactory => element switch"); source.AppendLine("\t{"); - foreach (var elm in elementInterfacetypes) + foreach (var elm in elementTypes) { - var wrapperName = $"{elm.Name.Substring(1)}Wrapper"; - source.AppendLine($"\t\t{elm.ToDisplayString(GeneratorConfig.SymbolFormat)} e => new {wrapperName}(e, elementFactory),"); + // Element interface names start with 'I' (e.g., IElement -> ElementWrapper) + var wrapperName = elm.Name.Length > 1 && elm.Name.StartsWith("I", StringComparison.Ordinal) + ? $"{elm.Name[1..]}Wrapper" + : $"{elm.Name}Wrapper"; + source.AppendLine($"\t\t{elm.FullyQualifiedName} e => new {wrapperName}(e, elementFactory),"); } source.AppendLine($"\t\t_ => new ElementWrapper(element, elementFactory),"); @@ -90,6 +123,17 @@ private static void GenerateWrapperFactory(StringBuilder source, IEnumerable FindElementInterfaces(IAssemblySymbol angleSharpAssembly) { var htmlDomNamespace = angleSharpAssembly @@ -104,6 +148,9 @@ private static IReadOnlyList FindElementInterfaces(IAssemblySy var elementInterfaceSymbol = angleSharpAssembly .GetTypeByMetadataName("AngleSharp.Dom.IElement"); + if (elementInterfaceSymbol is null) + return Array.Empty(); + var result = htmlDomNamespace .GetTypeMembers() .Where(typeSymbol => typeSymbol.TypeKind == TypeKind.Interface && typeSymbol.AllInterfaces.Contains(elementInterfaceSymbol)) @@ -139,3 +186,13 @@ private static string ReadEmbeddedResource(string resourceName) return reader.ReadToEnd(); } } + +// Cacheable data structure that stores minimal information about element interfaces +// This allows the incremental generator to cache and reuse results across builds +internal sealed record ElementInterfacesData( + ImmutableArray ElementTypes); + +internal sealed record ElementTypeInfo( + string Name, + string FullyQualifiedName, + string MetadataName); diff --git a/src/bunit/bunit.csproj b/src/bunit/bunit.csproj index 3a5511a05..0f2da07d1 100644 --- a/src/bunit/bunit.csproj +++ b/src/bunit/bunit.csproj @@ -44,8 +44,7 @@ - +