diff --git a/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.dll b/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.dll index 436cd71c..1686b527 100644 Binary files a/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.dll and b/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.dll differ diff --git a/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.pdb b/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.pdb index 80857d63..20d724d4 100644 Binary files a/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.pdb and b/Assets/UIComponents/Roslyn/UIComponents.Roslyn.Generation.pdb differ diff --git a/UIComponents.Roslyn/.gitignore b/UIComponents.Roslyn/.gitignore new file mode 100644 index 00000000..0819dad7 --- /dev/null +++ b/UIComponents.Roslyn/.gitignore @@ -0,0 +1 @@ +TestResults/ \ No newline at end of file diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/ProvideAugmentGeneratorSnapshotTests.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/ProvideAugmentGeneratorSnapshotTests.cs index 471b5e12..df28217b 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/ProvideAugmentGeneratorSnapshotTests.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/ProvideAugmentGeneratorSnapshotTests.cs @@ -122,6 +122,31 @@ private partial class NestedProvideComponent : UIComponent return GeneratorTester.Verify(source); } + [Fact] + public Task It_Handles_Common_Namespaces() + { + var firstSource = @" +namespace MyLibrary.Core.Services +{ + public interface IService {} +} +"; + var secondSource = @" +using UIComponents; +using MyLibrary.Core.Services; + +namespace MyLibrary.GUI +{ + public class GuiComponent + { + [Provide] + public IService service; + } +} +"; + return GeneratorTester.Verify(firstSource, secondSource); + } + [Fact] public Task It_Does_Not_Generate_For_Non_Interface_Or_Class_Fields() { diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/QueryAugmentGeneratorSnapshotTests.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/QueryAugmentGeneratorSnapshotTests.cs index d4321f19..927be7d9 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/QueryAugmentGeneratorSnapshotTests.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/QueryAugmentGeneratorSnapshotTests.cs @@ -217,6 +217,33 @@ public partial class MultipleQueryComponent : UIComponent return GeneratorTester.Verify(source); } + [Fact] + public Task It_Handles_Common_Namespaces() + { + var firstSource = @" +using UnityEngine.UIElements; + +namespace MyLibrary.Core.Elements; +{ + public class MyElement : VisualElement {} +} +"; + var secondSource = @" +using UIComponents; +using MyLibrary.Core.Elements; + +namespace MyLibrary.GUI +{ + public class MyComponent : UIComponent + { + [Query] + public MyElement element; + } +} +"; + return GeneratorTester.Verify(firstSource, secondSource); + } + [Fact] public Task Does_Not_Generate_If_No_Queries_Exist() { diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Snapshots/ProvideAugmentGeneratorSnapshotTests.It_Handles_Common_Namespaces.verified.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Snapshots/ProvideAugmentGeneratorSnapshotTests.It_Handles_Common_Namespaces.verified.cs new file mode 100644 index 00000000..eb39110f --- /dev/null +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Snapshots/ProvideAugmentGeneratorSnapshotTests.It_Handles_Common_Namespaces.verified.cs @@ -0,0 +1,39 @@ +//HintName: GuiComponent.Provide.g.cs +// +// This file has been generated automatically by UIComponents.Roslyn. +// Do not attempt to modify it. Any changes will be overridden during compilation. +// + +using System; +using UIComponents; +using System.CodeDom.Compiler; +using UnityEngine.UIElements; + +namespace MyLibrary.GUI +{ +public partial class GuiComponent +{ + [GeneratedCode("UIComponents.Roslyn.Generation", "1.0.0-alpha.1")] + private void UIC_SetProvideField(ref TField value, string fieldName) where TField : class where TCastFrom : class + { + try + { + value = (TField) (object) Provide(); + } + catch (MissingProviderException) + { + Logger.LogError($"Could not provide {typeof(TField).Name} to {fieldName}", this); + } + catch (InvalidCastException) + { + Logger.LogError($"Could not cast {typeof(TCastFrom).Name} to {typeof(TField).Name}", this); + } + } + + [GeneratedCode("UIComponents.Roslyn.Generation", "1.0.0-alpha.1")] + protected override void UIC_PopulateProvideFields() + { + UIC_SetProvideField(ref service, "service"); + } +} +} diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Snapshots/QueryAugmentGeneratorSnapshotTests.It_Handles_Common_Namespaces.verified.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Snapshots/QueryAugmentGeneratorSnapshotTests.It_Handles_Common_Namespaces.verified.cs new file mode 100644 index 00000000..0bb6ec06 --- /dev/null +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Snapshots/QueryAugmentGeneratorSnapshotTests.It_Handles_Common_Namespaces.verified.cs @@ -0,0 +1,24 @@ +//HintName: MyComponent.Query.g.cs +// +// This file has been generated automatically by UIComponents.Roslyn. +// Do not attempt to modify it. Any changes will be overridden during compilation. +// + +using System.Collections.Generic; +using System.CodeDom.Compiler; +using UnityEngine.UIElements; + +namespace MyLibrary.GUI +{ +public partial class MyComponent +{ + [GeneratedCode("UIComponents.Roslyn.Generation", "1.0.0-alpha.1")] + protected override void UIC_PopulateQueryFields() + { + var UIC_elementList = new List(); + this.Query(null, (string) null).ToList(UIC_elementList); + if (UIC_elementList.Count > 0) + element = UIC_elementList[0]; + } +} +} diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Utilities/GeneratorTester.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Utilities/GeneratorTester.cs index 79f5f398..3f48c149 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Utilities/GeneratorTester.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation.Tests/Utilities/GeneratorTester.cs @@ -7,9 +7,12 @@ namespace UIComponents.Roslyn.Generation.Tests.Utilities { internal class GeneratorTester { - public static Task Verify(string source) where TGenerator : ISourceGenerator, new() + public static Task Verify(params string[] sources) where TGenerator : ISourceGenerator, new() { - var syntaxTree = CSharpSyntaxTree.ParseText(source); + var syntaxTrees = new SyntaxTree[sources.Length]; + + for (var i = 0; i < sources.Length; i++) + syntaxTrees[i] = CSharpSyntaxTree.ParseText(sources[i]); var references = new[] { @@ -21,7 +24,7 @@ internal class GeneratorTester var compilation = CSharpCompilation.Create( assemblyName: "Tests", - syntaxTrees: new[] { syntaxTree }, + syntaxTrees: syntaxTrees, references: references ); diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideAugmentGenerator.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideAugmentGenerator.cs index 078b82b2..1e545715 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideAugmentGenerator.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideAugmentGenerator.cs @@ -116,11 +116,13 @@ protected override void UIC_PopulateProvideFields() foreach (var provideDescription in _provideDescriptions) { + var typeName = + RoslynUtilities.GetTypeNameWithoutRootNamespace(provideDescription.Field.Type, context.CurrentTypeNamespace); stringBuilder .Append(" UIC_SetProvideField<") - .Append(provideDescription.Field.Type.ToDisplayString()) + .Append(typeName) .Append(", ") - .Append(provideDescription.GetCastFromTypeName()) + .Append(provideDescription.GetCastFromTypeName(context.CurrentTypeNamespace)) .Append(">(ref ") .Append(provideDescription.Field.Name) .Append(", ") diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideDescription.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideDescription.cs index 8e83551d..e81e1efb 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideDescription.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/DependencyInjection/ProvideDescription.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using System; +using UIComponents.Roslyn.Generation.Utilities; namespace UIComponents.Roslyn.Generation.Generators.DependencyInjection { @@ -14,12 +15,12 @@ public ProvideDescription(IFieldSymbol field, INamedTypeSymbol castFromType) CastFromType = castFromType; } - public string GetCastFromTypeName() + public string GetCastFromTypeName(string nameSpace) { if (CastFromType != null) - return CastFromType.ToDisplayString(); + return RoslynUtilities.GetTypeNameWithoutRootNamespace(CastFromType, nameSpace); - return Field.Type.ToDisplayString(); + return RoslynUtilities.GetTypeNameWithoutRootNamespace(Field.Type, nameSpace); } } } diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/Uxml/QueryAugmentGenerator.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/Uxml/QueryAugmentGenerator.cs index 95bcdeba..f4028328 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/Uxml/QueryAugmentGenerator.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Generators/Uxml/QueryAugmentGenerator.cs @@ -80,8 +80,9 @@ private void GenerateQueryAssignment(QueryDescription queryDescription, StringBu var memberSymbol = queryDescription.MemberSymbol; var memberType = RoslynUtilities.GetMemberType(memberSymbol); + var namespaceString = memberSymbol.ContainingNamespace.ToDisplayString(); var concreteMemberType = RoslynUtilities.GetConcreteType(memberType); - var concreteMemberTypeName = concreteMemberType.ToDisplayString(); + var concreteMemberTypeName = RoslynUtilities.GetTypeNameWithoutRootNamespace(concreteMemberType, namespaceString); const string Padding = " "; diff --git a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Utilities/RoslynUtilities.cs b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Utilities/RoslynUtilities.cs index 73112ee9..4f90d5df 100644 --- a/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Utilities/RoslynUtilities.cs +++ b/UIComponents.Roslyn/UIComponents.Roslyn.Generation/Utilities/RoslynUtilities.cs @@ -9,15 +9,8 @@ internal static class RoslynUtilities // https://andrewlock.net/creating-a-source-generator-part-5-finding-a-type-declarations-namespace-and-type-hierarchy/ public static string GetTypeNamespace(SyntaxNode syntaxNode) { - if (syntaxNode == null) - return string.Empty; - var currentNamespace = string.Empty; - - var potentialNamespaceParent = syntaxNode.Parent; - - while (potentialNamespaceParent != null && !(potentialNamespaceParent is NamespaceDeclarationSyntax)) - potentialNamespaceParent = potentialNamespaceParent.Parent; + var potentialNamespaceParent = GetNamespaceDeclaration(syntaxNode); if (potentialNamespaceParent is NamespaceDeclarationSyntax namespaceParent) { @@ -36,6 +29,38 @@ public static string GetTypeNamespace(SyntaxNode syntaxNode) return currentNamespace; } + public static NamespaceDeclarationSyntax GetNamespaceDeclaration(SyntaxNode syntaxNode) + { + if (syntaxNode == null) + return null; + + var potentialNamespaceParent = syntaxNode.Parent; + + while (potentialNamespaceParent != null && !(potentialNamespaceParent is NamespaceDeclarationSyntax)) + potentialNamespaceParent = potentialNamespaceParent.Parent; + + return potentialNamespaceParent as NamespaceDeclarationSyntax; + } + + public static string GetTypeNameWithoutRootNamespace(ITypeSymbol type, string nameSpace) + { + var displayString = type.ToDisplayString(); + + var namespaceParts = nameSpace.Split('.'); + + if (namespaceParts.Length < 2) + return displayString; + + var rootNamespace = namespaceParts[0]; + + if (!displayString.StartsWith(rootNamespace)) + return displayString; + + var firstDotIndex = displayString.IndexOf('.') + 1; + + return displayString.Substring(firstDotIndex); + } + public static BaseTypeDeclarationSyntax GetBaseTypeSyntax(SyntaxNode syntaxNode) { while (syntaxNode != null)