-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
447 additions
and
510 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
<Project> | ||
<Import Project="../Properties/GirCore.Libraries.props" /> | ||
<Import Project="../Properties/GirCore.Publishing.props" /> | ||
</Project> |
26 changes: 26 additions & 0 deletions
26
src/Extensions/GObject-2.0.Integration/GObject-2.0.Integration.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<PackageId>GirCore.GObject-2.0.Integration</PackageId> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<RootNamespace>GObject.Integration</RootNamespace> | ||
<Description>Source Generator to make it easy to integrate C# with the GObject type system.</Description> | ||
|
||
<AnalysisMode>Recommended</AnalysisMode> | ||
<Nullable>enable</Nullable> | ||
<TreatWarningsErrors>true</TreatWarningsErrors> | ||
|
||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<IncludeBuildOutput>false</IncludeBuildOutput> | ||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<!-- Reference 4.8 as it is the first version which supports net8.0 --> | ||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.8.0"/> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||
</ItemGroup> | ||
</Project> |
9 changes: 9 additions & 0 deletions
9
src/Extensions/GObject-2.0.Integration/Properties/launchSettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"$schema": "http://json.schemastore.org/launchsettings.json", | ||
"profiles": { | ||
"GObject-2.0.Integration": { | ||
"commandName": "DebugRoslynComponent", | ||
"targetProject": "../../Samples/Gtk-4.0/GridView/GridView.csproj" | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/Extensions/GObject-2.0.Integration/SourceGenerator/Generator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace GObject.Integration.SourceGenerator; | ||
|
||
[Generator] | ||
public class Generator : IIncrementalGenerator | ||
{ | ||
public void Initialize(IncrementalGeneratorInitializationContext context) | ||
{ | ||
context.EnableSubclassSupport(); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Extensions/GObject-2.0.Integration/SourceGenerator/Subclass/Subclass.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace GObject.Integration.SourceGenerator; | ||
|
||
internal static class Subclass | ||
{ | ||
public static void EnableSubclassSupport(this IncrementalGeneratorInitializationContext context) | ||
{ | ||
context.RegisterImplementationSourceOutput( | ||
source: context.GetSubclassValuesProvider(), | ||
action: SubclassCode.Generate | ||
); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/Extensions/GObject-2.0.Integration/SourceGenerator/Subclass/SubclassCode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using System.Globalization; | ||
using System.Text; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace GObject.Integration.SourceGenerator; | ||
|
||
internal static class SubclassCode | ||
{ | ||
public static void Generate(SourceProductionContext context, SubclassData subclassData) | ||
{ | ||
context.AddSource( | ||
hintName: $"{subclassData.FileName}.Subclass.g.cs", | ||
source: ToCode(subclassData) | ||
); | ||
} | ||
private static string ToCode(SubclassData subclassData) | ||
{ | ||
return $$""" | ||
namespace {{subclassData.Namespace}}; | ||
{{RenderClassHierarchy(subclassData)}} | ||
"""; | ||
} | ||
|
||
private static string RenderClassHierarchy(SubclassData subclassData) | ||
{ | ||
var sb = new StringBuilder(); | ||
foreach (var typeData in subclassData.UpperNestedClasses) | ||
sb.AppendLine(CultureInfo.InvariantCulture, $"{typeData.Accessibility} partial {typeData.Kind} {typeData.NameGenericArguments} {{"); | ||
|
||
sb.AppendLine(RenderClassContent(subclassData)); | ||
|
||
foreach (var _ in subclassData.UpperNestedClasses) | ||
sb.AppendLine("}"); | ||
|
||
return sb.ToString(); | ||
} | ||
|
||
private static string RenderClassContent(SubclassData subclassData) | ||
{ | ||
return $$""" | ||
{{subclassData.Accessibility}} partial class {{subclassData.NameGenericArguments}}({{subclassData.ParentHandle}} handle) : {{subclassData.Parent}}(handle), GObject.GTypeProvider, GObject.InstanceFactory | ||
{ | ||
private static readonly GObject.Type GType = GObject.Internal.SubclassRegistrar.Register<{{subclassData.NameGenericArguments}}, {{subclassData.Parent}}>(); | ||
public static new GObject.Type GetGType() => GType; | ||
static object GObject.InstanceFactory.Create(System.IntPtr handle, bool ownsHandle) | ||
{ | ||
return new {{subclassData.NameGenericArguments}}(new {{subclassData.ParentHandle}}(handle, ownsHandle)); | ||
} | ||
public {{subclassData.Name}}(params GObject.ConstructArgument[] constructArguments) : this({{subclassData.ParentHandle}}.For<{{subclassData.NameGenericArguments}}>(constructArguments)) { } | ||
} | ||
"""; | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Extensions/GObject-2.0.Integration/SourceGenerator/Subclass/SubclassData.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace GObject.Integration.SourceGenerator; | ||
|
||
internal sealed record SubclassData( | ||
string Name, | ||
string NameGenericArguments, | ||
string Parent, | ||
string ParentHandle, | ||
string Namespace, | ||
string Accessibility, | ||
string FileName, | ||
Stack<TypeData> UpperNestedClasses | ||
); |
167 changes: 167 additions & 0 deletions
167
src/Extensions/GObject-2.0.Integration/SourceGenerator/Subclass/SubclassValuesProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace GObject.Integration.SourceGenerator; | ||
|
||
internal static class SubclassValuesProvider | ||
{ | ||
private const string SubclassAtributeName = "GObject.SubclassAttribute`1"; | ||
|
||
public static IncrementalValuesProvider<SubclassData> GetSubclassValuesProvider(this IncrementalGeneratorInitializationContext context) | ||
{ | ||
return context.SyntaxProvider | ||
.ForAttributeWithMetadataName( | ||
fullyQualifiedMetadataName: SubclassAtributeName, | ||
predicate: static (_, _) => true, | ||
transform: GetSubclassData) | ||
.Where(data => data is not null)!; | ||
} | ||
|
||
private static SubclassData? GetSubclassData(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) | ||
{ | ||
if (context.TargetSymbol is not INamedTypeSymbol subclass) | ||
return null; | ||
|
||
var subclassAttribute = context.Attributes.First().AttributeClass; | ||
if (subclassAttribute is null) | ||
return null; | ||
|
||
var parentType = subclassAttribute.TypeArguments.First(); | ||
|
||
var parentHandle = GetParentHandle(parentType); | ||
if (parentHandle is null) | ||
return null; | ||
|
||
var accessibility = GetAccessibility(context.TargetSymbol); | ||
if (accessibility is null) | ||
return null; | ||
|
||
var upperNestedClasses = GetUpperNestedClasses(context.TargetSymbol); | ||
if (upperNestedClasses is null) | ||
return null; | ||
|
||
return new SubclassData( | ||
Name: subclass.Name, | ||
NameGenericArguments: subclass.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), | ||
Parent: parentType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), | ||
ParentHandle: parentHandle, | ||
Namespace: context.TargetSymbol.ContainingNamespace.ToDisplayString(), | ||
Accessibility: accessibility, | ||
FileName: GetFileName(subclass), | ||
UpperNestedClasses: upperNestedClasses | ||
); | ||
} | ||
|
||
private static Stack<TypeData>? GetUpperNestedClasses(ISymbol symbol) | ||
{ | ||
var stack = new Stack<TypeData>(); | ||
var containingType = symbol.ContainingType; | ||
|
||
while (containingType is not null) | ||
{ | ||
var accessibility = GetAccessibility(containingType); | ||
if (accessibility is null) | ||
return null; | ||
|
||
var kind = GetKind(containingType); | ||
if (kind is null) | ||
return null; | ||
|
||
var typeData = new TypeData( | ||
NameGenericArguments: containingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), | ||
Accessibility: accessibility, | ||
Kind: kind | ||
); | ||
stack.Push(typeData); | ||
containingType = containingType.ContainingType; | ||
} | ||
|
||
return stack; | ||
} | ||
|
||
private static string? GetKind(INamedTypeSymbol symbol) | ||
{ | ||
return (symbol.TypeKind, symbol.IsRecord) switch | ||
{ | ||
(TypeKind.Struct, _) => "struct", | ||
(TypeKind.Class, true) => "record", | ||
(TypeKind.Class, _) => "class", | ||
_ => null | ||
}; | ||
} | ||
|
||
private static string GetFileName(INamedTypeSymbol typeSymbol) | ||
{ | ||
var prefix = GetFileNamePrefix(typeSymbol); | ||
var suffix = typeSymbol.Arity == 0 ? string.Empty : $"_{typeSymbol.Arity}"; | ||
|
||
return prefix + typeSymbol.Name + suffix; | ||
} | ||
|
||
private static string GetFileNamePrefix(INamedTypeSymbol typeSymbol) | ||
{ | ||
var sb = new StringBuilder(); | ||
|
||
var containingType = typeSymbol.ContainingType; | ||
while (containingType is not null) | ||
{ | ||
sb.Insert(0, $"{containingType.Name}."); | ||
containingType = containingType.ContainingType; | ||
} | ||
|
||
return sb.ToString(); | ||
} | ||
|
||
private static string? GetAccessibility(ISymbol type) | ||
{ | ||
var accessibility = type.DeclaredAccessibility switch | ||
{ | ||
Accessibility.Public => "public", | ||
Accessibility.Internal => "internal", | ||
Accessibility.Private => "private", | ||
Accessibility.NotApplicable => "internal", | ||
_ => null | ||
}; | ||
return accessibility; | ||
} | ||
|
||
private static string? GetParentHandle(ITypeSymbol type) | ||
{ | ||
ITypeSymbol? currentType = type; | ||
INamedTypeSymbol? parentHandleAttribute; | ||
do | ||
{ | ||
parentHandleAttribute = GetHandleAttribute(currentType); | ||
|
||
if (parentHandleAttribute is null) | ||
currentType = GetTypeInSubclassAttribute(currentType); | ||
|
||
} while (parentHandleAttribute is null && currentType is not null); | ||
|
||
return parentHandleAttribute?.TypeArguments.First().ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); | ||
} | ||
|
||
private static INamedTypeSymbol? GetHandleAttribute(ITypeSymbol type) | ||
{ | ||
var attributeData = type | ||
.GetAttributes() | ||
.FirstOrDefault(x => x.AttributeClass?.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::GObject.HandleAttribute<T>"); | ||
|
||
return attributeData?.AttributeClass; | ||
} | ||
|
||
private static ITypeSymbol? GetTypeInSubclassAttribute(ITypeSymbol type) | ||
{ | ||
var subclassAttribute = type | ||
.GetAttributes() | ||
.FirstOrDefault(x => x.AttributeClass?.ConstructedFrom.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::GObject.SubclassAttribute<T>"); | ||
|
||
if (subclassAttribute is null) | ||
return null; | ||
|
||
return subclassAttribute.AttributeClass?.TypeArguments.First(); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/Extensions/GObject-2.0.Integration/SourceGenerator/Subclass/TypeData.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace GObject.Integration.SourceGenerator; | ||
|
||
internal sealed record TypeData( | ||
string NameGenericArguments, | ||
string? Accessibility, | ||
string Kind | ||
); |
19 changes: 0 additions & 19 deletions
19
src/Extensions/Integration/Extensions/AssemblyExtension.cs
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.