Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 70 additions & 68 deletions Decomposer/src/DecomposerConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,78 +62,80 @@ internal static class Constants
+ "public interface IDecomposed<{{ name }}> { }";

public const string DecomposedInterfaceDeclaration = $$$"""
{{{DecomposedInterfaceHeader}}}
{{{GeneratedCodeAttributes}}}

{{ for member in members }}

namespace {{ member.namespace }}.{{{NamespaceSuffix}}}
{{{DecomposedInterfaceHeader
}
}}
{ { { GeneratedCodeAttributes} } }

{ { for member in members } }

namespace {{ member.namespace }}.{{{NamespaceSuffix }}}
{
public interface I{{ member.containing_type }}{{ member.name }}
{
{{ for declaration in member.declarations }}
{{ declaration }}
{{ end }}
}
public interface I {{ member.containing_type }}{ { member.name } }
{
{ { for declaration in member.declarations } }
{ { declaration } }
{ { end } }
}
}

{{ end }}
""";
{ { end } }
""";

public static readonly Scriban.Template DecomposedInterfaceDeclarationTemplate =
Scriban.Template.Parse(DecomposedInterfaceDeclaration);
public const string DecomposableAttributeFilename = "DecomposableAttribute.g.cs";
public const string DecomposableAttributeResourceName = "DecomposeAttribute.cs";
public static readonly string DecomposableAttributeDeclaration =
DecomposedInterfaceHeader
+ typeof(Constants).Assembly
.GetManifestResourceStream(DecomposableAttributeResourceName)
.ReadToEnd();
public static readonly SymbolDisplayFormat SymbolDisplayFormat =
new(
SymbolDisplayGlobalNamespaceStyle.Included,
SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
SymbolDisplayGenericsOptions.IncludeTypeParameters
| SymbolDisplayGenericsOptions.IncludeVariance
| SymbolDisplayGenericsOptions.IncludeTypeConstraints,
SymbolDisplayMemberOptions.IncludeConstantValue
| SymbolDisplayMemberOptions.IncludeExplicitInterface
| SymbolDisplayMemberOptions.IncludeModifiers
| SymbolDisplayMemberOptions.IncludeParameters
| SymbolDisplayMemberOptions.IncludeRef
| SymbolDisplayMemberOptions.IncludeType,
SymbolDisplayDelegateStyle.NameAndSignature,
SymbolDisplayExtensionMethodStyle.Default,
SymbolDisplayParameterOptions.IncludeExtensionThis
| SymbolDisplayParameterOptions.IncludeName
| SymbolDisplayParameterOptions.IncludeParamsRefOut
| SymbolDisplayParameterOptions.IncludeType
| SymbolDisplayParameterOptions.IncludeDefaultValue
| SymbolDisplayParameterOptions.IncludeOptionalBrackets,
SymbolDisplayPropertyStyle.ShowReadWriteDescriptor,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
| SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
| SymbolDisplayMiscellaneousOptions.UseAsterisksInMultiDimensionalArrays
| SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName
| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier
| SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier
);
internal const string AttributeName = "Decompose";
internal const string AttributeFullName = AttributeName + nameof(Attribute);
internal const string AttributeFulllyQualifiedName =
DecomposableAttributeNamespace + "." + AttributeName + nameof(Attribute);
internal const string InterfaceNamePrefix = "I";
internal const string InterfaceSuffix = "Decomposition";
internal const string NamespaceSuffix = ".Decompositions";
internal const string SourceFileNameSuffix = ".Decompositions.g.cs";
internal const string GeneratedCodeAttributeName = "GeneratedCodeAttribute";
internal const string GeneratedCodeAttributeFullName =
$"System.Diagnostics.CodeAnalysis.{GeneratedCodeAttributeName}";
internal const string CompilerGeneratedAttributeFullName =
"System.Runtime.CompilerServices.CompilerGeneratedAttribute";
Scriban.Template.Parse(DecomposedInterfaceDeclaration);

public const string DecomposableAttributeFilename = "DecomposableAttribute.g.cs";
public const string DecomposableAttributeResourceName = "DecomposeAttribute.cs";

public static readonly string DecomposableAttributeDeclaration =
DecomposedInterfaceHeader
+ typeof(Constants).Assembly
.GetManifestResourceStream(DecomposableAttributeResourceName)
.ReadToEnd();

public static readonly SymbolDisplayFormat SymbolDisplayFormat =
new(
SymbolDisplayGlobalNamespaceStyle.Included,
SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
SymbolDisplayGenericsOptions.IncludeTypeParameters
| SymbolDisplayGenericsOptions.IncludeVariance
| SymbolDisplayGenericsOptions.IncludeTypeConstraints,
SymbolDisplayMemberOptions.IncludeConstantValue
| SymbolDisplayMemberOptions.IncludeExplicitInterface
| SymbolDisplayMemberOptions.IncludeModifiers
| SymbolDisplayMemberOptions.IncludeParameters
| SymbolDisplayMemberOptions.IncludeRef
| SymbolDisplayMemberOptions.IncludeType,
SymbolDisplayDelegateStyle.NameAndSignature,
SymbolDisplayExtensionMethodStyle.Default,
SymbolDisplayParameterOptions.IncludeExtensionThis
| SymbolDisplayParameterOptions.IncludeName
| SymbolDisplayParameterOptions.IncludeParamsRefOut
| SymbolDisplayParameterOptions.IncludeType
| SymbolDisplayParameterOptions.IncludeDefaultValue
| SymbolDisplayParameterOptions.IncludeOptionalBrackets,
SymbolDisplayPropertyStyle.ShowReadWriteDescriptor,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
| SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
| SymbolDisplayMiscellaneousOptions.UseAsterisksInMultiDimensionalArrays
| SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName
| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier
| SymbolDisplayMiscellaneousOptions.IncludeNotNullableReferenceTypeModifier
);

internal const string AttributeName = "Decompose";
internal const string AttributeFullName = AttributeName + nameof(Attribute);
internal const string AttributeFulllyQualifiedName =
DecomposableAttributeNamespace + "." + AttributeName + nameof(Attribute);
internal const string InterfaceNamePrefix = "I";
internal const string InterfaceSuffix = "Decomposition";
internal const string NamespaceSuffix = ".Decompositions";
internal const string SourceFileNameSuffix = ".Decompositions.g.cs";
internal const string GeneratedCodeAttributeName = "GeneratedCodeAttribute";
internal const string GeneratedCodeAttributeFullName =
$"System.Diagnostics.CodeAnalysis.{GeneratedCodeAttributeName}";
internal const string CompilerGeneratedAttributeFullName =
"System.Runtime.CompilerServices.CompilerGeneratedAttribute";
}
}
126 changes: 63 additions & 63 deletions Decomposer/src/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,73 +31,73 @@ namespace InterfaceGenerator.Decomposer.Models
{
public record struct DecomposedType(INamedTypeSymbol Type)
{
public string Namespace => Type.ContainingNamespace.ToDisplayString();
public string Name => Type.MetadataName;
public DecomposedMethodTuple[] Methods =>
Type.GetMembers()
.Where(member => member is IMethodSymbol method && method.IsAccessor())
.Select(method => new DecomposedMethod((method as IMethodSymbol)!))
.GroupBy(method => method.Name, StringComparer.OrdinalIgnoreCase)
.Select(group => (group.Key, group.ToArray()))
.ToArray();
public DecomposedPropertyTuple[] Properties =>
Type.GetMembers()
.Where(member => member is IPropertySymbol)
.Select(property => new DecomposedProperty((property as IPropertySymbol)!))
.GroupBy(method => method.Name, StringComparer.OrdinalIgnoreCase)
.Select(group => (group.Key, group.ToArray()))
.ToArray();
public DecomposedMemberTuple[] Members
{
get
{
var @this = this;
return Methods
.Select(
method =>
(
method.Name,
@this.Type.MetadataName,
@this.Type.ContainingNamespace.ToDisplayString(),
method.Methods
.Select(declaration => declaration.Declaration)
.ToArray()
)
)
.Concat(
Properties.Select(
property =>
(
property.Name,
@this.Type.ContainingType.MetadataName,
@this.Type.ContainingNamespace.ToDisplayString(),
property.Properties
.Select(declaration => declaration.Declaration)
.ToArray()
)
)
)
.ToArray();
}
}
}
public record struct DecomposedMethod(IMethodSymbol Method)
public string Namespace => Type.ContainingNamespace.ToDisplayString();
public string Name => Type.MetadataName;
public DecomposedMethodTuple[] Methods =>
Type.GetMembers()
.Where(member => member is IMethodSymbol method && method.IsAccessor())
.Select(method => new DecomposedMethod((method as IMethodSymbol)!))
.GroupBy(method => method.Name, StringComparer.OrdinalIgnoreCase)
.Select(group => (group.Key, group.ToArray()))
.ToArray();
public DecomposedPropertyTuple[] Properties =>
Type.GetMembers()
.Where(member => member is IPropertySymbol)
.Select(property => new DecomposedProperty((property as IPropertySymbol)!))
.GroupBy(method => method.Name, StringComparer.OrdinalIgnoreCase)
.Select(group => (group.Key, group.ToArray()))
.ToArray();

public DecomposedMemberTuple[] Members
{
get
{
var @this = this;
return Methods
.Select(
method =>
(
method.Name,
@this.Type.MetadataName,
@this.Type.ContainingNamespace.ToDisplayString(),
method.Methods
.Select(declaration => declaration.Declaration)
.ToArray()
)
)
.Concat(
Properties.Select(
property =>
(
property.Name,
@this.Type.ContainingType.MetadataName,
@this.Type.ContainingNamespace.ToDisplayString(),
property.Properties
.Select(declaration => declaration.Declaration)
.ToArray()
)
)
)
.ToArray();
}
}
}

public record struct DecomposedMethod(IMethodSymbol Method)
{
public string Namespace => Method.ContainingType.ContainingNamespace.ToDisplayString();
public string ContainingType => Method.ContainingType.MetadataName;
public string Name => Method.Name;
public string ReturnType => Method.ReturnType.ToDisplayString();
public string Declaration => Method.ToDisplayString(Constants.SymbolDisplayFormat);
public string Namespace => Method.ContainingType.ContainingNamespace.ToDisplayString();
public string ContainingType => Method.ContainingType.MetadataName;
public string Name => Method.Name;
public string ReturnType => Method.ReturnType.ToDisplayString();
public string Declaration => Method.ToDisplayString(Constants.SymbolDisplayFormat);
}

public record struct DecomposedProperty(IPropertySymbol Property)
{
public string Namespace => Property.ContainingType.ContainingNamespace.ToDisplayString();
public string ContainingType => Property.ContainingType.MetadataName;
public string Name => Property.Name;
public string Type => Property.Type.ToDisplayString();
public string Declaration => Property.ToDisplayString(Constants.SymbolDisplayFormat);
public string Namespace => Property.ContainingType.ContainingNamespace.ToDisplayString();
public string ContainingType => Property.ContainingType.MetadataName;
public string Name => Property.Name;
public string Type => Property.Type.ToDisplayString();
public string Declaration => Property.ToDisplayString(Constants.SymbolDisplayFormat);
}
}
6 changes: 3 additions & 3 deletions Decomposer/src/Runtime/DecomposeAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
| AttributeTargets.Interface
| AttributeTargets.Assembly
)]
public sealed class DecomposeAttribute(type type, string? @namespace = default) : Attribute
public sealed class DecomposeAttribute(type type, string ? @namespace = default) : Attribute
{
public DecomposeAttribute(string? @namespace = default)
: this(default!, @namespace) { }

public type Type => type;
public string? Namespace => @namespace;
public type Type => type;
public string? Namespace => @namespace;
}
75 changes: 39 additions & 36 deletions InterfaceGenerator/src/InterfaceGeneratorConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ public static class Constants
""";

public const string GenerateInterfaceAttributeDeclaration = $$$"""
{{{Header}}}
{{{Header
}}}

[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class {{{GenerateInterfaceAttribute}}}(Type? @type = default, string? interfaceName, string? @namespace) : Attribute
{
public type Type { get; } = @type;
public string InterfaceName { get; } = interfaceName;
public string Namespace { get; } = @namespace;
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class {{ { GenerateInterfaceAttribute} }}(Type ? @type = default, string? interfaceName, string? @namespace) : Attribute
{
public type Type { get; } = @type;
public string InterfaceName { get; } = interfaceName;
public string Namespace { get; } = @namespace;
}
""";

Expand All @@ -63,44 +64,46 @@ public sealed class {{{GenerateInterfaceAttribute}}}(Type? @type = default, stri

namespace {{ namespace }}
{
public partial interface {{ interface_name }}
{
{{ members }}
}
public partial interface {{ interface_name
}}
{
{ { members } }
}
}
""";

public static readonly Scriban.Template InterfaceDeclarationTemplate = Scriban.Template.Parse(
InterfaceDeclaration
);

public const string MethodDeclaration = "{{ full_definition }};";

public static readonly Scriban.Template MethodDeclarationTemplate = Scriban.Template.Parse(
MethodDeclaration
);

public const string MethodParameter = """
{{ type }} {{ name }}
""";
);

public const string MethodDeclaration = "{{ full_definition }};";

public static readonly Scriban.Template MethodDeclarationTemplate = Scriban.Template.Parse(
MethodDeclaration
);

public const string MethodParameter = """
{ { type } }
{ { name } }
""";

public static readonly Scriban.Template MethodParameterTemplate = Scriban.Template.Parse(
MethodParameter
);

public const string PropertyDeclaration =
"{{ type }} {{ if is_indexed }}this[ {{ indexers }}] {{ else }} {{ name }} {{ end }} { {{ if is_gettable }} get; {{ end }} {{ if is_settable }} set; {{ end }} }";

public static readonly Scriban.Template PropertyDeclarationTemplate = Scriban.Template.Parse(
PropertyDeclaration
);

public static readonly SymbolDisplayFormat SymbolDisplayFormat =
new(
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters,
parameterOptions: SymbolDisplayParameterOptions.None
);

public const string PropertyDeclaration =
"{{ type }} {{ if is_indexed }}this[ {{ indexers }}] {{ else }} {{ name }} {{ end }} { {{ if is_gettable }} get; {{ end }} {{ if is_settable }} set; {{ end }} }";

public static readonly Scriban.Template PropertyDeclarationTemplate = Scriban.Template.Parse(
PropertyDeclaration
);

public static readonly SymbolDisplayFormat SymbolDisplayFormat =
new(
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters,
parameterOptions: SymbolDisplayParameterOptions.None
);
// new(
// globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
// typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
Expand Down