diff --git a/ShadowExample.Plugin.Emoji/EmojiPlugin.cs b/ShadowExample.Plugin.Emoji/EmojiPlugin.cs index 0489f04..b7aff73 100644 --- a/ShadowExample.Plugin.Emoji/EmojiPlugin.cs +++ b/ShadowExample.Plugin.Emoji/EmojiPlugin.cs @@ -32,7 +32,7 @@ public override string GetEmoji() { return "💡😭"; } - + public override string DisplayName => ""; } } diff --git a/ShadowExample.Plugin.Emoji/ShadowExample.Plugin.Emoji.csproj b/ShadowExample.Plugin.Emoji/ShadowExample.Plugin.Emoji.csproj index a3b1c88..3d48001 100644 --- a/ShadowExample.Plugin.Emoji/ShadowExample.Plugin.Emoji.csproj +++ b/ShadowExample.Plugin.Emoji/ShadowExample.Plugin.Emoji.csproj @@ -41,8 +41,8 @@ - - + + diff --git a/ShadowExample.Plugin.Emoji/ViewModels/Test2ViewModel.cs b/ShadowExample.Plugin.Emoji/ViewModels/Test2ViewModel.cs new file mode 100644 index 0000000..9a0c4cd --- /dev/null +++ b/ShadowExample.Plugin.Emoji/ViewModels/Test2ViewModel.cs @@ -0,0 +1,11 @@ +using ShadowExample.Core; +using ShadowPluginLoader.MetaAttributes; + +namespace ShadowExample.Plugin.Emoji.ViewModels; + +public partial class Test2ViewModel: TestViewModel +{ + [Autowired] + public ShadowExamplePluginLoader ShadowExamplePluginLoader { get; } + +} \ No newline at end of file diff --git a/ShadowExample.Plugin.Emoji/ViewModels/TestViewModel.cs b/ShadowExample.Plugin.Emoji/ViewModels/TestViewModel.cs new file mode 100644 index 0000000..0d9e886 --- /dev/null +++ b/ShadowExample.Plugin.Emoji/ViewModels/TestViewModel.cs @@ -0,0 +1,10 @@ +using ShadowPluginLoader.MetaAttributes; +using ShadowPluginLoader.WinUI; + +namespace ShadowExample.Plugin.Emoji.ViewModels; + +public partial class TestViewModel +{ + [Autowired] + public PluginEventService PluginEventService { get; } +} \ No newline at end of file diff --git a/ShadowPluginLoader.SourceGenerator/Generators/AutowiredGenerator.cs b/ShadowPluginLoader.SourceGenerator/Generators/AutowiredGenerator.cs new file mode 100644 index 0000000..9c716d9 --- /dev/null +++ b/ShadowPluginLoader.SourceGenerator/Generators/AutowiredGenerator.cs @@ -0,0 +1,136 @@ +using Microsoft.CodeAnalysis; +using ShadowPluginLoader.SourceGenerator.Helpers; +using ShadowPluginLoader.SourceGenerator.Models; +using ShadowPluginLoader.SourceGenerator.Receivers; + +namespace ShadowPluginLoader.SourceGenerator.Generators; + +/// +/// +/// +[Generator] +public class AutowiredGenerator : ISourceGenerator +{ + private Dictionary> BaseConstructors { get; } = new(); + + private static string ToLowerFirst(string input) + { + if (string.IsNullOrEmpty(input) || char.IsLower(input[0])) + return input; + + return char.ToLower(input[0]) + input.Substring(1); + } + + /// + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new AutowiredClassSyntaxReceiver()); + } + + /// + public void Execute(GeneratorExecutionContext context) + { + var logger = new Logger("AutowiredGenerator", context); + if (context.SyntaxReceiver is not AutowiredClassSyntaxReceiver receiver) + { + logger.Warning("SPLW003", "No Autowired Class found, skip Autowired generation."); + return; + } + + if (receiver.Classes.Count == 0) + { + logger.Warning("SPLW003", "No Autowired Class found, skip Autowired generation."); + return; + } + + var sortedClasses = InheritanceSorter.SortTypesByInheritance(receiver.Classes + .Select(classSyntax => + context.Compilation + .GetSemanticModel(classSyntax.SyntaxTree) + .GetDeclaredSymbol(classSyntax)) + .OfType()); + foreach (var classSymbol in sortedClasses) + { + var properties = classSymbol.GetMembers() + .OfType().Where(p => p.HasAttribute(context, + "ShadowPluginLoader.MetaAttributes.AutowiredAttribute")); + var propertySymbols = properties as IPropertySymbol[] ?? properties.ToArray(); + if (!propertySymbols.Any()) continue; + var namespaceName = classSymbol.ContainingNamespace.ToDisplayString(); + var classClassName = classSymbol.Name; + + var constructors = new List(); + var assignments = new List(); + var baseConstructors = new List(); + var constructorRecord = new List(); + var baseConstructorString = string.Empty; + + foreach (var property in propertySymbols) + { + var propertyName = property.Name; + var propertySmallName = ToLowerFirst(propertyName); + var propertyType = property.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + constructors.Add($"{propertyType} {propertySmallName}"); + assignments.Add($"{propertyName} = {propertySmallName};"); + constructorRecord.Add(new BaseConstructor(propertyType, propertySmallName)); + } + + var baseTypeSymbol = classSymbol.BaseType; + if (baseTypeSymbol != null) + { + if (BaseConstructors.ContainsKey( + baseTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))) + { + foreach (var parameter in BaseConstructors[ + baseTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)]) + { + constructors.Add($"{parameter.Type} {parameter.Name}"); + baseConstructors.Add($"{parameter.Name}"); + } + } + else + { + var baseConstructor = baseTypeSymbol?.GetMembers() + .OfType() + .Where(m => m.MethodKind == MethodKind.Constructor) + .OrderByDescending(m => m.Parameters.Length) + .FirstOrDefault(); + if (baseConstructor != null && baseConstructor.Name != "object.Object()") + { + foreach (var parameter in baseConstructor.Parameters) + { + var propertyType = parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var propertySmallName = ToLowerFirst(parameter.Name); + constructors.Add($"{propertyType} {propertySmallName}"); + baseConstructors.Add($"{propertySmallName}"); + } + } + } + + if (baseConstructors.Count > 0) + { + baseConstructorString = " : base(" + string.Join(", ", baseConstructors) + ")"; + } + } + + + if (constructors.Count == 0 || assignments.Count == 0) continue; + var baseConstructorsKey = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + BaseConstructors[baseConstructorsKey] = constructorRecord; + var code = $$""" + // Automatic Generate From ShadowPluginLoader.SourceGenerator + + namespace {{namespaceName}}; + + public partial class {{classClassName}} + { + public {{classClassName}}({{string.Join(", ", constructors)}}){{baseConstructorString}} + { + {{string.Join("\n", assignments)}} + } + } + """; + context.AddSource($"{classClassName}_Autowired.g.cs", code); + } + } +} \ No newline at end of file diff --git a/ShadowPluginLoader.SourceGenerator/Generators/SettingsGenerator.cs b/ShadowPluginLoader.SourceGenerator/Generators/SettingsGenerator.cs index 3a742bc..d425f3f 100644 --- a/ShadowPluginLoader.SourceGenerator/Generators/SettingsGenerator.cs +++ b/ShadowPluginLoader.SourceGenerator/Generators/SettingsGenerator.cs @@ -1,5 +1,5 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; +using ShadowPluginLoader.SourceGenerator.Receivers; namespace ShadowPluginLoader.SourceGenerator.Generators; @@ -191,15 +191,3 @@ public partial class {{pluginName}} } } -public class EnumSyntaxReceiver : ISyntaxReceiver -{ - public List Enums { get; } = []; - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is EnumDeclarationSyntax enumDeclaration) - { - Enums.Add(enumDeclaration); - } - } -} \ No newline at end of file diff --git a/ShadowPluginLoader.SourceGenerator/Helpers/InheritanceSorter.cs b/ShadowPluginLoader.SourceGenerator/Helpers/InheritanceSorter.cs new file mode 100644 index 0000000..311d547 --- /dev/null +++ b/ShadowPluginLoader.SourceGenerator/Helpers/InheritanceSorter.cs @@ -0,0 +1,41 @@ +namespace ShadowPluginLoader.SourceGenerator.Helpers; + +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +public class CustomSymbolComparer : IEqualityComparer +{ + public bool Equals(INamedTypeSymbol x, INamedTypeSymbol y) + { + return x.Name == y.Name && x.Kind == y.Kind; + } + + public int GetHashCode(INamedTypeSymbol obj) + { + return obj.Name.GetHashCode() ^ obj.Kind.GetHashCode(); + } +} +public static class InheritanceSorter +{ + public static List SortTypesByInheritance(IEnumerable types) + { + // 创建一个字典存储每个类型的继承链 + var inheritanceChains = new Dictionary>(new CustomSymbolComparer()); + + var namedTypeSymbols = types as INamedTypeSymbol[] ?? types.ToArray(); + foreach (var type in namedTypeSymbols) + { + var chain = new List(); + var currentType = type; + + while (currentType != null) + { + chain.Add(currentType); + currentType = currentType.BaseType; + } + chain.Reverse(); + inheritanceChains[type] = chain; + } + var sortedTypes = namedTypeSymbols.OrderBy(t => string.Join(",", inheritanceChains[t])).ToList(); + return sortedTypes; + } +} diff --git a/ShadowPluginLoader.SourceGenerator/Models/BaseConstructor.cs b/ShadowPluginLoader.SourceGenerator/Models/BaseConstructor.cs new file mode 100644 index 0000000..7399c30 --- /dev/null +++ b/ShadowPluginLoader.SourceGenerator/Models/BaseConstructor.cs @@ -0,0 +1,11 @@ +namespace ShadowPluginLoader.SourceGenerator.Models +{ + public record BaseConstructor(string Type, string Name); +} + +namespace System.Runtime.CompilerServices +{ + class IsExternalInit + { + } +} \ No newline at end of file diff --git a/ShadowPluginLoader.SourceGenerator/Receivers/AutowiredClassSyntaxReceiver.cs b/ShadowPluginLoader.SourceGenerator/Receivers/AutowiredClassSyntaxReceiver.cs new file mode 100644 index 0000000..312fce1 --- /dev/null +++ b/ShadowPluginLoader.SourceGenerator/Receivers/AutowiredClassSyntaxReceiver.cs @@ -0,0 +1,15 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ShadowPluginLoader.SourceGenerator.Receivers; + +public class AutowiredClassSyntaxReceiver : ISyntaxReceiver +{ + public List Classes { get; } = []; + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is not ClassDeclarationSyntax classDeclaration) return; + Classes.Add(classDeclaration); + } +} \ No newline at end of file diff --git a/ShadowPluginLoader.SourceGenerator/Receivers/EnumSyntaxReceiver.cs b/ShadowPluginLoader.SourceGenerator/Receivers/EnumSyntaxReceiver.cs new file mode 100644 index 0000000..2c4693a --- /dev/null +++ b/ShadowPluginLoader.SourceGenerator/Receivers/EnumSyntaxReceiver.cs @@ -0,0 +1,17 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ShadowPluginLoader.SourceGenerator.Receivers; + +public class EnumSyntaxReceiver : ISyntaxReceiver +{ + public List Enums { get; } = []; + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is EnumDeclarationSyntax enumDeclaration) + { + Enums.Add(enumDeclaration); + } + } +} \ No newline at end of file diff --git a/ShadowPluginLoader.SourceGenerator/ShadowPluginLoader.SourceGenerator.csproj b/ShadowPluginLoader.SourceGenerator/ShadowPluginLoader.SourceGenerator.csproj index afb292b..b6bf30c 100644 --- a/ShadowPluginLoader.SourceGenerator/ShadowPluginLoader.SourceGenerator.csproj +++ b/ShadowPluginLoader.SourceGenerator/ShadowPluginLoader.SourceGenerator.csproj @@ -6,7 +6,7 @@ enable latest - 1.4.15 + 1.5.1 true ShadowPluginLoader.SourceGenerator kitUIN