diff --git a/external/.gitkeep.txt b/external/.gitkeep
similarity index 100%
rename from external/.gitkeep.txt
rename to external/.gitkeep
diff --git a/src/Avalonia.NameGenerator.Sandbox/App.xaml b/src/Avalonia.NameGenerator.Sandbox/App.xaml
index 832760aa12..889bbe1e66 100644
--- a/src/Avalonia.NameGenerator.Sandbox/App.xaml
+++ b/src/Avalonia.NameGenerator.Sandbox/App.xaml
@@ -2,6 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Avalonia.NameGenerator.Sandbox.App">
-
+
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs b/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs
index 2edb66f525..12210d4bce 100644
--- a/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs
+++ b/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs
@@ -9,7 +9,8 @@ namespace Avalonia.NameGenerator.Sandbox
public override void OnFrameworkInitializationCompleted()
{
- new SignUpView().Show();
+ var view = new SignUpView();
+ view.Show();
base.OnFrameworkInitializationCompleted();
}
}
diff --git a/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj b/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj
index 30e9ae2347..a0218f4bec 100644
--- a/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj
+++ b/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj
@@ -4,13 +4,8 @@
net5
preview
false
+ true
-
-
-
-
-
-
%(Filename)
@@ -18,11 +13,10 @@
Designer
+
-
+
diff --git a/src/Avalonia.NameGenerator.Sandbox/Program.cs b/src/Avalonia.NameGenerator.Sandbox/Program.cs
index 84de29af16..39d3d0ff7f 100644
--- a/src/Avalonia.NameGenerator.Sandbox/Program.cs
+++ b/src/Avalonia.NameGenerator.Sandbox/Program.cs
@@ -6,10 +6,10 @@ namespace Avalonia.NameGenerator.Sandbox
{
public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
- public static AppBuilder BuildAvaloniaApp()
+ private static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
.UseReactiveUI()
.UsePlatformDetect()
- .LogToDebug();
+ .LogToTrace();
}
}
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml b/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml
index 78c9dfc699..b300765827 100644
--- a/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml
+++ b/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml
@@ -10,9 +10,11 @@
UseFloatingWatermark="True" />
@@ -47,4 +49,4 @@
Foreground="Red"
FontSize="12" />
-
\ No newline at end of file
+
diff --git a/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml.cs b/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml.cs
index d946bef821..c5798628ec 100644
--- a/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml.cs
+++ b/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml.cs
@@ -1,5 +1,4 @@
-using Avalonia;
-using Avalonia.Controls;
+using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Avalonia.NameGenerator.Sandbox.Views
diff --git a/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj b/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj
index 9a2f8c86cb..bc73022bc8 100644
--- a/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj
+++ b/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj
@@ -4,19 +4,19 @@
net5
preview
false
+ true
-
-
-
+
+
+
-
-
+
diff --git a/src/Avalonia.NameGenerator.Tests/FindControlNameGeneratorTests.cs b/src/Avalonia.NameGenerator.Tests/FindControlNameGeneratorTests.cs
new file mode 100644
index 0000000000..460c597240
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/FindControlNameGeneratorTests.cs
@@ -0,0 +1,41 @@
+using System.Threading.Tasks;
+using Avalonia.NameGenerator.Resolver;
+using Avalonia.NameGenerator.Tests.GeneratedCode;
+using Avalonia.NameGenerator.Tests.Views;
+using Microsoft.CodeAnalysis.CSharp;
+using Xunit;
+
+namespace Avalonia.NameGenerator.Tests
+{
+ public class FindControlNameGeneratorTests
+ {
+ [Theory]
+ [InlineData(Code.NamedControl, View.NamedControl)]
+ [InlineData(Code.NamedControls, View.NamedControls)]
+ [InlineData(Code.XNamedControl, View.XNamedControl)]
+ [InlineData(Code.XNamedControls, View.XNamedControls)]
+ [InlineData(Code.NoNamedControls, View.NoNamedControls)]
+ [InlineData(Code.CustomControls, View.CustomControls)]
+ [InlineData(Code.DataTemplates, View.DataTemplates)]
+ [InlineData(Code.SignUpView, View.SignUpView)]
+ [InlineData(Code.AttachedProps, View.AttachedProps)]
+ [InlineData(Code.FieldModifier, View.FieldModifier)]
+ public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File(string expectation, string markup)
+ {
+ var xaml = await View.Load(markup);
+ var compilation =
+ View.CreateAvaloniaCompilation()
+ .WithCustomTextBox();
+
+ var resolver = new XamlXNameResolver(compilation);
+ var generator = new FindControlNameGenerator();
+ var code = generator
+ .GenerateNames("SampleView", "Sample.App", resolver.ResolveNames(xaml))
+ .Replace("\r", string.Empty);
+
+ var expected = await Code.Load(expectation);
+ CSharpSyntaxTree.ParseText(code);
+ Assert.Equal(expected.Replace("\r", string.Empty), code);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/AttachedProps.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/AttachedProps.txt
new file mode 100644
index 0000000000..c4202f080d
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/AttachedProps.txt
@@ -0,0 +1,11 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.Controls.TextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/Code.cs b/src/Avalonia.NameGenerator.Tests/GeneratedCode/Code.cs
new file mode 100644
index 0000000000..1f44606788
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/Code.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Avalonia.NameGenerator.Tests.GeneratedCode
+{
+ public class Code
+ {
+ public const string NamedControl = "NamedControl.txt";
+ public const string NamedControls = "NamedControls.txt";
+ public const string XNamedControl = "xNamedControl.txt";
+ public const string XNamedControls = "xNamedControls.txt";
+ public const string NoNamedControls = "NoNamedControls.txt";
+ public const string CustomControls = "CustomControls.txt";
+ public const string DataTemplates = "DataTemplates.txt";
+ public const string SignUpView = "SignUpView.txt";
+ public const string AttachedProps = "AttachedProps.txt";
+ public const string FieldModifier = "FieldModifier.txt";
+
+ public static async Task Load(string generatedCodeResourceName)
+ {
+ var assembly = typeof(XamlXNameResolverTests).Assembly;
+ var fullResourceName = assembly
+ .GetManifestResourceNames()
+ .First(name => name.EndsWith(generatedCodeResourceName));
+
+ await using var stream = assembly.GetManifestResourceStream(fullResourceName);
+ using var reader = new StreamReader(stream!);
+ return await reader.ReadToEndAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/CustomControls.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/CustomControls.txt
new file mode 100644
index 0000000000..b87d95048b
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/CustomControls.txt
@@ -0,0 +1,13 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.ReactiveUI.RoutedViewHost ClrNamespaceRoutedViewHost => this.FindControl("ClrNamespaceRoutedViewHost");
+ internal global::Avalonia.ReactiveUI.RoutedViewHost UriRoutedViewHost => this.FindControl("UriRoutedViewHost");
+ internal global::Controls.CustomTextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/DataTemplates.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/DataTemplates.txt
new file mode 100644
index 0000000000..0e8e6f1f09
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/DataTemplates.txt
@@ -0,0 +1,12 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.Controls.TextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ internal global::Avalonia.Controls.ListBox NamedListBox => this.FindControl("NamedListBox");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/FieldModifier.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/FieldModifier.txt
new file mode 100644
index 0000000000..17df7f2d82
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/FieldModifier.txt
@@ -0,0 +1,16 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ public global::Avalonia.Controls.TextBox FirstNameTextBox => this.FindControl("FirstNameTextBox");
+ public global::Avalonia.Controls.TextBox LastNameTextBox => this.FindControl("LastNameTextBox");
+ protected global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl("PasswordTextBox");
+ private global::Avalonia.Controls.TextBox ConfirmPasswordTextBox => this.FindControl("ConfirmPasswordTextBox");
+ internal global::Avalonia.Controls.Button SignUpButton => this.FindControl("SignUpButton");
+ internal global::Avalonia.Controls.Button RegisterButton => this.FindControl("RegisterButton");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/NamedControl.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/NamedControl.txt
new file mode 100644
index 0000000000..c4202f080d
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/NamedControl.txt
@@ -0,0 +1,11 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.Controls.TextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/NamedControls.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/NamedControls.txt
new file mode 100644
index 0000000000..e7a678d4a4
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/NamedControls.txt
@@ -0,0 +1,13 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.Controls.TextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ internal global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl("PasswordTextBox");
+ internal global::Avalonia.Controls.Button SignUpButton => this.FindControl("SignUpButton");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/NoNamedControls.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/NoNamedControls.txt
new file mode 100644
index 0000000000..7db25c4693
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/NoNamedControls.txt
@@ -0,0 +1,11 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/SignUpView.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/SignUpView.txt
new file mode 100644
index 0000000000..76067e6e7b
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/SignUpView.txt
@@ -0,0 +1,19 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Controls.CustomTextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ internal global::Avalonia.Controls.TextBlock UserNameValidation => this.FindControl("UserNameValidation");
+ internal global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl("PasswordTextBox");
+ internal global::Avalonia.Controls.TextBlock PasswordValidation => this.FindControl("PasswordValidation");
+ internal global::Avalonia.Controls.ListBox AwesomeListView => this.FindControl("AwesomeListView");
+ internal global::Avalonia.Controls.TextBox ConfirmPasswordTextBox => this.FindControl("ConfirmPasswordTextBox");
+ internal global::Avalonia.Controls.TextBlock ConfirmPasswordValidation => this.FindControl("ConfirmPasswordValidation");
+ internal global::Avalonia.Controls.Button SignUpButton => this.FindControl("SignUpButton");
+ internal global::Avalonia.Controls.TextBlock CompoundValidation => this.FindControl("CompoundValidation");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/xNamedControl.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/xNamedControl.txt
new file mode 100644
index 0000000000..c4202f080d
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/xNamedControl.txt
@@ -0,0 +1,11 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.Controls.TextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/GeneratedCode/xNamedControls.txt b/src/Avalonia.NameGenerator.Tests/GeneratedCode/xNamedControls.txt
new file mode 100644
index 0000000000..e7a678d4a4
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/GeneratedCode/xNamedControls.txt
@@ -0,0 +1,13 @@
+//
+
+using Avalonia.Controls;
+
+namespace Sample.App
+{
+ partial class SampleView
+ {
+ internal global::Avalonia.Controls.TextBox UserNameTextBox => this.FindControl("UserNameTextBox");
+ internal global::Avalonia.Controls.TextBox PasswordTextBox => this.FindControl("PasswordTextBox");
+ internal global::Avalonia.Controls.Button SignUpButton => this.FindControl("SignUpButton");
+ }
+}
diff --git a/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs b/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs
index ea9be2c1e0..2b59e8227a 100644
--- a/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs
+++ b/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs
@@ -1,9 +1,9 @@
using System;
using System.ComponentModel;
-using Avalonia.Controls;
+using Avalonia.NameGenerator.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
-using Avalonia.NameGenerator.Infrastructure;
+using Avalonia.NameGenerator.Tests.Views;
using XamlX;
using XamlX.Parsers;
using Xunit;
@@ -41,19 +41,15 @@ namespace Avalonia.NameGenerator.Tests
public void Should_Resolve_Types_From_Simple_Avalonia_Markup()
{
var xaml = XDocumentXamlParser.Parse(AvaloniaXaml);
- var compilation = CreateAvaloniaCompilation();
+ var compilation = View.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") =>
+ private static CSharpCompilation CreateBasicCompilation(string source) =>
CSharpCompilation
- .Create(name, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
+ .Create("BasicLib", 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))
diff --git a/src/Avalonia.NameGenerator.Tests/NameResolverTests.cs b/src/Avalonia.NameGenerator.Tests/NameResolverTests.cs
deleted file mode 100644
index 5b8c31cc29..0000000000
--- a/src/Avalonia.NameGenerator.Tests/NameResolverTests.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-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 Avalonia.NameGenerator.Infrastructure;
-using Xunit;
-
-namespace Avalonia.NameGenerator.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";
- private const string AttachedProps = "AttachedProps.xml";
-
- [Theory]
- [InlineData(NamedControl)]
- [InlineData(XNamedControl)]
- [InlineData(AttachedProps)]
- 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 compilation =
- CreateAvaloniaCompilation()
- .AddSyntaxTrees(
- CSharpSyntaxTree.ParseText(
- "using Avalonia.Controls;" +
- "namespace Controls {" +
- " public class CustomTextBox : TextBox { }" +
- " public class EvilControl { }" +
- "}"));
-
- var xaml = await LoadEmbeddedResource(CustomControls);
- var resolver = new NameResolver(compilation);
- var controls = resolver.ResolveNames(xaml);
-
- Assert.NotEmpty(controls);
- Assert.Equal(3, controls.Count);
- Assert.Equal("ClrNamespaceRoutedViewHost", controls[0].Name);
- Assert.Equal("UriRoutedViewHost", controls[1].Name);
- Assert.Equal("UserNameTextBox", controls[2].Name);
- Assert.Equal(typeof(RoutedViewHost).FullName, controls[0].TypeName);
- Assert.Equal(typeof(RoutedViewHost).FullName, controls[1].TypeName);
- Assert.Equal("Controls.CustomTextBox", controls[2].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 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();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Tests/Views/FieldModifier.xml b/src/Avalonia.NameGenerator.Tests/Views/FieldModifier.xml
new file mode 100644
index 0000000000..5c7926777b
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/Views/FieldModifier.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Tests/Views/View.cs b/src/Avalonia.NameGenerator.Tests/Views/View.cs
new file mode 100644
index 0000000000..c15e381d32
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/Views/View.cs
@@ -0,0 +1,67 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace Avalonia.NameGenerator.Tests.Views
+{
+ public static class View
+ {
+ public const string NamedControl = "NamedControl.xml";
+ public const string NamedControls = "NamedControls.xml";
+ public const string XNamedControl = "xNamedControl.xml";
+ public const string XNamedControls = "xNamedControls.xml";
+ public const string NoNamedControls = "NoNamedControls.xml";
+ public const string CustomControls = "CustomControls.xml";
+ public const string DataTemplates = "DataTemplates.xml";
+ public const string SignUpView = "SignUpView.xml";
+ public const string AttachedProps = "AttachedProps.xml";
+ public const string FieldModifier = "FieldModifier.xml";
+
+ public static async Task Load(string viewName)
+ {
+ var assembly = typeof(XamlXNameResolverTests).Assembly;
+ var fullResourceName = assembly
+ .GetManifestResourceNames()
+ .First(name => name.EndsWith(viewName));
+
+ await using var stream = assembly.GetManifestResourceStream(fullResourceName);
+ using var reader = new StreamReader(stream!);
+ return await reader.ReadToEndAsync();
+ }
+
+ public static CSharpCompilation CreateAvaloniaCompilation()
+ {
+ var compilation = CSharpCompilation
+ .Create("AvaloniaLib", 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);
+ }
+
+ public static CSharpCompilation WithCustomTextBox(this CSharpCompilation compilation) =>
+ compilation.AddSyntaxTrees(
+ CSharpSyntaxTree.ParseText(
+ "using Avalonia.Controls;" +
+ "namespace Controls {" +
+ " public class CustomTextBox : TextBox { }" +
+ " public class EvilControl { }" +
+ "}"));
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs b/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs
new file mode 100644
index 0000000000..a07fd30712
--- /dev/null
+++ b/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs
@@ -0,0 +1,121 @@
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.NameGenerator.Resolver;
+using Avalonia.ReactiveUI;
+using Avalonia.NameGenerator.Tests.Views;
+using Xunit;
+
+namespace Avalonia.NameGenerator.Tests
+{
+ public class XamlXNameResolverTests
+ {
+ [Theory]
+ [InlineData(View.NamedControl)]
+ [InlineData(View.XNamedControl)]
+ [InlineData(View.AttachedProps)]
+ public async Task Should_Resolve_Types_From_Avalonia_Markup_File_With_Named_Control(string resource)
+ {
+ var xaml = await View.Load(resource);
+ var compilation = View.CreateAvaloniaCompilation();
+ var resolver = new XamlXNameResolver(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(View.NamedControls)]
+ [InlineData(View.XNamedControls)]
+ public async Task Should_Resolve_Types_From_Avalonia_Markup_File_With_Named_Controls(string resource)
+ {
+ var xaml = await View.Load(resource);
+ var compilation = View.CreateAvaloniaCompilation();
+ var resolver = new XamlXNameResolver(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 compilation =
+ View.CreateAvaloniaCompilation()
+ .WithCustomTextBox();
+
+ var xaml = await View.Load(View.CustomControls);
+ var resolver = new XamlXNameResolver(compilation);
+ var controls = resolver.ResolveNames(xaml);
+
+ Assert.NotEmpty(controls);
+ Assert.Equal(3, controls.Count);
+ Assert.Equal("ClrNamespaceRoutedViewHost", controls[0].Name);
+ Assert.Equal("UriRoutedViewHost", controls[1].Name);
+ Assert.Equal("UserNameTextBox", controls[2].Name);
+ Assert.Equal(typeof(RoutedViewHost).FullName, controls[0].TypeName);
+ Assert.Equal(typeof(RoutedViewHost).FullName, controls[1].TypeName);
+ Assert.Equal("Controls.CustomTextBox", controls[2].TypeName);
+ }
+
+ [Fact]
+ public async Task Should_Not_Resolve_Named_Controls_From_Avalonia_Markup_File_Without_Named_Controls()
+ {
+ var xaml = await View.Load(View.NoNamedControls);
+ var compilation = View.CreateAvaloniaCompilation();
+ var resolver = new XamlXNameResolver(compilation);
+ var controls = resolver.ResolveNames(xaml);
+
+ Assert.Empty(controls);
+ }
+
+ [Fact]
+ public async Task Should_Not_Resolve_Elements_From_DataTemplates()
+ {
+ var xaml = await View.Load(View.DataTemplates);
+ var compilation = View.CreateAvaloniaCompilation();
+ var resolver = new XamlXNameResolver(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 =
+ View.CreateAvaloniaCompilation()
+ .WithCustomTextBox();
+
+ var xaml = await View.Load(View.SignUpView);
+ var resolver = new XamlXNameResolver(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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator.sln b/src/Avalonia.NameGenerator.sln
index c440bad019..7f92e53c43 100644
--- a/src/Avalonia.NameGenerator.sln
+++ b/src/Avalonia.NameGenerator.sln
@@ -6,6 +6,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.NameGenerator.Sand
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.NameGenerator.Tests", "Avalonia.NameGenerator.Tests\Avalonia.NameGenerator.Tests.csproj", "{B13A0A44-85BC-49A7-970F-6C9BF8BDFD54}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{56EF74A3-1D59-42BC-B7EC-2E07C08B9F95}"
+ProjectSection(SolutionItems) = preProject
+ ..\version.json = ..\version.json
+ ..\.gitignore = ..\.gitignore
+ ..\.gitmodules = ..\.gitmodules
+ Directory.Build.props = Directory.Build.props
+EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj b/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj
index 2e211bf841..4fc813cf60 100644
--- a/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj
+++ b/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj
@@ -6,8 +6,8 @@
false
-
-
+
+
diff --git a/src/Avalonia.NameGenerator/Infrastructure/DataTemplateTransformer.cs b/src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs
similarity index 91%
rename from src/Avalonia.NameGenerator/Infrastructure/DataTemplateTransformer.cs
rename to src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs
index bce02164c1..8296353636 100644
--- a/src/Avalonia.NameGenerator/Infrastructure/DataTemplateTransformer.cs
+++ b/src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs
@@ -1,7 +1,7 @@
using XamlX.Ast;
using XamlX.Transform;
-namespace Avalonia.NameGenerator.Infrastructure
+namespace Avalonia.NameGenerator.Compiler
{
internal class DataTemplateTransformer : IXamlAstTransformer
{
diff --git a/src/Avalonia.NameGenerator/Infrastructure/MiniCompiler.cs b/src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs
similarity index 95%
rename from src/Avalonia.NameGenerator/Infrastructure/MiniCompiler.cs
rename to src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs
index 2f9606748a..64b2a02546 100644
--- a/src/Avalonia.NameGenerator/Infrastructure/MiniCompiler.cs
+++ b/src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs
@@ -1,12 +1,11 @@
using System;
-using System.Collections.Generic;
using XamlX.Compiler;
using XamlX.Emit;
using XamlX.Transform;
using XamlX.Transform.Transformers;
using XamlX.TypeSystem;
-namespace Avalonia.NameGenerator.Infrastructure
+namespace Avalonia.NameGenerator.Compiler
{
internal sealed class MiniCompiler : XamlCompiler