diff --git a/.github/workflows/runtests.yml b/.github/workflows/runtests.yml index 8499c6fcf..92486bc38 100644 --- a/.github/workflows/runtests.yml +++ b/.github/workflows/runtests.yml @@ -22,7 +22,7 @@ jobs: with: fetch-depth: 0 # needed for GitVersioning to work - name: Setup NuGet - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v2 - name: Cache NuGet packages uses: actions/cache@v4 id: cache @@ -61,7 +61,7 @@ jobs: with: fetch-depth: 0 - name: Setup NuGet - uses: NuGet/setup-nuget@v1 + uses: NuGet/setup-nuget@v2 - name: Cache NuGet packages uses: actions/cache@v4 id: cache diff --git a/.gitignore b/.gitignore index d2ada0cd0..3bc552dc3 100644 --- a/.gitignore +++ b/.gitignore @@ -361,3 +361,5 @@ Tests/W3CTestSuite/images Tests/W3CTestSuite/png Tests/W3CTestSuite/resources Tests/W3CTestSuite/svg + +Source/Generated/**/*.cs diff --git a/Generators/AvailableElementsGenerator.cs b/Generators/AvailableElementsGenerator.cs index 8c11bff9b..ee206cb27 100644 --- a/Generators/AvailableElementsGenerator.cs +++ b/Generators/AvailableElementsGenerator.cs @@ -5,7 +5,6 @@ using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace Svg.Generators @@ -26,78 +25,6 @@ public class AvailableElementsGenerator : ISourceGenerator DiagnosticSeverity.Error, isEnabledByDefault: true); - #region Model - - /// - /// The object model used to generate SvgElements descriptors. - /// - private const string ModelText = @"// -#nullable disable -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; - -namespace Svg -{ - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] - public sealed class ElementFactoryAttribute : Attribute - { - } - - internal enum DescriptorType - { - Property, - Event - } - - internal interface ISvgPropertyDescriptor - { - DescriptorType DescriptorType { get; } - string AttributeName { get; } - string AttributeNamespace { get; } - TypeConverter Converter { get; } - Type Type { get; } - object GetValue(object component); - void SetValue(object component, ITypeDescriptorContext context, CultureInfo culture, object value); - } - - internal class SvgPropertyDescriptor : ISvgPropertyDescriptor - { - public DescriptorType DescriptorType { get; } - public string AttributeName { get; } - public string AttributeNamespace { get; } - public TypeConverter Converter { get; } - public Type Type { get; } = typeof(TU); - private Func Getter { get; } - private Action Setter { get; } - - public SvgPropertyDescriptor(DescriptorType descriptorType, string attributeName, string attributeNamespace, TypeConverter converter, Func getter, Action setter) - { - DescriptorType = descriptorType; - AttributeName = attributeName; - AttributeNamespace = attributeNamespace; - Converter = converter; - Getter = getter; - Setter = setter; - } - - public object GetValue(object component) - { - return (object)Getter((T)component); - } - - public void SetValue(object component, ITypeDescriptorContext context, CultureInfo culture, object value) - { - if (Converter != null) - { - Setter((T)component, (TU)Converter.ConvertFrom(context, culture, value)); - } - } - } -}"; - #endregion - /// public void Initialize(GeneratorInitializationContext context) { @@ -109,17 +36,13 @@ public void Initialize(GeneratorInitializationContext context) /// public void Execute(GeneratorExecutionContext context) { - // Add the ElementFactory model source to compilation. - context.AddSource("Svg_Model", SourceText.From(ModelText, Encoding.UTF8)); - // Check is we have our SyntaxReceiver object used to filter compiled code. if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) { return; } - var options = (context.Compilation as CSharpCompilation)?.SyntaxTrees[0].Options as CSharpParseOptions; - var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(ModelText, Encoding.UTF8), options)); + var compilation = context.Compilation; var elementFactoryAttribute = compilation.GetTypeByMetadataName("Svg.ElementFactoryAttribute"); if (elementFactoryAttribute is null) @@ -367,7 +290,7 @@ internal virtual bool SetValue(string attributeName, ITypeDescriptorContext cont }} }} "); - context.AddSource($"Svg_SvgElement_Properties.cs", SourceText.From(source.ToString(), Encoding.UTF8)); + context.AddSource($"Svg.SvgElement.Properties.g.cs", SourceText.From(source.ToString(), Encoding.UTF8)); source.Clear(); // Generate SvgElement derived classes with descriptor Properties dictionary. @@ -456,7 +379,7 @@ internal override bool SetValue(string attributeName, ITypeDescriptorContext con }} }} "); - context.AddSource($"{namespaceElement.Replace('.', '_')}_{element.Symbol.Name}_Properties.cs", SourceText.From(source.ToString(), Encoding.UTF8)); + context.AddSource($"{namespaceElement}.{element.Symbol.Name}.Properties.g.cs", SourceText.From(source.ToString(), Encoding.UTF8)); source.Clear(); } @@ -492,7 +415,7 @@ internal static class SvgElements } } "); - context.AddSource($"Svg_SvgElements.cs", SourceText.From(source.ToString(), Encoding.UTF8)); + context.AddSource($"Svg.SvgElements.g.cs", SourceText.From(source.ToString(), Encoding.UTF8)); source.Clear(); // Generate ElementFactory class. @@ -586,7 +509,7 @@ internal partial class {classElementFactory} }} }}"); - context.AddSource($"{namespaceElementFactory.Replace('.', '_')}_{elementFactorySymbol.Name}_ElementFactory.cs", SourceText.From(source.ToString(), Encoding.UTF8)); + context.AddSource($"{namespaceElementFactory}.{elementFactorySymbol.Name}.ElementFactory.g.cs", SourceText.From(source.ToString(), Encoding.UTF8)); source.Clear(); } @@ -755,6 +678,10 @@ private static IEnumerable GetBaseTypes(INamedTypeSymbol named { "System.String" => "System.ComponentModel.StringConverter", "System.Single" => "System.ComponentModel.SingleConverter", + "System.Int16" => "System.ComponentModel.Int16Converter", + "System.Int32" => "System.ComponentModel.Int32Converter", + "System.Int64" => "System.ComponentModel.Int64Converter", + "System.Boolean" => "System.ComponentModel.BooleanConverter", "System.Uri" => "System.UriTypeConverter", _ => null }; @@ -863,149 +790,5 @@ private static IEnumerable GetElementProperties(Compilation compilatio } } } - - /// - /// Symbol member type. - /// - private enum MemberType - { - /// - /// Property symbol. - /// - Property, - /// - /// Event symbol. - /// - Event - } - - /// - /// The SvgElement object property/event. - /// - private class Property - { - /// - /// Gets or sets property/event symbol. - /// - public ISymbol Symbol { get; } - - /// - /// Gets or sets property/event symbol member type. - /// - public MemberType MemberType { get; } - - /// - /// Gets or sets property/event attribute name. - /// - public string AttributeName { get; } - - /// - /// Gets or sets property/event attribute namespace. - /// - public string AttributeNamespace { get; } - - /// - /// Gets or sets property/event type converter type string. - /// - public string? Converter { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The property/event symbol. - /// The property/event symbol member type. - /// The property/event attribute name. - /// The property/event attribute namespace. - /// The property/event type converter type string. - public Property(ISymbol symbol, MemberType memberType, string attributeName, string attributeNamespace, string? converter) - { - Symbol = symbol; - MemberType = memberType; - AttributeName = attributeName; - AttributeNamespace = attributeNamespace; - Converter = converter; - } - } - - /// - /// Custom equality comparer using for cmparison. - /// - private class PropertyEqualityComparer : IEqualityComparer - { - /// - public bool Equals(Property p1, Property p2) - { - return SymbolEqualityComparer.Default.Equals(p1.Symbol, p2.Symbol); - } - - /// - public int GetHashCode(Property p) - { -#pragma warning disable RS1024 - return p.Symbol.GetHashCode(); -#pragma warning restore RS1024 - } - } - - /// - /// The SvgElement object. - /// - private class Element - { - /// - /// Gets or sets element type symbol. - /// - public INamedTypeSymbol Symbol { get; } - - /// - /// Gets or sets element name. - /// - public string? ElementName { get; } - - /// - /// Gets or sets classes that use element name. - /// - public List ClassNames { get; } - - /// - /// Gets or sets element properties list. - /// - public List Properties { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The element type symbol. - /// The element name. - /// The classes that use element name. - /// The element properties list. - public Element(INamedTypeSymbol symbol, string? elementName, List classNames, List properties) - { - Symbol = symbol; - ElementName = elementName; - ClassNames = classNames; - Properties = properties; - } - } - - /// - /// The SyntaxReceiver is used to filter compiled code. This enable quick and easy way to filter compiled code. - /// - private class SyntaxReceiver : ISyntaxReceiver - { - /// - /// Gets the list of all candidate class. - /// - public List CandidateClasses { get; } = new(); - - /// - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax) - { - CandidateClasses.Add(classDeclarationSyntax); - } - } - } } } diff --git a/Generators/Element.cs b/Generators/Element.cs new file mode 100644 index 000000000..e660cdbc2 --- /dev/null +++ b/Generators/Element.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace Svg.Generators +{ + /// + /// The SvgElement object. + /// + class Element + { + /// + /// Gets or sets element type symbol. + /// + public INamedTypeSymbol Symbol { get; } + + /// + /// Gets or sets element name. + /// + public string? ElementName { get; } + + /// + /// Gets or sets classes that use element name. + /// + public List ClassNames { get; } + + /// + /// Gets or sets element properties list. + /// + public List Properties { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The element type symbol. + /// The element name. + /// The classes that use element name. + /// The element properties list. + public Element(INamedTypeSymbol symbol, string? elementName, List classNames, List properties) + { + Symbol = symbol; + ElementName = elementName; + ClassNames = classNames; + Properties = properties; + } + } +} diff --git a/Generators/MemberType.cs b/Generators/MemberType.cs new file mode 100644 index 000000000..e040d1fc2 --- /dev/null +++ b/Generators/MemberType.cs @@ -0,0 +1,17 @@ +namespace Svg.Generators +{ + /// + /// Symbol member type. + /// + enum MemberType + { + /// + /// Property symbol. + /// + Property, + /// + /// Event symbol. + /// + Event + } +} diff --git a/Generators/Property.cs b/Generators/Property.cs new file mode 100644 index 000000000..8bcc46091 --- /dev/null +++ b/Generators/Property.cs @@ -0,0 +1,52 @@ +using Microsoft.CodeAnalysis; + +namespace Svg.Generators +{ + /// + /// The SvgElement object property/event. + /// + class Property + { + /// + /// Gets or sets property/event symbol. + /// + public ISymbol Symbol { get; } + + /// + /// Gets or sets property/event symbol member type. + /// + public MemberType MemberType { get; } + + /// + /// Gets or sets property/event attribute name. + /// + public string AttributeName { get; } + + /// + /// Gets or sets property/event attribute namespace. + /// + public string AttributeNamespace { get; } + + /// + /// Gets or sets property/event type converter type string. + /// + public string? Converter { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The property/event symbol. + /// The property/event symbol member type. + /// The property/event attribute name. + /// The property/event attribute namespace. + /// The property/event type converter type string. + public Property(ISymbol symbol, MemberType memberType, string attributeName, string attributeNamespace, string? converter) + { + Symbol = symbol; + MemberType = memberType; + AttributeName = attributeName; + AttributeNamespace = attributeNamespace; + Converter = converter; + } + } +} diff --git a/Generators/PropertyEqualityComparer.cs b/Generators/PropertyEqualityComparer.cs new file mode 100644 index 000000000..6da19d8e9 --- /dev/null +++ b/Generators/PropertyEqualityComparer.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace Svg.Generators +{ + /// + /// Custom equality comparer using for cmparison. + /// + class PropertyEqualityComparer : IEqualityComparer + { + /// + public bool Equals(Property p1, Property p2) + { + return SymbolEqualityComparer.Default.Equals(p1.Symbol, p2.Symbol); + } + + /// + public int GetHashCode(Property p) + { +#pragma warning disable RS1024 + return p.Symbol.GetHashCode(); +#pragma warning restore RS1024 + } + } +} diff --git a/Generators/Svg.Generators.csproj b/Generators/Svg.Generators.csproj index 9ada21dd0..5ec25fb2a 100644 --- a/Generators/Svg.Generators.csproj +++ b/Generators/Svg.Generators.csproj @@ -19,7 +19,7 @@ - + diff --git a/Generators/SyntaxReceiver.cs b/Generators/SyntaxReceiver.cs new file mode 100644 index 000000000..3ac51d56e --- /dev/null +++ b/Generators/SyntaxReceiver.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Svg.Generators +{ + /// + /// The SyntaxReceiver is used to filter compiled code. This enable quick and easy way to filter compiled code. + /// + class SyntaxReceiver : ISyntaxReceiver + { + /// + /// Gets the list of all candidate class. + /// + public List CandidateClasses { get; } = new(); + + /// + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax) + { + CandidateClasses.Add(classDeclarationSyntax); + } + } + } +} diff --git a/Source/Generated/Readme.md b/Source/Generated/Readme.md new file mode 100644 index 000000000..3e5c91e8e --- /dev/null +++ b/Source/Generated/Readme.md @@ -0,0 +1,5 @@ +### Geneated +A directory of the generated source codes. +- The generated files are excluded from the repository. +- These files will not be directly included in the project. +- To view the generated files, turn "Show All Files" on in the VS. \ No newline at end of file diff --git a/Source/Svg.csproj b/Source/Svg.csproj index c0aa6b26f..802d91cbe 100644 --- a/Source/Svg.csproj +++ b/Source/Svg.csproj @@ -37,7 +37,7 @@ true snupkg true - $(BaseIntermediateOutputPath)\$(Configuration)\$(TargetFramework)\GeneratedFiles + Generated diff --git a/Source/Svg.sln b/Source/Svg.sln index add02a0d1..fdca85558 100644 --- a/Source/Svg.sln +++ b/Source/Svg.sln @@ -35,10 +35,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Benchmark", "..\Tests\S EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generators", "Generators", "{FAFD6DC7-5203-4CED-A151-66379DD43695}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Custom", "..\Svg.Custom\Svg.Custom.csproj", "{6FAA2B79-FE5C-456B-A743-E9290665B223}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Tests.Common", "..\Tests\Svg.Tests.Common\Svg.Tests.Common.csproj", "{D7C625E8-79EA-4859-A457-2C4A90786790}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Custom", "..\Tests\Svg.Custom\Svg.Custom.csproj", "{AD112647-446B-4DAC-8D90-17B0F5FA7188}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,14 +85,14 @@ Global {8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Release|Any CPU.Build.0 = Release|Any CPU - {6FAA2B79-FE5C-456B-A743-E9290665B223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6FAA2B79-FE5C-456B-A743-E9290665B223}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6FAA2B79-FE5C-456B-A743-E9290665B223}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6FAA2B79-FE5C-456B-A743-E9290665B223}.Release|Any CPU.Build.0 = Release|Any CPU {D7C625E8-79EA-4859-A457-2C4A90786790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7C625E8-79EA-4859-A457-2C4A90786790}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7C625E8-79EA-4859-A457-2C4A90786790}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7C625E8-79EA-4859-A457-2C4A90786790}.Release|Any CPU.Build.0 = Release|Any CPU + {AD112647-446B-4DAC-8D90-17B0F5FA7188}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD112647-446B-4DAC-8D90-17B0F5FA7188}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD112647-446B-4DAC-8D90-17B0F5FA7188}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD112647-446B-4DAC-8D90-17B0F5FA7188}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -108,6 +108,7 @@ Global {BACDD1F4-B97D-4E27-BD09-6957633FF484} = {FAFD6DC7-5203-4CED-A151-66379DD43695} {8DEB3EA7-5915-4EB9-8052-85A7AC4379EC} = {2EC3F3A0-F097-43EF-A603-9B82E3061469} {D7C625E8-79EA-4859-A457-2C4A90786790} = {2EC3F3A0-F097-43EF-A603-9B82E3061469} + {AD112647-446B-4DAC-8D90-17B0F5FA7188} = {2EC3F3A0-F097-43EF-A603-9B82E3061469} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5096EEB3-8F41-44B5-BCF9-58743A59AB21} diff --git a/Source/SvgModel.cs b/Source/SvgModel.cs new file mode 100644 index 000000000..5378eeef4 --- /dev/null +++ b/Source/SvgModel.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; + +namespace Svg +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public sealed class ElementFactoryAttribute : Attribute + { + } + + internal enum DescriptorType + { + Property, + Event + } + + internal interface ISvgPropertyDescriptor + { + DescriptorType DescriptorType { get; } + string AttributeName { get; } + string AttributeNamespace { get; } + TypeConverter Converter { get; } + Type Type { get; } + object GetValue(object component); + void SetValue(object component, ITypeDescriptorContext context, CultureInfo culture, object value); + } + + internal class SvgPropertyDescriptor : ISvgPropertyDescriptor + { + public DescriptorType DescriptorType { get; } + public string AttributeName { get; } + public string AttributeNamespace { get; } + public TypeConverter Converter { get; } + public Type Type { get; } = typeof(TU); + private Func Getter { get; } + private Action Setter { get; } + + public SvgPropertyDescriptor(DescriptorType descriptorType, string attributeName, + string attributeNamespace, TypeConverter converter, Func getter, Action setter) + { + DescriptorType = descriptorType; + AttributeName = attributeName; + AttributeNamespace = attributeNamespace; + Converter = converter; + Getter = getter; + Setter = setter; + } + + public object GetValue(object component) + { + return (object)Getter((T)component); + } + + public void SetValue(object component, ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (Converter != null) + { + Setter((T)component, (TU)Converter.ConvertFrom(context, culture, value)); + } + } + } +} diff --git a/Svg.Custom/Svg.Custom.csproj b/Tests/Svg.Custom/Svg.Custom.csproj similarity index 89% rename from Svg.Custom/Svg.Custom.csproj rename to Tests/Svg.Custom/Svg.Custom.csproj index 7b4d346e4..0f538e18d 100644 --- a/Svg.Custom/Svg.Custom.csproj +++ b/Tests/Svg.Custom/Svg.Custom.csproj @@ -11,7 +11,7 @@ True disable true - $(BaseIntermediateOutputPath)\$(Configuration)\$(TargetFramework)\GeneratedFiles + $(SvgSourcesBasePath)\Generated $(DefineConstants);NO_SDC 10.0 @@ -24,7 +24,7 @@ - .. + ..\.. @@ -42,6 +42,11 @@ + + + + + $(DefineConstants);NETSTANDARD;NETSTANDARD20