committed by
GitHub
12 changed files with 442 additions and 134 deletions
@ -0,0 +1,42 @@ |
|||||
|
using System; |
||||
|
using XamlX.Compiler; |
||||
|
using XamlX.Emit; |
||||
|
using XamlX.Transform; |
||||
|
using XamlX.Transform.Transformers; |
||||
|
using XamlX.TypeSystem; |
||||
|
|
||||
|
namespace XamlNameReferenceGenerator.Infrastructure |
||||
|
{ |
||||
|
internal sealed class MiniCompiler : XamlCompiler<object, IXamlEmitResult> |
||||
|
{ |
||||
|
public static MiniCompiler CreateDefault(RoslynTypeSystem typeSystem) |
||||
|
{ |
||||
|
var avaloniaXmlns = typeSystem.GetType("Avalonia.Metadata.XmlnsDefinitionAttribute"); |
||||
|
var configuration = new TransformerConfiguration( |
||||
|
typeSystem, |
||||
|
typeSystem.Assemblies[0], |
||||
|
new XamlLanguageTypeMappings(typeSystem) {XmlnsAttributes = {avaloniaXmlns}}); |
||||
|
return new MiniCompiler(configuration); |
||||
|
} |
||||
|
|
||||
|
private MiniCompiler(TransformerConfiguration configuration) |
||||
|
: base(configuration, new XamlLanguageEmitMappings<object, IXamlEmitResult>(), false) |
||||
|
{ |
||||
|
Transformers.Add(new KnownDirectivesTransformer()); |
||||
|
Transformers.Add(new XamlIntrinsicsTransformer()); |
||||
|
Transformers.Add(new XArgumentsTransformer()); |
||||
|
Transformers.Add(new TypeReferenceResolver()); |
||||
|
Transformers.Add(new MarkupExtensionTransformer()); |
||||
|
Transformers.Add(new PropertyReferenceResolver()); |
||||
|
Transformers.Add(new ResolvePropertyValueAddersTransformer()); |
||||
|
Transformers.Add(new ConstructableObjectTransformer()); |
||||
|
} |
||||
|
|
||||
|
protected override XamlEmitContext<object, IXamlEmitResult> InitCodeGen( |
||||
|
IFileSource file, |
||||
|
Func<string, IXamlType, IXamlTypeBuilder<object>> createSubType, |
||||
|
object codeGen, XamlRuntimeContext<object, IXamlEmitResult> context, |
||||
|
bool needContextLocal) => |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using XamlX.Ast; |
||||
|
|
||||
|
namespace XamlNameReferenceGenerator.Infrastructure |
||||
|
{ |
||||
|
internal sealed class MiniNamedControlCollector : IXamlAstVisitor |
||||
|
{ |
||||
|
private readonly List<(string TypeName, string Name)> _items = new List<(string TypeName, string Name)>(); |
||||
|
|
||||
|
public IReadOnlyList<(string TypeName, string Name)> Controls => _items; |
||||
|
|
||||
|
public IXamlAstNode Visit(IXamlAstNode node) |
||||
|
{ |
||||
|
if (!(node is XamlAstConstructableObjectNode constructableObjectNode)) |
||||
|
return node; |
||||
|
|
||||
|
foreach (var child in constructableObjectNode.Children) |
||||
|
{ |
||||
|
var nameValue = ResolveNameDirectiveOrDefault(child); |
||||
|
if (nameValue == null) continue; |
||||
|
|
||||
|
var clrType = constructableObjectNode.Type.GetClrType(); |
||||
|
var typeNamePair = ($@"{clrType.Namespace}.{clrType.Name}", nameValue); |
||||
|
if (!_items.Contains(typeNamePair)) |
||||
|
{ |
||||
|
_items.Add(typeNamePair); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
public void Push(IXamlAstNode node) { } |
||||
|
|
||||
|
public void Pop() { } |
||||
|
|
||||
|
private static string ResolveNameDirectiveOrDefault(IXamlAstNode node) => |
||||
|
node switch |
||||
|
{ |
||||
|
XamlAstXamlPropertyValueNode propertyValueNode when |
||||
|
propertyValueNode.Property is XamlAstClrProperty reference && |
||||
|
reference.Name == "Name" && |
||||
|
propertyValueNode.Values.Count > 0 && |
||||
|
propertyValueNode.Values[0] is XamlAstTextNode nameNode => nameNode.Text, |
||||
|
|
||||
|
XamlAstXmlDirective xmlDirective when |
||||
|
xmlDirective.Name == "Name" && |
||||
|
xmlDirective.Values.Count > 0 && |
||||
|
xmlDirective.Values[0] is XamlAstTextNode xNameNode => xNameNode.Text, |
||||
|
|
||||
|
_ => null |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -1,13 +1,14 @@ |
|||||
using System; |
using System; |
||||
using System.IO; |
using System.IO; |
||||
|
|
||||
namespace XamlNameReferenceGenerator |
namespace XamlNameReferenceGenerator.Infrastructure |
||||
{ |
{ |
||||
internal class NameReferenceDebugger |
internal class PhysicalFileDebugger |
||||
{ |
{ |
||||
|
private const string DefaultPath = @"C:\Users\prizr\Documents\GitHub\XamlNameReferenceGenerator\debug.txt"; |
||||
private readonly string _path; |
private readonly string _path; |
||||
|
|
||||
public NameReferenceDebugger(string path) => _path = path; |
public PhysicalFileDebugger(string path = DefaultPath) => _path = path; |
||||
|
|
||||
public string Debug(Func<string> function) |
public string Debug(Func<string> function) |
||||
{ |
{ |
||||
@ -0,0 +1,281 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Microsoft.CodeAnalysis; |
||||
|
using Microsoft.CodeAnalysis.CSharp; |
||||
|
using XamlX.TypeSystem; |
||||
|
|
||||
|
namespace XamlNameReferenceGenerator.Infrastructure |
||||
|
{ |
||||
|
public class RoslynTypeSystem : IXamlTypeSystem |
||||
|
{ |
||||
|
private readonly List<IXamlAssembly> _assemblies = new List<IXamlAssembly>(); |
||||
|
|
||||
|
public RoslynTypeSystem(CSharpCompilation compilation) |
||||
|
{ |
||||
|
_assemblies.Add(new RoslynAssembly(compilation.Assembly)); |
||||
|
|
||||
|
var assemblySymbols = compilation |
||||
|
.References |
||||
|
.Select(compilation.GetAssemblyOrModuleSymbol) |
||||
|
.OfType<IAssemblySymbol>() |
||||
|
.Select(assembly => new RoslynAssembly(assembly)) |
||||
|
.ToList(); |
||||
|
|
||||
|
_assemblies.AddRange(assemblySymbols); |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyList<IXamlAssembly> Assemblies => _assemblies; |
||||
|
|
||||
|
public IXamlAssembly FindAssembly(string substring) => _assemblies[0]; |
||||
|
|
||||
|
public IXamlType FindType(string name) |
||||
|
{ |
||||
|
foreach (var assembly in _assemblies) |
||||
|
{ |
||||
|
var type = assembly.FindType(name); |
||||
|
if (type != null) |
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public IXamlType FindType(string name, string assembly) |
||||
|
{ |
||||
|
foreach (var assemblyInstance in _assemblies) |
||||
|
{ |
||||
|
var type = assemblyInstance.FindType(name); |
||||
|
if (type != null) |
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class RoslynAssembly : IXamlAssembly |
||||
|
{ |
||||
|
private readonly IAssemblySymbol _symbol; |
||||
|
|
||||
|
public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol; |
||||
|
|
||||
|
public bool Equals(IXamlAssembly other) => |
||||
|
other is RoslynAssembly roslynAssembly && |
||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol); |
||||
|
|
||||
|
public string Name => _symbol.Name; |
||||
|
|
||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => |
||||
|
_symbol.GetAttributes() |
||||
|
.Select(data => new RoslynAttribute(data, this)) |
||||
|
.ToList(); |
||||
|
|
||||
|
public IXamlType FindType(string fullName) |
||||
|
{ |
||||
|
var type = _symbol.GetTypeByMetadataName(fullName); |
||||
|
return type is null ? null : new RoslynType(type, this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class RoslynAttribute : IXamlCustomAttribute |
||||
|
{ |
||||
|
private readonly AttributeData _data; |
||||
|
private readonly RoslynAssembly _assembly; |
||||
|
|
||||
|
public RoslynAttribute(AttributeData data, RoslynAssembly assembly) |
||||
|
{ |
||||
|
_data = data; |
||||
|
_assembly = assembly; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(IXamlCustomAttribute other) => |
||||
|
other is RoslynAttribute attribute && |
||||
|
_data == attribute._data; |
||||
|
|
||||
|
public IXamlType Type => new RoslynType(_data.AttributeClass, _assembly); |
||||
|
|
||||
|
public List<object> Parameters => |
||||
|
_data.ConstructorArguments |
||||
|
.Select(argument => argument.Value) |
||||
|
.ToList(); |
||||
|
|
||||
|
public Dictionary<string, object> Properties => |
||||
|
_data.NamedArguments.ToDictionary( |
||||
|
pair => pair.Key, |
||||
|
pair => pair.Value.Value); |
||||
|
} |
||||
|
|
||||
|
public class RoslynType : IXamlType |
||||
|
{ |
||||
|
private static readonly SymbolDisplayFormat SymbolDisplayFormat = new SymbolDisplayFormat( |
||||
|
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, |
||||
|
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | |
||||
|
SymbolDisplayGenericsOptions.IncludeTypeConstraints | |
||||
|
SymbolDisplayGenericsOptions.IncludeVariance); |
||||
|
|
||||
|
private readonly RoslynAssembly _assembly; |
||||
|
private readonly INamedTypeSymbol _symbol; |
||||
|
|
||||
|
public RoslynType(INamedTypeSymbol symbol, RoslynAssembly assembly) |
||||
|
{ |
||||
|
_symbol = symbol; |
||||
|
_assembly = assembly; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(IXamlType other) => |
||||
|
other is RoslynType roslynType && |
||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynType._symbol); |
||||
|
|
||||
|
public object Id => _symbol; |
||||
|
|
||||
|
public string Name => _symbol.Name; |
||||
|
|
||||
|
public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat); |
||||
|
|
||||
|
public string FullName => $"{Namespace}.{Name}"; |
||||
|
|
||||
|
public IXamlAssembly Assembly => _assembly; |
||||
|
|
||||
|
public IReadOnlyList<IXamlProperty> Properties => |
||||
|
_symbol.GetMembers() |
||||
|
.Where(member => member.Kind == SymbolKind.Property) |
||||
|
.OfType<IPropertySymbol>() |
||||
|
.Select(property => new RoslynProperty(property, _assembly)) |
||||
|
.ToList(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlEventInfo> Events { get; } = new List<IXamlEventInfo>(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlField> Fields { get; } = new List<IXamlField>(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlMethod> Methods { get; } = new List<IXamlMethod>(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlConstructor> Constructors => |
||||
|
_symbol.Constructors |
||||
|
.Select(method => new RoslynConstructor(method, _assembly)) |
||||
|
.ToList(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlType> GenericArguments { get; } = new List<IXamlType>(); |
||||
|
|
||||
|
public bool IsAssignableFrom(IXamlType type) => type == this; |
||||
|
|
||||
|
public IXamlType MakeGenericType(IReadOnlyList<IXamlType> typeArguments) => this; |
||||
|
|
||||
|
public IXamlType GenericTypeDefinition => this; |
||||
|
|
||||
|
public bool IsArray => false; |
||||
|
|
||||
|
public IXamlType ArrayElementType { get; } = null; |
||||
|
|
||||
|
public IXamlType MakeArrayType(int dimensions) => null; |
||||
|
|
||||
|
public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly); |
||||
|
|
||||
|
public bool IsValueType { get; } = false; |
||||
|
|
||||
|
public bool IsEnum { get; } = false; |
||||
|
|
||||
|
public IReadOnlyList<IXamlType> Interfaces { get; } = new List<IXamlType>(); |
||||
|
|
||||
|
public bool IsInterface { get; } = false; |
||||
|
|
||||
|
public IXamlType GetEnumUnderlyingType() => null; |
||||
|
|
||||
|
public IReadOnlyList<IXamlType> GenericParameters { get; } = new List<IXamlType>(); |
||||
|
} |
||||
|
|
||||
|
public class RoslynConstructor : IXamlConstructor |
||||
|
{ |
||||
|
private readonly IMethodSymbol _symbol; |
||||
|
private readonly RoslynAssembly _assembly; |
||||
|
|
||||
|
public RoslynConstructor(IMethodSymbol symbol, RoslynAssembly assembly) |
||||
|
{ |
||||
|
_symbol = symbol; |
||||
|
_assembly = assembly; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(IXamlConstructor other) => |
||||
|
other is RoslynConstructor roslynConstructor && |
||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynConstructor._symbol); |
||||
|
|
||||
|
public bool IsPublic => true; |
||||
|
|
||||
|
public bool IsStatic => false; |
||||
|
|
||||
|
public IReadOnlyList<IXamlType> Parameters => |
||||
|
_symbol.Parameters |
||||
|
.Select(parameter => parameter.Type) |
||||
|
.OfType<INamedTypeSymbol>() |
||||
|
.Select(type => new RoslynType(type, _assembly)) |
||||
|
.ToList(); |
||||
|
} |
||||
|
|
||||
|
public class RoslynProperty : IXamlProperty |
||||
|
{ |
||||
|
private readonly IPropertySymbol _symbol; |
||||
|
private readonly RoslynAssembly _assembly; |
||||
|
|
||||
|
public RoslynProperty(IPropertySymbol symbol, RoslynAssembly assembly) |
||||
|
{ |
||||
|
_symbol = symbol; |
||||
|
_assembly = assembly; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(IXamlProperty other) => |
||||
|
other is RoslynProperty roslynProperty && |
||||
|
SymbolEqualityComparer.Default.Equals(_symbol, roslynProperty._symbol); |
||||
|
|
||||
|
public string Name => _symbol.Name; |
||||
|
|
||||
|
public IXamlType PropertyType => |
||||
|
_symbol.Type is INamedTypeSymbol namedTypeSymbol |
||||
|
? new RoslynType(namedTypeSymbol, _assembly) |
||||
|
: null; |
||||
|
|
||||
|
public IXamlMethod Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly); |
||||
|
|
||||
|
public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly); |
||||
|
|
||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>(); |
||||
|
|
||||
|
public IReadOnlyList<IXamlType> IndexerParameters { get; } = new List<IXamlType>(); |
||||
|
} |
||||
|
|
||||
|
public class RoslynMethod : IXamlMethod |
||||
|
{ |
||||
|
private readonly IMethodSymbol _symbol; |
||||
|
private readonly RoslynAssembly _assembly; |
||||
|
|
||||
|
public RoslynMethod(IMethodSymbol symbol, RoslynAssembly assembly) |
||||
|
{ |
||||
|
_symbol = symbol; |
||||
|
_assembly = assembly; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(IXamlMethod other) => |
||||
|
other is RoslynMethod roslynMethod && |
||||
|
SymbolEqualityComparer.Default.Equals(roslynMethod._symbol, _symbol); |
||||
|
|
||||
|
public string Name => _symbol.Name; |
||||
|
|
||||
|
public bool IsPublic => true; |
||||
|
|
||||
|
public bool IsStatic => false; |
||||
|
|
||||
|
public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly); |
||||
|
|
||||
|
public IReadOnlyList<IXamlType> Parameters => |
||||
|
_symbol.Parameters.Select(parameter => parameter.Type) |
||||
|
.OfType<INamedTypeSymbol>() |
||||
|
.Select(type => new RoslynType(type, _assembly)) |
||||
|
.ToList(); |
||||
|
|
||||
|
public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly); |
||||
|
|
||||
|
public IXamlMethod MakeGenericMethod(IReadOnlyList<IXamlType> typeArguments) => null; |
||||
|
|
||||
|
public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = new List<IXamlCustomAttribute>(); |
||||
|
} |
||||
|
} |
||||
@ -1,12 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
|
|
||||
namespace XamlNameReferenceGenerator.Parsers |
|
||||
{ |
|
||||
public class XamlXCompiledNameReferenceXamlParser : INameReferenceXamlParser |
|
||||
{ |
|
||||
public List<(string TypeName, string Name)> GetNamedControls(string xaml) |
|
||||
{ |
|
||||
throw new System.NotImplementedException(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,32 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.CodeAnalysis.CSharp; |
||||
|
using XamlNameReferenceGenerator.Infrastructure; |
||||
|
using XamlX; |
||||
|
using XamlX.Parsers; |
||||
|
|
||||
|
namespace XamlNameReferenceGenerator.Parsers |
||||
|
{ |
||||
|
internal class XamlXNameReferenceXamlParser : INameReferenceXamlParser |
||||
|
{ |
||||
|
private readonly CSharpCompilation _compilation; |
||||
|
|
||||
|
public XamlXNameReferenceXamlParser(CSharpCompilation compilation) => _compilation = compilation; |
||||
|
|
||||
|
public IReadOnlyList<(string TypeName, string Name)> GetNamedControls(string xaml) |
||||
|
{ |
||||
|
var parsed = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string> |
||||
|
{ |
||||
|
{XamlNamespaces.Blend2008, XamlNamespaces.Blend2008} |
||||
|
}); |
||||
|
|
||||
|
MiniCompiler |
||||
|
.CreateDefault(new RoslynTypeSystem(_compilation)) |
||||
|
.Transform(parsed); |
||||
|
|
||||
|
var visitor = new MiniNamedControlCollector(); |
||||
|
parsed.Root.Visit(visitor); |
||||
|
parsed.Root.VisitChildren(visitor); |
||||
|
return visitor.Controls; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,67 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using XamlX; |
|
||||
using XamlX.Ast; |
|
||||
using XamlX.Parsers; |
|
||||
|
|
||||
namespace XamlNameReferenceGenerator.Parsers |
|
||||
{ |
|
||||
public class XamlXRawNameReferenceXamlParser : INameReferenceXamlParser |
|
||||
{ |
|
||||
public List<(string TypeName, string Name)> GetNamedControls(string xaml) |
|
||||
{ |
|
||||
var parsed = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string> |
|
||||
{ |
|
||||
{XamlNamespaces.Blend2008, XamlNamespaces.Blend2008} |
|
||||
}); |
|
||||
|
|
||||
var visitor = new XamlAstCollector(); |
|
||||
parsed.Root.Visit(visitor); |
|
||||
parsed.Root.VisitChildren(visitor); |
|
||||
return visitor.Controls; |
|
||||
} |
|
||||
|
|
||||
private class XamlAstCollector : IXamlAstVisitor |
|
||||
{ |
|
||||
public List<(string TypeName, string Name)> Controls { get; } = new List<(string TypeName, string Name)>(); |
|
||||
|
|
||||
public IXamlAstNode Visit(IXamlAstNode node) |
|
||||
{ |
|
||||
if (node is XamlAstObjectNode element && element.Type is XamlAstXmlTypeReference type) |
|
||||
{ |
|
||||
foreach (var child in element.Children) |
|
||||
{ |
|
||||
var nameValue = ResolveNameDirectiveOrDefault(child); |
|
||||
if (nameValue == null) continue; |
|
||||
|
|
||||
var typeNamePair = (type.Name, nameValue); |
|
||||
if (!Controls.Contains(typeNamePair)) |
|
||||
Controls.Add(typeNamePair); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return node; |
|
||||
} |
|
||||
|
|
||||
public void Push(IXamlAstNode node) { } |
|
||||
|
|
||||
public void Pop() { } |
|
||||
|
|
||||
private static string ResolveNameDirectiveOrDefault(IXamlAstNode node) => |
|
||||
node switch |
|
||||
{ |
|
||||
XamlAstXamlPropertyValueNode propertyValueNode when |
|
||||
propertyValueNode.Property is XamlAstNamePropertyReference reference && |
|
||||
reference.Name == "Name" && |
|
||||
propertyValueNode.Values.Count > 0 && |
|
||||
propertyValueNode.Values[0] is XamlAstTextNode nameNode => nameNode.Text, |
|
||||
|
|
||||
XamlAstXmlDirective xmlDirective when |
|
||||
xmlDirective.Name == "Name" && |
|
||||
xmlDirective.Values.Count > 0 && |
|
||||
xmlDirective.Values[0] is XamlAstTextNode xNameNode => xNameNode.Text, |
|
||||
|
|
||||
_ => null |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue