Browse Source

feature: Don't Generate x:Name References for DataTemplates (#10)

pull/10407/head
Artyom V. Gorchakov 6 years ago
committed by GitHub
parent
commit
701daf0176
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/XamlNameReferenceGenerator.Sandbox/App.xaml.cs
  2. 2
      src/XamlNameReferenceGenerator.Sandbox/Program.cs
  3. 4
      src/XamlNameReferenceGenerator.Sandbox/Views/SignUpView.xaml
  4. 5
      src/XamlNameReferenceGenerator.Sandbox/Views/SignUpView.xaml.cs
  5. 31
      src/XamlNameReferenceGenerator.Tests/MiniCompilerTests.cs
  6. 167
      src/XamlNameReferenceGenerator.Tests/NameResolverTests.cs
  7. 7
      src/XamlNameReferenceGenerator.Tests/Views/CustomControls.xml
  8. 17
      src/XamlNameReferenceGenerator.Tests/Views/DataTemplates.xml
  9. 6
      src/XamlNameReferenceGenerator.Tests/Views/NamedControl.xml
  10. 13
      src/XamlNameReferenceGenerator.Tests/Views/NamedControls.xml
  11. 5
      src/XamlNameReferenceGenerator.Tests/Views/NoNamedControls.xml
  12. 44
      src/XamlNameReferenceGenerator.Tests/Views/SignUpView.xml
  13. 6
      src/XamlNameReferenceGenerator.Tests/Views/xNamedControl.xml
  14. 13
      src/XamlNameReferenceGenerator.Tests/Views/xNamedControls.xml
  15. 6
      src/XamlNameReferenceGenerator.Tests/XamlNameReferenceGenerator.Tests.csproj
  16. 13
      src/XamlNameReferenceGenerator.Tests/XamlXNameReferenceXamlParserTests.cs
  17. 17
      src/XamlNameReferenceGenerator/Infrastructure/DataTemplateTransformer.cs
  18. 9
      src/XamlNameReferenceGenerator/Infrastructure/INameResolver.cs
  19. 1
      src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs
  20. 2
      src/XamlNameReferenceGenerator/Infrastructure/NameDirectiveTransformer.cs
  21. 2
      src/XamlNameReferenceGenerator/Infrastructure/NameReceiver.cs
  22. 11
      src/XamlNameReferenceGenerator/Infrastructure/NameResolver.cs
  23. 9
      src/XamlNameReferenceGenerator/NameReferenceGenerator.cs
  24. 9
      src/XamlNameReferenceGenerator/Parsers/INameReferenceXamlParser.cs
  25. 46
      src/XamlNameReferenceGenerator/Parsers/XmlDocumentNameReferenceXamlParser.cs

1
src/XamlNameReferenceGenerator.Sandbox/App.xaml.cs

@ -1,5 +1,6 @@
using Avalonia; using Avalonia;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using XamlNameReferenceGenerator.Sandbox.Views;
namespace XamlNameReferenceGenerator.Sandbox namespace XamlNameReferenceGenerator.Sandbox
{ {

2
src/XamlNameReferenceGenerator.Sandbox/Program.cs

@ -3,7 +3,7 @@ using Avalonia.ReactiveUI;
namespace XamlNameReferenceGenerator.Sandbox namespace XamlNameReferenceGenerator.Sandbox
{ {
class Program internal static class Program
{ {
public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);

4
src/XamlNameReferenceGenerator.Sandbox/SignUpView.xaml → src/XamlNameReferenceGenerator.Sandbox/Views/SignUpView.xaml

@ -1,8 +1,7 @@
<Window xmlns="https://github.com/avaloniaui" <Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sandbox="clr-namespace:XamlNameReferenceGenerator.Sandbox"
xmlns:controls="clr-namespace:XamlNameReferenceGenerator.Sandbox.Controls" xmlns:controls="clr-namespace:XamlNameReferenceGenerator.Sandbox.Controls"
x:Class="XamlNameReferenceGenerator.Sandbox.SignUpView"> x:Class="XamlNameReferenceGenerator.Sandbox.Views.SignUpView">
<StackPanel> <StackPanel>
<!-- Regular name directives. --> <!-- Regular name directives. -->
<controls:CustomTextBox Margin="0 10 0 0" <controls:CustomTextBox Margin="0 10 0 0"
@ -21,6 +20,7 @@
Foreground="Red" Foreground="Red"
FontSize="12" /> FontSize="12" />
<!-- DataTemplates should be removed. -->
<ListBox x:Name="AwesomeListView"> <ListBox x:Name="AwesomeListView">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>

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

@ -1,7 +1,7 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace XamlNameReferenceGenerator.Sandbox namespace XamlNameReferenceGenerator.Sandbox.Views
{ {
/// <summary> /// <summary>
/// This is a sample view class with typed x:Name references generated at compile-time using /// This is a sample view class with typed x:Name references generated at compile-time using
@ -16,6 +16,7 @@ namespace XamlNameReferenceGenerator.Sandbox
public SignUpView() public SignUpView()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
UserNameTextBox.Text = "Joseph!"; UserNameTextBox.Text = "Joseph!";
UserNameValidation.Text = "User name is valid."; UserNameValidation.Text = "User name is valid.";
PasswordTextBox.Text = "qwerty"; PasswordTextBox.Text = "qwerty";
@ -24,6 +25,8 @@ namespace XamlNameReferenceGenerator.Sandbox
ConfirmPasswordValidation.Text = "Password confirmation is valid."; ConfirmPasswordValidation.Text = "Password confirmation is valid.";
SignUpButton.Content = "Sign up please!"; SignUpButton.Content = "Sign up please!";
CompoundValidation.Text = "Everything is okay."; CompoundValidation.Text = "Everything is okay.";
var listView = AwesomeListView;
} }
} }
} }

31
src/XamlNameReferenceGenerator.Tests/MiniCompilerTests.cs

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Controls;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using XamlNameReferenceGenerator.Infrastructure; using XamlNameReferenceGenerator.Infrastructure;
@ -11,34 +12,46 @@ namespace XamlNameReferenceGenerator.Tests
{ {
public class MiniCompilerTests public class MiniCompilerTests
{ {
private const string AvaloniaXaml = "<TextBlock xmlns='clr-namespace:Avalonia.Controls;assembly=Avalonia' />";
private const string MiniClass = "namespace Example { public class Valid { public int Foo() => 21; } }"; private const string MiniClass = "namespace Example { public class Valid { public int Foo() => 21; } }";
private const string MiniInvalidXaml = "<Invalid xmlns='clr-namespace:Example;assembly=Example' />"; private const string MiniInvalidXaml = "<Invalid xmlns='clr-namespace:Example;assembly=Example' />";
private const string MiniValidXaml = "<Valid xmlns='clr-namespace:Example;assembly=Example' />"; private const string MiniValidXaml = "<Valid xmlns='clr-namespace:Example;assembly=Example' />";
[Fact] [Fact]
public void Should_Resolve_Types_From_Valid_Xaml_Markup() public void Should_Resolve_Types_From_Simple_Valid_Xaml_Markup()
{ {
var xaml = XDocumentXamlParser.Parse(MiniValidXaml); var xaml = XDocumentXamlParser.Parse(MiniValidXaml);
var compilation = CreateBasicCompilation(MiniClass, "Example"); var compilation = CreateBasicCompilation(MiniClass);
MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml);
MiniCompiler
.CreateDefault(new RoslynTypeSystem(compilation))
.Transform(xaml);
Assert.NotNull(xaml.Root); Assert.NotNull(xaml.Root);
} }
[Fact] [Fact]
public void Should_Throw_When_Unable_To_Resolve_Types() public void Should_Throw_When_Unable_To_Resolve_Types_From_Simple_Invalid_Markup()
{ {
var xaml = XDocumentXamlParser.Parse(MiniInvalidXaml); var xaml = XDocumentXamlParser.Parse(MiniInvalidXaml);
var compilation = CreateBasicCompilation(MiniClass, "Example"); var compilation = CreateBasicCompilation(MiniClass);
var compiler = MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)); var compiler = MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation));
Assert.Throws<XamlParseException>(() => compiler.Transform(xaml)); Assert.Throws<XamlParseException>(() => compiler.Transform(xaml));
} }
private static CSharpCompilation CreateBasicCompilation(string source, string name) => [Fact]
public void Should_Resolve_Types_From_Simple_Avalonia_Markup()
{
var xaml = XDocumentXamlParser.Parse(AvaloniaXaml);
var compilation = CreateAvaloniaCompilation();
MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml);
Assert.NotNull(xaml.Root);
}
private static CSharpCompilation CreateAvaloniaCompilation(string name = "AvaloniaCompilation") =>
CreateBasicCompilation(string.Empty, name)
.AddReferences(MetadataReference.CreateFromFile(typeof(TextBlock).Assembly.Location));
private static CSharpCompilation CreateBasicCompilation(string source, string name = "BasicCompilation") =>
CSharpCompilation CSharpCompilation
.Create(name, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .Create(name, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location)) .AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))

167
src/XamlNameReferenceGenerator.Tests/NameResolverTests.cs

@ -0,0 +1,167 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.ReactiveUI;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using XamlNameReferenceGenerator.Infrastructure;
using Xunit;
namespace XamlNameReferenceGenerator.Tests
{
public class NameResolverTests
{
private const string NamedControl = "NamedControl.xml";
private const string NamedControls = "NamedControls.xml";
private const string XNamedControl = "xNamedControl.xml";
private const string XNamedControls = "xNamedControls.xml";
private const string NoNamedControls = "NoNamedControls.xml";
private const string CustomControls = "CustomControls.xml";
private const string DataTemplates = "DataTemplates.xml";
private const string SignUpView = "SignUpView.xml";
[Theory]
[InlineData(NamedControl)]
[InlineData(XNamedControl)]
public async Task Should_Resolve_Types_From_Avalonia_Markup_File_With_Named_Control(string resource)
{
var xaml = await LoadEmbeddedResource(resource);
var compilation = CreateAvaloniaCompilation();
var resolver = new NameResolver(compilation);
var controls = resolver.ResolveNames(xaml);
Assert.NotEmpty(controls);
Assert.Equal(1, controls.Count);
Assert.Equal("UserNameTextBox", controls[0].Name);
Assert.Equal(typeof(TextBox).FullName, controls[0].TypeName);
}
[Theory]
[InlineData(NamedControls)]
[InlineData(XNamedControls)]
public async Task Should_Resolve_Types_From_Avalonia_Markup_File_With_Named_Controls(string resource)
{
var xaml = await LoadEmbeddedResource(resource);
var compilation = CreateAvaloniaCompilation();
var resolver = new NameResolver(compilation);
var controls = resolver.ResolveNames(xaml);
Assert.NotEmpty(controls);
Assert.Equal(3, controls.Count);
Assert.Equal("UserNameTextBox", controls[0].Name);
Assert.Equal("PasswordTextBox", controls[1].Name);
Assert.Equal("SignUpButton", controls[2].Name);
Assert.Equal(typeof(TextBox).FullName, controls[0].TypeName);
Assert.Equal(typeof(TextBox).FullName, controls[1].TypeName);
Assert.Equal(typeof(Button).FullName, controls[2].TypeName);
}
[Fact]
public async Task Should_Resolve_Types_From_Avalonia_Markup_File_With_Custom_Controls()
{
var xaml = await LoadEmbeddedResource(CustomControls);
var compilation = CreateAvaloniaCompilation();
var resolver = new NameResolver(compilation);
var controls = resolver.ResolveNames(xaml);
Assert.NotEmpty(controls);
Assert.Equal(2, controls.Count);
Assert.Equal("ClrNamespaceRoutedViewHost", controls[0].Name);
Assert.Equal("UriRoutedViewHost", controls[1].Name);
Assert.Equal(typeof(RoutedViewHost).FullName, controls[0].TypeName);
Assert.Equal(typeof(RoutedViewHost).FullName, controls[1].TypeName);
}
[Fact]
public async Task Should_Not_Resolve_Named_Controls_From_Avalonia_Markup_File_Without_Named_Controls()
{
var xaml = await LoadEmbeddedResource(NoNamedControls);
var compilation = CreateAvaloniaCompilation();
var resolver = new NameResolver(compilation);
var controls = resolver.ResolveNames(xaml);
Assert.Empty(controls);
}
[Fact]
public async Task Should_Not_Resolve_Elements_From_DataTemplates()
{
var xaml = await LoadEmbeddedResource(DataTemplates);
var compilation = CreateAvaloniaCompilation();
var resolver = new NameResolver(compilation);
var controls = resolver.ResolveNames(xaml);
Assert.NotEmpty(controls);
Assert.Equal(2, controls.Count);
Assert.Equal("UserNameTextBox", controls[0].Name);
Assert.Equal("NamedListBox", controls[1].Name);
Assert.Equal(typeof(TextBox).FullName, controls[0].TypeName);
Assert.Equal(typeof(ListBox).FullName, controls[1].TypeName);
}
[Fact]
public async Task Should_Resolve_Names_From_Complex_Views()
{
var compilation =
CreateAvaloniaCompilation()
.AddSyntaxTrees(
CSharpSyntaxTree.ParseText(
"using Avalonia.Controls;" +
"namespace Controls {" +
" public class CustomTextBox : TextBox { }" +
"}"));
var xaml = await LoadEmbeddedResource(SignUpView);
var resolver = new NameResolver(compilation);
var controls = resolver.ResolveNames(xaml);
Assert.NotEmpty(controls);
Assert.Equal(9, controls.Count);
Assert.Equal("UserNameTextBox", controls[0].Name);
Assert.Equal("UserNameValidation", controls[1].Name);
Assert.Equal("PasswordTextBox", controls[2].Name);
Assert.Equal("PasswordValidation", controls[3].Name);
Assert.Equal("AwesomeListView", controls[4].Name);
Assert.Equal("ConfirmPasswordTextBox", controls[5].Name);
Assert.Equal("ConfirmPasswordValidation", controls[6].Name);
Assert.Equal("SignUpButton", controls[7].Name);
Assert.Equal("CompoundValidation", controls[8].Name);
}
private static CSharpCompilation CreateAvaloniaCompilation(string name = "AvaloniaCompilation2")
{
var compilation = CSharpCompilation
.Create(name, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(IServiceProvider).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(ITypeDescriptorContext).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(ISupportInitialize).Assembly.Location))
.AddReferences(MetadataReference.CreateFromFile(typeof(TypeConverterAttribute).Assembly.Location));
var avaloniaAssemblyLocation = typeof(TextBlock).Assembly.Location;
var avaloniaAssemblyDirectory = Path.GetDirectoryName(avaloniaAssemblyLocation);
var avaloniaAssemblyReferences = Directory
.EnumerateFiles(avaloniaAssemblyDirectory!)
.Where(file => file.EndsWith(".dll") && file.Contains("Avalonia"))
.Select(file => MetadataReference.CreateFromFile(file))
.ToList();
return compilation.AddReferences(avaloniaAssemblyReferences);
}
private static async Task<string> LoadEmbeddedResource(string shortResourceName)
{
var assembly = typeof(NameResolverTests).Assembly;
var fullResourceName = assembly
.GetManifestResourceNames()
.First(name => name.EndsWith(shortResourceName));
await using var stream = assembly.GetManifestResourceStream(fullResourceName);
using var reader = new StreamReader(stream!);
return await reader.ReadToEndAsync();
}
}
}

7
src/XamlNameReferenceGenerator.Tests/Views/CustomControls.xml

@ -0,0 +1,7 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:Avalonia.ReactiveUI;assembly=Avalonia.ReactiveUI"
xmlns:rxui="http://reactiveui.net">
<custom:RoutedViewHost Name="ClrNamespaceRoutedViewHost" />
<rxui:RoutedViewHost Name="UriRoutedViewHost" />
</Window>

17
src/XamlNameReferenceGenerator.Tests/Views/DataTemplates.xml

@ -0,0 +1,17 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox x:Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
<ListBox Name="NamedListBox">
<ListBox.ItemTemplate>
<DataTemplate x:Name="NamedDataTemplate">
<TextBox x:Name="TemplatedTextBox"
Watermark="Templated input"
UseFloatingWatermark="True" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>

6
src/XamlNameReferenceGenerator.Tests/Views/NamedControl.xml

@ -0,0 +1,6 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
</Window>

13
src/XamlNameReferenceGenerator.Tests/Views/NamedControls.xml

@ -0,0 +1,13 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
<TextBox Name="PasswordTextBox"
Watermark="Password input"
UseFloatingWatermark="True" />
<Button Name="SignUpButton"
Content="Sign up" />
</StackPanel>
</Window>

5
src/XamlNameReferenceGenerator.Tests/Views/NoNamedControls.xml

@ -0,0 +1,5 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Watermark="Username input"
UseFloatingWatermark="True" />
</Window>

44
src/XamlNameReferenceGenerator.Tests/Views/SignUpView.xml

@ -0,0 +1,44 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Controls">
<StackPanel>
<controls:CustomTextBox Margin="0 10 0 0"
Name="UserNameTextBox"
Watermark="Please, enter user name..."
UseFloatingWatermark="True" />
<TextBlock Name="UserNameValidation"
Foreground="Red"
FontSize="12" />
<TextBox Margin="0 10 0 0"
Name="PasswordTextBox"
Watermark="Please, enter your password..."
UseFloatingWatermark="True"
PasswordChar="*" />
<TextBlock Name="PasswordValidation"
Foreground="Red"
FontSize="12" />
<ListBox x:Name="AwesomeListView">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="MeaningLessName" Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Margin="0 10 0 0"
x:Name="ConfirmPasswordTextBox"
Watermark="Please, confirm the password..."
UseFloatingWatermark="True"
PasswordChar="*" />
<TextBlock x:Name="ConfirmPasswordValidation"
TextWrapping="Wrap"
Foreground="Red"
FontSize="12" />
<Button Margin="0 10 0 5"
Content="Sign up"
x:Name="SignUpButton" />
<TextBlock x:Name="CompoundValidation"
TextWrapping="Wrap"
Foreground="Red"
FontSize="12" />
</StackPanel>
</Window>

6
src/XamlNameReferenceGenerator.Tests/Views/xNamedControl.xml

@ -0,0 +1,6 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox x:Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
</Window>

13
src/XamlNameReferenceGenerator.Tests/Views/xNamedControls.xml

@ -0,0 +1,13 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox x:Name="UserNameTextBox"
Watermark="Username input"
UseFloatingWatermark="True" />
<TextBox x:Name="PasswordTextBox"
Watermark="Password input"
UseFloatingWatermark="True" />
<Button x:Name="SignUpButton"
Content="Sign up" />
</StackPanel>
</Window>

6
src/XamlNameReferenceGenerator.Tests/XamlNameReferenceGenerator.Tests.csproj

@ -11,12 +11,12 @@
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" /> <PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.0-preview5" /> <PackageReference Include="Avalonia" Version="0.10.0-preview5" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview5" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.0-preview5" /> <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.0-preview5" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Views/*.xml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../XamlNameReferenceGenerator/XamlNameReferenceGenerator.csproj" /> <ProjectReference Include="../XamlNameReferenceGenerator/XamlNameReferenceGenerator.csproj" />
</ItemGroup> </ItemGroup>

13
src/XamlNameReferenceGenerator.Tests/XamlXNameReferenceXamlParserTests.cs

@ -1,13 +0,0 @@
using Xunit;
namespace XamlNameReferenceGenerator.Tests
{
public class XamlXNameReferenceXamlParserTests
{
[Fact]
public void Should_Resolve_Named_Controls_From_Xaml_Markup()
{
// TODO: Fix all stuff.
}
}
}

17
src/XamlNameReferenceGenerator/Infrastructure/DataTemplateTransformer.cs

@ -0,0 +1,17 @@
using XamlX.Ast;
using XamlX.Transform;
namespace XamlNameReferenceGenerator.Infrastructure
{
internal class DataTemplateTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlAstObjectNode objectNode &&
objectNode.Type is XamlAstXmlTypeReference typeReference &&
typeReference.Name == "DataTemplate")
objectNode.Children.Clear();
return node;
}
}
}

9
src/XamlNameReferenceGenerator/Infrastructure/INameResolver.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace XamlNameReferenceGenerator.Infrastructure
{
internal interface INameResolver
{
IReadOnlyList<(string TypeName, string Name)> ResolveNames(string xaml);
}
}

1
src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs

@ -27,6 +27,7 @@ namespace XamlNameReferenceGenerator.Infrastructure
: base(configuration, new XamlLanguageEmitMappings<object, IXamlEmitResult>(), false) : base(configuration, new XamlLanguageEmitMappings<object, IXamlEmitResult>(), false)
{ {
Transformers.Add(new NameDirectiveTransformer()); Transformers.Add(new NameDirectiveTransformer());
Transformers.Add(new DataTemplateTransformer());
Transformers.Add(new KnownDirectivesTransformer()); Transformers.Add(new KnownDirectivesTransformer());
Transformers.Add(new XamlIntrinsicsTransformer()); Transformers.Add(new XamlIntrinsicsTransformer());
Transformers.Add(new XArgumentsTransformer()); Transformers.Add(new XArgumentsTransformer());

2
src/XamlNameReferenceGenerator/Infrastructure/NameDirectiveTransformer.cs

@ -4,7 +4,7 @@ using XamlX.Transform;
namespace XamlNameReferenceGenerator.Infrastructure namespace XamlNameReferenceGenerator.Infrastructure
{ {
public class NameDirectiveTransformer : IXamlAstTransformer internal class NameDirectiveTransformer : IXamlAstTransformer
{ {
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{ {

2
src/XamlNameReferenceGenerator/Infrastructure/NamedControlCollector.cs → src/XamlNameReferenceGenerator/Infrastructure/NameReceiver.cs

@ -3,7 +3,7 @@ using XamlX.Ast;
namespace XamlNameReferenceGenerator.Infrastructure namespace XamlNameReferenceGenerator.Infrastructure
{ {
internal sealed class NamedControlCollector : IXamlAstVisitor internal sealed class NameReceiver : IXamlAstVisitor
{ {
private readonly List<(string TypeName, string Name)> _items = new List<(string TypeName, string Name)>(); private readonly List<(string TypeName, string Name)> _items = new List<(string TypeName, string Name)>();

11
src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs → src/XamlNameReferenceGenerator/Infrastructure/NameResolver.cs

@ -1,19 +1,18 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using XamlNameReferenceGenerator.Infrastructure;
using XamlX; using XamlX;
using XamlX.Parsers; using XamlX.Parsers;
namespace XamlNameReferenceGenerator.Parsers namespace XamlNameReferenceGenerator.Infrastructure
{ {
internal class XamlXNameReferenceXamlParser : INameReferenceXamlParser internal class NameResolver : INameResolver
{ {
private const string AvaloniaXmlnsAttribute = "Avalonia.Metadata.XmlnsDefinitionAttribute"; private const string AvaloniaXmlnsAttribute = "Avalonia.Metadata.XmlnsDefinitionAttribute";
private readonly CSharpCompilation _compilation; private readonly CSharpCompilation _compilation;
public XamlXNameReferenceXamlParser(CSharpCompilation compilation) => _compilation = compilation; public NameResolver(CSharpCompilation compilation) => _compilation = compilation;
public IReadOnlyList<(string TypeName, string Name)> GetNamedControls(string xaml) public IReadOnlyList<(string TypeName, string Name)> ResolveNames(string xaml)
{ {
var parsed = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string> var parsed = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string>
{ {
@ -24,7 +23,7 @@ namespace XamlNameReferenceGenerator.Parsers
.CreateDefault(new RoslynTypeSystem(_compilation), AvaloniaXmlnsAttribute) .CreateDefault(new RoslynTypeSystem(_compilation), AvaloniaXmlnsAttribute)
.Transform(parsed); .Transform(parsed);
var visitor = new NamedControlCollector(); var visitor = new NameReceiver();
parsed.Root.Visit(visitor); parsed.Root.Visit(visitor);
parsed.Root.VisitChildren(visitor); parsed.Root.VisitChildren(visitor);
return visitor.Controls; return visitor.Controls;

9
src/XamlNameReferenceGenerator/NameReferenceGenerator.cs

@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Text;
using System.Text; using System.Text;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using XamlNameReferenceGenerator.Infrastructure; using XamlNameReferenceGenerator.Infrastructure;
using XamlNameReferenceGenerator.Parsers;
[assembly: InternalsVisibleTo("XamlNameReferenceGenerator.Tests")] [assembly: InternalsVisibleTo("XamlNameReferenceGenerator.Tests")]
@ -47,7 +46,7 @@ namespace XamlNameReferenceGenerator
return; return;
var compilation = (CSharpCompilation) context.Compilation; var compilation = (CSharpCompilation) context.Compilation;
var xamlParser = new XamlXNameReferenceXamlParser(compilation); var xamlParser = new NameResolver(compilation);
var symbols = UnpackAnnotatedTypes(compilation, receiver); var symbols = UnpackAnnotatedTypes(compilation, receiver);
foreach (var typeSymbol in symbols) foreach (var typeSymbol in symbols)
{ {
@ -98,14 +97,14 @@ namespace XamlNameReferenceGenerator
} }
private static string GenerateSourceCode( private static string GenerateSourceCode(
INameReferenceXamlParser xamlParser, INameResolver nameResolver,
INamedTypeSymbol classSymbol, INamedTypeSymbol classSymbol,
AdditionalText xamlFile) AdditionalText xamlFile)
{ {
var className = classSymbol.Name; var className = classSymbol.Name;
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat); var nameSpace = classSymbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat);
var namedControls = xamlParser var namedControls = nameResolver
.GetNamedControls(xamlFile.GetText()!.ToString()) .ResolveNames(xamlFile.GetText()!.ToString())
.Select(info => " " + .Select(info => " " +
$"public {info.TypeName} {info.Name} => " + $"public {info.TypeName} {info.Name} => " +
$"this.FindControl<{info.TypeName}>(\"{info.Name}\");"); $"this.FindControl<{info.TypeName}>(\"{info.Name}\");");

9
src/XamlNameReferenceGenerator/Parsers/INameReferenceXamlParser.cs

@ -1,9 +0,0 @@
using System.Collections.Generic;
namespace XamlNameReferenceGenerator.Parsers
{
internal interface INameReferenceXamlParser
{
IReadOnlyList<(string TypeName, string Name)> GetNamedControls(string xaml);
}
}

46
src/XamlNameReferenceGenerator/Parsers/XmlDocumentNameReferenceXamlParser.cs

@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml;
namespace XamlNameReferenceGenerator.Parsers
{
internal class XmlDocumentNameReferenceXamlParser : INameReferenceXamlParser
{
public IReadOnlyList<(string TypeName, string Name)> GetNamedControls(string xaml)
{
var document = new XmlDocument();
document.LoadXml(xaml);
var names = new List<(string TypeName, string Name)>();
IterateThroughAllNodes(document, node =>
{
var type = node.Name;
if (type.Contains(":"))
type = type.Split(':')[1];
var name = node.Attributes?["x:Name"]?.Value ??
node.Attributes?["Name"]?.Value;
if (!string.IsNullOrWhiteSpace(name))
names.Add((type, name));
});
return names;
}
private static void IterateThroughAllNodes(XmlDocument doc, Action<XmlNode> elementVisitor)
{
foreach (XmlNode node in doc.ChildNodes)
{
IterateNode(node, elementVisitor);
}
}
private static void IterateNode(XmlNode node, Action<XmlNode> elementVisitor)
{
elementVisitor(node);
foreach (XmlNode childNode in node.ChildNodes)
{
IterateNode(childNode, elementVisitor);
}
}
}
}
Loading…
Cancel
Save