Browse Source

feature: Use XamlX for Type Resolution (#5)

pull/10407/head
Artyom V. Gorchakov 5 years ago
committed by GitHub
parent
commit
08ed528420
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      README.md
  2. 5
      src/XamlNameReferenceGenerator.Sandbox/SignUpView.xaml.cs
  3. 42
      src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs
  4. 54
      src/XamlNameReferenceGenerator/Infrastructure/MiniNamedControlCollector.cs
  5. 7
      src/XamlNameReferenceGenerator/Infrastructure/PhysicalFileDebugger.cs
  6. 281
      src/XamlNameReferenceGenerator/Infrastructure/RoslynTypeSystem.cs
  7. 52
      src/XamlNameReferenceGenerator/NameReferenceGenerator.cs
  8. 4
      src/XamlNameReferenceGenerator/Parsers/INameReferenceXamlParser.cs
  9. 12
      src/XamlNameReferenceGenerator/Parsers/XamlXCompiledNameReferenceXamlParser.cs
  10. 32
      src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs
  11. 67
      src/XamlNameReferenceGenerator/Parsers/XamlXRawNameReferenceXamlParser.cs
  12. 2
      src/XamlNameReferenceGenerator/Parsers/XmlDocumentNameReferenceXamlParser.cs

18
README.md

@ -52,22 +52,20 @@ For the [`SignUpView` view class](https://github.com/worldbeater/XamlNameReferen
```cs
// <auto-generated />
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace XamlNameReferenceGenerator.Sandbox
{
public partial class SignUpView
{
public TextBox UserNameTextBox => this.FindControl<TextBox>("UserNameTextBox");
public TextBlock UserNameValidation => this.FindControl<TextBlock>("UserNameValidation");
public TextBox PasswordTextBox => this.FindControl<TextBox>("PasswordTextBox");
public TextBlock PasswordValidation => this.FindControl<TextBlock>("PasswordValidation");
public TextBox ConfirmPasswordTextBox => this.FindControl<TextBox>("ConfirmPasswordTextBox");
public TextBlock ConfirmPasswordValidation => this.FindControl<TextBlock>("ConfirmPasswordValidation");
public Button SignUpButton => this.FindControl<Button>("SignUpButton");
public TextBlock CompoundValidation => this.FindControl<TextBlock>("CompoundValidation");
public XamlNameReferenceGenerator.Sandbox.Controls.CustomTextBox UserNameTextBox => this.FindControl<XamlNameReferenceGenerator.Sandbox.Controls.CustomTextBox>("UserNameTextBox");
public Avalonia.Controls.TextBlock UserNameValidation => this.FindControl<Avalonia.Controls.TextBlock>("UserNameValidation");
public Avalonia.Controls.TextBox PasswordTextBox => this.FindControl<Avalonia.Controls.TextBox>("PasswordTextBox");
public Avalonia.Controls.TextBlock PasswordValidation => this.FindControl<Avalonia.Controls.TextBlock>("PasswordValidation");
public Avalonia.Controls.TextBox ConfirmPasswordTextBox => this.FindControl<Avalonia.Controls.TextBox>("ConfirmPasswordTextBox");
public Avalonia.Controls.TextBlock ConfirmPasswordValidation => this.FindControl<Avalonia.Controls.TextBlock>("ConfirmPasswordValidation");
public Avalonia.Controls.Button SignUpButton => this.FindControl<Avalonia.Controls.Button>("SignUpButton");
public Avalonia.Controls.TextBlock CompoundValidation => this.FindControl<Avalonia.Controls.TextBlock>("CompoundValidation");
}
}
```

5
src/XamlNameReferenceGenerator.Sandbox/SignUpView.xaml.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace XamlNameReferenceGenerator.Sandbox
@ -11,7 +10,7 @@ namespace XamlNameReferenceGenerator.Sandbox
/// references are living in a separate partial class file. See also:
/// https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/
/// </summary>
[GenerateTypedNameReferences(AdditionalNamespaces = new[] {"XamlNameReferenceGenerator.Sandbox.Controls"})]
[GenerateTypedNameReferences]
public partial class SignUpView : Window
{
public SignUpView()

42
src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs

@ -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();
}
}

54
src/XamlNameReferenceGenerator/Infrastructure/MiniNamedControlCollector.cs

@ -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
};
}
}

7
src/XamlNameReferenceGenerator/NameReferenceDebugger.cs → src/XamlNameReferenceGenerator/Infrastructure/PhysicalFileDebugger.cs

@ -1,13 +1,14 @@
using System;
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;
public NameReferenceDebugger(string path) => _path = path;
public PhysicalFileDebugger(string path = DefaultPath) => _path = path;
public string Debug(Func<string> function)
{

281
src/XamlNameReferenceGenerator/Infrastructure/RoslynTypeSystem.cs

@ -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>();
}
}

52
src/XamlNameReferenceGenerator/NameReferenceGenerator.cs

@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;
using XamlNameReferenceGenerator.Infrastructure;
using XamlNameReferenceGenerator.Parsers;
namespace XamlNameReferenceGenerator
@ -20,23 +21,16 @@ using System;
namespace XamlNameReferenceGenerator
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
sealed class GenerateTypedNameReferencesAttribute : Attribute
{
public GenerateTypedNameReferencesAttribute() { }
public string[] AdditionalNamespaces { get; set; } = null;
}
sealed class GenerateTypedNameReferencesAttribute : Attribute { }
}
";
private const string DebugPath = @"C:\Users\prizr\Documents\GitHub\XamlNameReferenceGenerator\debug.txt";
private static readonly INameReferenceXamlParser XamlParser = new XamlXRawNameReferenceXamlParser();
private static readonly NameReferenceDebugger Debugger = new NameReferenceDebugger(DebugPath);
private static readonly PhysicalFileDebugger Debugger = new PhysicalFileDebugger();
private static readonly SymbolDisplayFormat SymbolDisplayFormat = new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters |
SymbolDisplayGenericsOptions.IncludeTypeConstraints |
SymbolDisplayGenericsOptions.IncludeVariance);
SymbolDisplayGenericsOptions.IncludeVariance);
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new NameReferenceSyntaxReceiver());
@ -48,8 +42,10 @@ namespace XamlNameReferenceGenerator
if (!(context.SyntaxReceiver is NameReferenceSyntaxReceiver receiver))
return;
var symbols = UnpackAnnotatedTypes((CSharpCompilation) context.Compilation, receiver);
foreach (var (typeSymbol, additionalNamespaces) in symbols)
var compilation = (CSharpCompilation) context.Compilation;
var xamlParser = new XamlXNameReferenceXamlParser(compilation);
var symbols = UnpackAnnotatedTypes(compilation, receiver);
foreach (var typeSymbol in symbols)
{
var relevantXamlFile = context.AdditionalFiles
.First(text =>
@ -57,30 +53,26 @@ namespace XamlNameReferenceGenerator
text.Path.EndsWith($"{typeSymbol.Name}.axaml"));
var sourceCode = Debugger.Debug(
() => GenerateSourceCode(typeSymbol, relevantXamlFile, additionalNamespaces));
() => GenerateSourceCode(xamlParser, typeSymbol, relevantXamlFile));
context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8));
}
}
private static string GenerateSourceCode(
INameReferenceXamlParser xamlParser,
INamedTypeSymbol classSymbol,
AdditionalText xamlFile,
IList<string> additionalNamespaces)
AdditionalText xamlFile)
{
var className = classSymbol.Name;
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat);
var namespaces = additionalNamespaces.Select(name => $"using {name};");
var namedControls = XamlParser
var namedControls = xamlParser
.GetNamedControls(xamlFile.GetText()!.ToString())
.Select(info => " " +
$"public {info.TypeName} {info.Name} => " +
$"this.FindControl<{info.TypeName}>(\"{info.Name}\");");
return $@"// <auto-generated />
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
{string.Join("\n", namespaces)}
namespace {nameSpace}
{{
@ -92,7 +84,7 @@ namespace {nameSpace}
";
}
private static IReadOnlyList<(INamedTypeSymbol Type, IList<string> Namespaces)> UnpackAnnotatedTypes(
private static IReadOnlyList<INamedTypeSymbol> UnpackAnnotatedTypes(
CSharpCompilation existingCompilation,
NameReferenceSyntaxReceiver nameReferenceSyntaxReceiver)
{
@ -102,8 +94,8 @@ namespace {nameSpace}
SourceText.From(AttributeCode, Encoding.UTF8),
options));
var symbols = new List<INamedTypeSymbol>();
var attributeSymbol = compilation.GetTypeByMetadataName(AttributeName);
var symbols = new List<(INamedTypeSymbol Type, IList<string> Namespaces)>();
foreach (var candidateClass in nameReferenceSyntaxReceiver.CandidateClasses)
{
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
@ -114,19 +106,7 @@ namespace {nameSpace}
if (relevantAttribute != null)
{
var additionalNamespaces = new List<string>();
if (relevantAttribute.NamedArguments.Any(kvp => kvp.Key == "AdditionalNamespaces"))
{
additionalNamespaces = relevantAttribute
.NamedArguments
.First(kvp => kvp.Key == "AdditionalNamespaces")
.Value.Values
.Where(constant => !constant.IsNull)
.Select(constant => constant.Value!.ToString())
.ToList();
}
symbols.Add((typeSymbol, additionalNamespaces));
symbols.Add(typeSymbol);
}
}

4
src/XamlNameReferenceGenerator/Parsers/INameReferenceXamlParser.cs

@ -2,8 +2,8 @@
namespace XamlNameReferenceGenerator.Parsers
{
public interface INameReferenceXamlParser
internal interface INameReferenceXamlParser
{
List<(string TypeName, string Name)> GetNamedControls(string xaml);
IReadOnlyList<(string TypeName, string Name)> GetNamedControls(string xaml);
}
}

12
src/XamlNameReferenceGenerator/Parsers/XamlXCompiledNameReferenceXamlParser.cs

@ -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();
}
}
}

32
src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs

@ -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;
}
}
}

67
src/XamlNameReferenceGenerator/Parsers/XamlXRawNameReferenceXamlParser.cs

@ -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
};
}
}
}

2
src/XamlNameReferenceGenerator/Parsers/XmlDocumentNameReferenceXamlParser.cs

@ -6,7 +6,7 @@ namespace XamlNameReferenceGenerator.Parsers
{
internal class XmlDocumentNameReferenceXamlParser : INameReferenceXamlParser
{
public List<(string TypeName, string Name)> GetNamedControls(string xaml)
public IReadOnlyList<(string TypeName, string Name)> GetNamedControls(string xaml)
{
var document = new XmlDocument();
document.LoadXml(xaml);

Loading…
Cancel
Save