Skip to content

Commit

Permalink
Feat | Autowired
Browse files Browse the repository at this point in the history
  • Loading branch information
kitUIN committed Jan 24, 2025
1 parent 6bf2265 commit 8d9a8ac
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 17 deletions.
2 changes: 1 addition & 1 deletion ShadowExample.Plugin.Emoji/EmojiPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public override string GetEmoji()
{
return "💡😭";
}

public override string DisplayName => "";
}
}
4 changes: 2 additions & 2 deletions ShadowExample.Plugin.Emoji/ShadowExample.Plugin.Emoji.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
<ItemGroup>
<PackageReference Include="ShadowExample.Core" Version="1.0.5.1"/>
<PackageReference Include="ShadowPluginLoader.WinUI" Version="1.5.4"/>
<PackageReference Include="ShadowPluginLoader.SourceGenerator" Version="1.4.15"/>
<PackageReference Include="ShadowPluginLoader.MetaAttributes" Version="1.2.4"/>
<PackageReference Include="ShadowPluginLoader.SourceGenerator" Version="1.5.0.24"/>
<PackageReference Include="ShadowPluginLoader.MetaAttributes" Version="1.3.1"/>
</ItemGroup>

<ItemGroup>
Expand Down
11 changes: 11 additions & 0 deletions ShadowExample.Plugin.Emoji/ViewModels/Test2ViewModel.cs
Original file line number Diff line number Diff line change
@@ -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; }

}
10 changes: 10 additions & 0 deletions ShadowExample.Plugin.Emoji/ViewModels/TestViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using ShadowPluginLoader.MetaAttributes;
using ShadowPluginLoader.WinUI;

namespace ShadowExample.Plugin.Emoji.ViewModels;

public partial class TestViewModel
{
[Autowired]
public PluginEventService PluginEventService { get; }
}
136 changes: 136 additions & 0 deletions ShadowPluginLoader.SourceGenerator/Generators/AutowiredGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using Microsoft.CodeAnalysis;
using ShadowPluginLoader.SourceGenerator.Helpers;
using ShadowPluginLoader.SourceGenerator.Models;
using ShadowPluginLoader.SourceGenerator.Receivers;

namespace ShadowPluginLoader.SourceGenerator.Generators;

/// <summary>
///
/// </summary>
[Generator]
public class AutowiredGenerator : ISourceGenerator
{
private Dictionary<string, List<BaseConstructor>> 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);
}

/// <inheritdoc />
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new AutowiredClassSyntaxReceiver());
}

/// <inheritdoc />
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<INamedTypeSymbol>());
foreach (var classSymbol in sortedClasses)
{
var properties = classSymbol.GetMembers()
.OfType<IPropertySymbol>().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<string>();
var assignments = new List<string>();
var baseConstructors = new List<string>();
var constructorRecord = new List<BaseConstructor>();
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<IMethodSymbol>()
.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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using ShadowPluginLoader.SourceGenerator.Receivers;

namespace ShadowPluginLoader.SourceGenerator.Generators;

Expand Down Expand Up @@ -191,15 +191,3 @@ public partial class {{pluginName}}
}
}

public class EnumSyntaxReceiver : ISyntaxReceiver
{
public List<EnumDeclarationSyntax> Enums { get; } = [];

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is EnumDeclarationSyntax enumDeclaration)
{
Enums.Add(enumDeclaration);
}
}
}
41 changes: 41 additions & 0 deletions ShadowPluginLoader.SourceGenerator/Helpers/InheritanceSorter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace ShadowPluginLoader.SourceGenerator.Helpers;

using Microsoft.CodeAnalysis;
using System.Collections.Generic;
public class CustomSymbolComparer : IEqualityComparer<INamedTypeSymbol>
{
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<INamedTypeSymbol> SortTypesByInheritance(IEnumerable<INamedTypeSymbol> types)
{
// 创建一个字典存储每个类型的继承链
var inheritanceChains = new Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>>(new CustomSymbolComparer());

var namedTypeSymbols = types as INamedTypeSymbol[] ?? types.ToArray();
foreach (var type in namedTypeSymbols)
{
var chain = new List<INamedTypeSymbol>();
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;
}
}
11 changes: 11 additions & 0 deletions ShadowPluginLoader.SourceGenerator/Models/BaseConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace ShadowPluginLoader.SourceGenerator.Models
{
public record BaseConstructor(string Type, string Name);
}

namespace System.Runtime.CompilerServices
{
class IsExternalInit
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace ShadowPluginLoader.SourceGenerator.Receivers;

public class AutowiredClassSyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> Classes { get; } = [];

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is not ClassDeclarationSyntax classDeclaration) return;
Classes.Add(classDeclaration);
}
}
17 changes: 17 additions & 0 deletions ShadowPluginLoader.SourceGenerator/Receivers/EnumSyntaxReceiver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace ShadowPluginLoader.SourceGenerator.Receivers;

public class EnumSyntaxReceiver : ISyntaxReceiver
{
public List<EnumDeclarationSyntax> Enums { get; } = [];

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is EnumDeclarationSyntax enumDeclaration)
{
Enums.Add(enumDeclaration);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>

<Version>1.4.15</Version>
<Version>1.5.1</Version>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<PackageId>ShadowPluginLoader.SourceGenerator</PackageId>
<Owner>kitUIN</Owner>
Expand Down

0 comments on commit 8d9a8ac

Please sign in to comment.