From f0a69d4663b1185ba822a78e55396bf010d77896 Mon Sep 17 00:00:00 2001 From: Artyom Date: Fri, 7 Jan 2022 14:42:38 +0300 Subject: [PATCH 1/4] Use file scoped namespaces in the sandbox --- .../App.xaml.cs | 21 +++--- .../Avalonia.NameGenerator.Sandbox.csproj | 3 + .../Controls/CustomTextBox.cs | 8 ++- src/Avalonia.NameGenerator.Sandbox/Program.cs | 19 +++-- .../ViewModels/SignUpViewModel.cs | 70 +++++++++++++++++++ .../Views/SignUpView.xaml | 29 +++----- .../Views/SignUpView.xaml.cs | 67 ++++++++++++------ 7 files changed, 154 insertions(+), 63 deletions(-) create mode 100644 src/Avalonia.NameGenerator.Sandbox/ViewModels/SignUpViewModel.cs diff --git a/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs b/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs index 12210d4..896de18 100644 --- a/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs +++ b/src/Avalonia.NameGenerator.Sandbox/App.xaml.cs @@ -1,17 +1,20 @@ using Avalonia.Markup.Xaml; +using Avalonia.NameGenerator.Sandbox.ViewModels; using Avalonia.NameGenerator.Sandbox.Views; -namespace Avalonia.NameGenerator.Sandbox +namespace Avalonia.NameGenerator.Sandbox; + +public class App : Application { - public class App : Application - { - public override void Initialize() => AvaloniaXamlLoader.Load(this); + public override void Initialize() => AvaloniaXamlLoader.Load(this); - public override void OnFrameworkInitializationCompleted() + public override void OnFrameworkInitializationCompleted() + { + var view = new SignUpView { - var view = new SignUpView(); - view.Show(); - base.OnFrameworkInitializationCompleted(); - } + ViewModel = new SignUpViewModel() + }; + view.Show(); + base.OnFrameworkInitializationCompleted(); } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj b/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj index a0218f4..cd0e0a4 100644 --- a/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj +++ b/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj @@ -19,4 +19,7 @@ + + + diff --git a/src/Avalonia.NameGenerator.Sandbox/Controls/CustomTextBox.cs b/src/Avalonia.NameGenerator.Sandbox/Controls/CustomTextBox.cs index 264baf5..543d73c 100644 --- a/src/Avalonia.NameGenerator.Sandbox/Controls/CustomTextBox.cs +++ b/src/Avalonia.NameGenerator.Sandbox/Controls/CustomTextBox.cs @@ -1,6 +1,10 @@ +using System; using Avalonia.Controls; +using Avalonia.Styling; -namespace Avalonia.NameGenerator.Sandbox.Controls +namespace Avalonia.NameGenerator.Sandbox.Controls; + +public class CustomTextBox : TextBox, IStyleable { - public class CustomTextBox : TextBox { } + Type IStyleable.StyleKey => typeof(TextBox); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Sandbox/Program.cs b/src/Avalonia.NameGenerator.Sandbox/Program.cs index 39d3d0f..6133430 100644 --- a/src/Avalonia.NameGenerator.Sandbox/Program.cs +++ b/src/Avalonia.NameGenerator.Sandbox/Program.cs @@ -1,15 +1,14 @@ using Avalonia.ReactiveUI; -namespace Avalonia.NameGenerator.Sandbox +namespace Avalonia.NameGenerator.Sandbox; + +internal static class Program { - internal static class Program - { - public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - private static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UseReactiveUI() - .UsePlatformDetect() - .LogToTrace(); - } + private static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UseReactiveUI() + .UsePlatformDetect() + .LogToTrace(); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Sandbox/ViewModels/SignUpViewModel.cs b/src/Avalonia.NameGenerator.Sandbox/ViewModels/SignUpViewModel.cs new file mode 100644 index 0000000..fc54364 --- /dev/null +++ b/src/Avalonia.NameGenerator.Sandbox/ViewModels/SignUpViewModel.cs @@ -0,0 +1,70 @@ +using System.Reactive; +using ReactiveUI; +using ReactiveUI.Validation.Extensions; +using ReactiveUI.Validation.Helpers; + +namespace Avalonia.NameGenerator.Sandbox.ViewModels; + +public class SignUpViewModel : ReactiveValidationObject +{ + private string _userName = string.Empty; + private string _password = string.Empty; + private string _confirmPassword = string.Empty; + + public SignUpViewModel() + { + this.ValidationRule( + vm => vm.UserName, + name => !string.IsNullOrWhiteSpace(name), + "UserName is required."); + + this.ValidationRule( + vm => vm.Password, + password => !string.IsNullOrWhiteSpace(password), + "Password is required."); + + this.ValidationRule( + vm => vm.Password, + password => password?.Length > 2, + password => $"Password should be longer, current length: {password.Length}"); + + this.ValidationRule( + vm => vm.ConfirmPassword, + confirmation => !string.IsNullOrWhiteSpace(confirmation), + "Confirm password field is required."); + + var passwordsObservable = + this.WhenAnyValue( + x => x.Password, + x => x.ConfirmPassword, + (password, confirmation) => + password == confirmation); + + this.ValidationRule( + vm => vm.ConfirmPassword, + passwordsObservable, + "Passwords must match."); + + SignUp = ReactiveCommand.Create(() => {}, this.IsValid()); + } + + public ReactiveCommand SignUp { get; } + + public string UserName + { + get => _userName; + set => this.RaiseAndSetIfChanged(ref _userName, value); + } + + public string Password + { + get => _password; + set => this.RaiseAndSetIfChanged(ref _password, value); + } + + public string ConfirmPassword + { + get => _confirmPassword; + set => this.RaiseAndSetIfChanged(ref _confirmPassword, value); + } +} \ 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 b300765..a187d05 100644 --- a/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml +++ b/src/Avalonia.NameGenerator.Sandbox/Views/SignUpView.xaml @@ -2,36 +2,23 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:Avalonia.NameGenerator.Sandbox.Controls" x:Class="Avalonia.NameGenerator.Sandbox.Views.SignUpView"> - - + + - + - - - - - - - - - - - - +/// This is a sample view class with typed x:Name references generated using +/// .NET 5 source generators. The class has to be partial because x:Name +/// references are living in a separate partial class file. See also: +/// https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/ +/// +public partial class SignUpView : ReactiveWindow { - /// - /// This is a sample view class with typed x:Name references generated at compile-time using - /// .NET 5 source generators. The class should be marked with [GenerateTypedNameReferences], - /// this attribute is also compile-time generated. The class has to be partial because x:Name - /// references are living in a separate partial class file. See also: - /// https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/ - /// - public partial class SignUpView : Window + public SignUpView() { - public SignUpView() + // The InitializeComponent method is also generated automatically + // and lives in the autogenerated part of the partial class. + InitializeComponent(); + this.WhenActivated(disposables => { - InitializeComponent(); + this.Bind(ViewModel, x => x.UserName, x => x.UserNameTextBox.Text) + .DisposeWith(disposables); + this.Bind(ViewModel, x => x.Password, x => x.PasswordTextBox.Text) + .DisposeWith(disposables); + this.Bind(ViewModel, x => x.ConfirmPassword, x => x.ConfirmPasswordTextBox.Text) + .DisposeWith(disposables); + this.BindCommand(ViewModel, x => x.SignUp, x => x.SignUpButton) + .DisposeWith(disposables); + + this.BindValidation(ViewModel, x => x.UserName, x => x.UserNameValidation.Text) + .DisposeWith(disposables); + this.BindValidation(ViewModel, x => x.Password, x => x.PasswordValidation.Text) + .DisposeWith(disposables); + this.BindValidation(ViewModel, x => x.ConfirmPassword, x => x.ConfirmPasswordValidation.Text) + .DisposeWith(disposables); + + var newLineFormatter = new SingleLineFormatter(Environment.NewLine); + this.BindValidation(ViewModel, x => x.CompoundValidation.Text, newLineFormatter) + .DisposeWith(disposables); + + // The references to text boxes below are also auto generated. + // Use Ctrl+Click in order to view the generated sources. UserNameTextBox.Text = "Joseph!"; - UserNameValidation.Text = "User name is valid."; - PasswordTextBox.Text = "qwerty"; - PasswordValidation.Text = "Password is valid."; - ConfirmPasswordTextBox.Text = "qwerty"; - ConfirmPasswordValidation.Text = "Password confirmation is valid."; - SignUpButton.Content = "Sign up please!"; - CompoundValidation.Text = "Everything is okay."; - AwesomeListView.VirtualizationMode = ItemVirtualizationMode.None; - } + PasswordTextBox.Text = "1234"; + ConfirmPasswordTextBox.Text = "1234"; + }); } } \ No newline at end of file From 09bdd3f412c1ab959accab2acfb305aff4c6d740 Mon Sep 17 00:00:00 2001 From: Artyom Date: Fri, 7 Jan 2022 14:57:50 +0300 Subject: [PATCH 2/4] More file-scoped namespaces --- .../GlobPatternTests.cs | 47 ++- .../InitializeComponentCode.cs | 53 ++- .../InitializeComponentTests.cs | 85 ++-- .../MiniCompilerTests.cs | 91 ++-- .../GeneratedCode/OnlyPropertiesCode.cs | 47 ++- .../OnlyProperties/OnlyPropertiesTests.cs | 69 ++-- .../Views/View.cs | 105 +++-- .../XamlXClassResolverTests.cs | 53 ++- .../XamlXNameResolverTests.cs | 215 +++++----- .../Compiler/DataTemplateTransformer.cs | 21 +- .../Compiler/MiniCompiler.cs | 63 ++- .../Compiler/NameDirectiveTransformer.cs | 37 +- .../Compiler/RoslynTypeSystem.cs | 391 +++++++++--------- .../Domain/ICodeGenerator.cs | 9 +- .../Domain/IGlobPattern.cs | 9 +- .../Domain/INameGenerator.cs | 13 +- .../Domain/INameResolver.cs | 13 +- .../Domain/IViewResolver.cs | 14 +- .../Domain/IsExternalInit.cs | 7 +- src/Avalonia.NameGenerator/Generator.cs | 133 +++--- .../Generator/AvaloniaNameGenerator.cs | 81 ++-- .../Generator/GlobPattern.cs | 23 +- .../Generator/GlobPatternGroup.cs | 19 +- .../InitializeComponentCodeGenerator.cs | 65 ++- .../Generator/OnlyPropertiesCodeGenerator.cs | 23 +- .../Generator/XamlXNameResolver.cs | 141 ++++--- .../Generator/XamlXViewResolver.cs | 131 +++--- .../GeneratorContextExtensions.cs | 47 ++- .../GeneratorOptions.cs | 135 +++--- 29 files changed, 1055 insertions(+), 1085 deletions(-) diff --git a/src/Avalonia.NameGenerator.Tests/GlobPatternTests.cs b/src/Avalonia.NameGenerator.Tests/GlobPatternTests.cs index 68ee426..c255beb 100644 --- a/src/Avalonia.NameGenerator.Tests/GlobPatternTests.cs +++ b/src/Avalonia.NameGenerator.Tests/GlobPatternTests.cs @@ -1,32 +1,31 @@ using Avalonia.NameGenerator.Generator; using Xunit; -namespace Avalonia.NameGenerator.Tests +namespace Avalonia.NameGenerator.Tests; + +public class GlobPatternTests { - public class GlobPatternTests + [Theory] + [InlineData("*", "anything", true)] + [InlineData("", "anything", false)] + [InlineData("Views/*", "Views/SignUpView.xaml", true)] + [InlineData("Views/*", "Extensions/SignUpView.xaml", false)] + [InlineData("*SignUpView*", "Extensions/SignUpView.xaml", true)] + [InlineData("*SignUpView.paml", "Extensions/SignUpView.xaml", false)] + [InlineData("*.xaml", "Extensions/SignUpView.xaml", true)] + public void Should_Match_Glob_Expressions(string pattern, string value, bool matches) { - [Theory] - [InlineData("*", "anything", true)] - [InlineData("", "anything", false)] - [InlineData("Views/*", "Views/SignUpView.xaml", true)] - [InlineData("Views/*", "Extensions/SignUpView.xaml", false)] - [InlineData("*SignUpView*", "Extensions/SignUpView.xaml", true)] - [InlineData("*SignUpView.paml", "Extensions/SignUpView.xaml", false)] - [InlineData("*.xaml", "Extensions/SignUpView.xaml", true)] - public void Should_Match_Glob_Expressions(string pattern, string value, bool matches) - { - Assert.Equal(matches, new GlobPattern(pattern).Matches(value)); - } + Assert.Equal(matches, new GlobPattern(pattern).Matches(value)); + } - [Theory] - [InlineData("Views/SignUpView.xaml", true, new[] { "*.xaml", "Extensions/*" })] - [InlineData("Extensions/SignUpView.paml", true, new[] { "*.xaml", "Extensions/*" })] - [InlineData("Extensions/SignUpView.paml", false, new[] { "*.xaml", "Views/*" })] - [InlineData("anything", true, new[] { "*", "*" })] - [InlineData("anything", false, new[] { "", "" })] - public void Should_Match_Glob_Pattern_Groups(string value, bool matches, string[] patterns) - { - Assert.Equal(matches, new GlobPatternGroup(patterns).Matches(value)); - } + [Theory] + [InlineData("Views/SignUpView.xaml", true, new[] { "*.xaml", "Extensions/*" })] + [InlineData("Extensions/SignUpView.paml", true, new[] { "*.xaml", "Extensions/*" })] + [InlineData("Extensions/SignUpView.paml", false, new[] { "*.xaml", "Views/*" })] + [InlineData("anything", true, new[] { "*", "*" })] + [InlineData("anything", false, new[] { "", "" })] + public void Should_Match_Glob_Pattern_Groups(string value, bool matches, string[] patterns) + { + Assert.Equal(matches, new GlobPatternGroup(patterns).Matches(value)); } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Tests/InitializeComponent/GeneratedInitializeComponent/InitializeComponentCode.cs b/src/Avalonia.NameGenerator.Tests/InitializeComponent/GeneratedInitializeComponent/InitializeComponentCode.cs index 59ee051..309e866 100644 --- a/src/Avalonia.NameGenerator.Tests/InitializeComponent/GeneratedInitializeComponent/InitializeComponentCode.cs +++ b/src/Avalonia.NameGenerator.Tests/InitializeComponent/GeneratedInitializeComponent/InitializeComponentCode.cs @@ -2,35 +2,34 @@ using System.Linq; using System.Threading.Tasks; -namespace Avalonia.NameGenerator.Tests.InitializeComponent.GeneratedInitializeComponent +namespace Avalonia.NameGenerator.Tests.InitializeComponent.GeneratedInitializeComponent; + +public static class InitializeComponentCode { - public static class InitializeComponentCode - { - 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 FieldModifier = "FieldModifier.txt"; - public const string AttachedProps = "AttachedProps.txt"; - public const string AttachedPropsWithDevTools = "AttachedPropsWithDevTools.txt"; - public const string ControlWithoutWindow = "ControlWithoutWindow.txt"; + 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 FieldModifier = "FieldModifier.txt"; + public const string AttachedProps = "AttachedProps.txt"; + public const string AttachedPropsWithDevTools = "AttachedPropsWithDevTools.txt"; + public const string ControlWithoutWindow = "ControlWithoutWindow.txt"; - public static async Task Load(string generatedCodeResourceName) - { - var assembly = typeof(XamlXNameResolverTests).Assembly; - var fullResourceName = assembly - .GetManifestResourceNames() - .First(name => name.Contains("InitializeComponent") && - name.Contains("GeneratedInitializeComponent") && - name.EndsWith(generatedCodeResourceName)); + public static async Task Load(string generatedCodeResourceName) + { + var assembly = typeof(XamlXNameResolverTests).Assembly; + var fullResourceName = assembly + .GetManifestResourceNames() + .First(name => name.Contains("InitializeComponent") && + name.Contains("GeneratedInitializeComponent") && + name.EndsWith(generatedCodeResourceName)); - await using var stream = assembly.GetManifestResourceStream(fullResourceName); - using var reader = new StreamReader(stream!); - return await reader.ReadToEndAsync(); - } + 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/InitializeComponent/InitializeComponentTests.cs b/src/Avalonia.NameGenerator.Tests/InitializeComponent/InitializeComponentTests.cs index d5e3343..d3339a2 100644 --- a/src/Avalonia.NameGenerator.Tests/InitializeComponent/InitializeComponentTests.cs +++ b/src/Avalonia.NameGenerator.Tests/InitializeComponent/InitializeComponentTests.cs @@ -7,57 +7,56 @@ using Microsoft.CodeAnalysis.CSharp; using Xunit; -namespace Avalonia.NameGenerator.Tests.InitializeComponent +namespace Avalonia.NameGenerator.Tests.InitializeComponent; + +public class InitializeComponentTests { - public class InitializeComponentTests + [Theory] + [InlineData(InitializeComponentCode.NamedControl, View.NamedControl, false)] + [InlineData(InitializeComponentCode.NamedControls, View.NamedControls, false)] + [InlineData(InitializeComponentCode.XNamedControl, View.XNamedControl, false)] + [InlineData(InitializeComponentCode.XNamedControls, View.XNamedControls, false)] + [InlineData(InitializeComponentCode.NoNamedControls, View.NoNamedControls, false)] + [InlineData(InitializeComponentCode.CustomControls, View.CustomControls, false)] + [InlineData(InitializeComponentCode.DataTemplates, View.DataTemplates, false)] + [InlineData(InitializeComponentCode.SignUpView, View.SignUpView, false)] + [InlineData(InitializeComponentCode.FieldModifier, View.FieldModifier, false)] + [InlineData(InitializeComponentCode.AttachedPropsWithDevTools, View.AttachedProps, true)] + [InlineData(InitializeComponentCode.AttachedProps, View.AttachedProps, false)] + [InlineData(InitializeComponentCode.ControlWithoutWindow, View.ControlWithoutWindow, true)] + [InlineData(InitializeComponentCode.ControlWithoutWindow, View.ControlWithoutWindow, false)] + public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File( + string expectation, + string markup, + bool devToolsMode) { - [Theory] - [InlineData(InitializeComponentCode.NamedControl, View.NamedControl, false)] - [InlineData(InitializeComponentCode.NamedControls, View.NamedControls, false)] - [InlineData(InitializeComponentCode.XNamedControl, View.XNamedControl, false)] - [InlineData(InitializeComponentCode.XNamedControls, View.XNamedControls, false)] - [InlineData(InitializeComponentCode.NoNamedControls, View.NoNamedControls, false)] - [InlineData(InitializeComponentCode.CustomControls, View.CustomControls, false)] - [InlineData(InitializeComponentCode.DataTemplates, View.DataTemplates, false)] - [InlineData(InitializeComponentCode.SignUpView, View.SignUpView, false)] - [InlineData(InitializeComponentCode.FieldModifier, View.FieldModifier, false)] - [InlineData(InitializeComponentCode.AttachedPropsWithDevTools, View.AttachedProps, true)] - [InlineData(InitializeComponentCode.AttachedProps, View.AttachedProps, false)] - [InlineData(InitializeComponentCode.ControlWithoutWindow, View.ControlWithoutWindow, true)] - [InlineData(InitializeComponentCode.ControlWithoutWindow, View.ControlWithoutWindow, false)] - public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File( - string expectation, - string markup, - bool devToolsMode) - { - var excluded = devToolsMode ? null : "Avalonia.Diagnostics"; - var compilation = - View.CreateAvaloniaCompilation(excluded) - .WithCustomTextBox(); + var excluded = devToolsMode ? null : "Avalonia.Diagnostics"; + var compilation = + View.CreateAvaloniaCompilation(excluded) + .WithCustomTextBox(); - var types = new RoslynTypeSystem(compilation); - var classResolver = new XamlXViewResolver( - types, - MiniCompiler.CreateDefault( - new RoslynTypeSystem(compilation), - MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); + var types = new RoslynTypeSystem(compilation); + var classResolver = new XamlXViewResolver( + types, + MiniCompiler.CreateDefault( + new RoslynTypeSystem(compilation), + MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); - var xaml = await View.Load(markup); - var classInfo = classResolver.ResolveView(xaml); - var nameResolver = new XamlXNameResolver(); - var names = nameResolver.ResolveNames(classInfo.Xaml); + var xaml = await View.Load(markup); + var classInfo = classResolver.ResolveView(xaml); + var nameResolver = new XamlXNameResolver(); + var names = nameResolver.ResolveNames(classInfo.Xaml); - var generator = new InitializeComponentCodeGenerator(types); + var generator = new InitializeComponentCodeGenerator(types); - var code = generator - .GenerateCode("SampleView", "Sample.App", classInfo.XamlType, names) - .Replace("\r", string.Empty); + var code = generator + .GenerateCode("SampleView", "Sample.App", classInfo.XamlType, names) + .Replace("\r", string.Empty); - var expected = await InitializeComponentCode.Load(expectation); + var expected = await InitializeComponentCode.Load(expectation); - CSharpSyntaxTree.ParseText(code); - Assert.Equal(expected.Replace("\r", string.Empty), code); - } + CSharpSyntaxTree.ParseText(code); + Assert.Equal(expected.Replace("\r", string.Empty), code); } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs b/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs index 2b59e82..51e8149 100644 --- a/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs +++ b/src/Avalonia.NameGenerator.Tests/MiniCompilerTests.cs @@ -8,53 +8,52 @@ using XamlX.Parsers; using Xunit; -namespace Avalonia.NameGenerator.Tests +namespace Avalonia.NameGenerator.Tests; + +public class MiniCompilerTests { - public class MiniCompilerTests + private const string AvaloniaXaml = ""; + private const string MiniClass = "namespace Example { public class Valid { public int Foo() => 21; } }"; + private const string MiniInvalidXaml = ""; + private const string MiniValidXaml = ""; + + [Fact] + public void Should_Resolve_Types_From_Simple_Valid_Xaml_Markup() + { + var xaml = XDocumentXamlParser.Parse(MiniValidXaml); + var compilation = CreateBasicCompilation(MiniClass); + MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml); + + Assert.NotNull(xaml.Root); + } + + [Fact] + public void Should_Throw_When_Unable_To_Resolve_Types_From_Simple_Invalid_Markup() { - private const string AvaloniaXaml = ""; - private const string MiniClass = "namespace Example { public class Valid { public int Foo() => 21; } }"; - private const string MiniInvalidXaml = ""; - private const string MiniValidXaml = ""; - - [Fact] - public void Should_Resolve_Types_From_Simple_Valid_Xaml_Markup() - { - var xaml = XDocumentXamlParser.Parse(MiniValidXaml); - var compilation = CreateBasicCompilation(MiniClass); - MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml); - - Assert.NotNull(xaml.Root); - } - - [Fact] - public void Should_Throw_When_Unable_To_Resolve_Types_From_Simple_Invalid_Markup() - { - var xaml = XDocumentXamlParser.Parse(MiniInvalidXaml); - var compilation = CreateBasicCompilation(MiniClass); - var compiler = MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)); - - Assert.Throws(() => compiler.Transform(xaml)); - } - - [Fact] - public void Should_Resolve_Types_From_Simple_Avalonia_Markup() - { - var xaml = XDocumentXamlParser.Parse(AvaloniaXaml); - var compilation = View.CreateAvaloniaCompilation(); - MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml); - - Assert.NotNull(xaml.Root); - } - - private static CSharpCompilation CreateBasicCompilation(string source) => - CSharpCompilation - .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)) - .AddReferences(MetadataReference.CreateFromFile(typeof(ISupportInitialize).Assembly.Location)) - .AddReferences(MetadataReference.CreateFromFile(typeof(TypeConverterAttribute).Assembly.Location)) - .AddSyntaxTrees(CSharpSyntaxTree.ParseText(source)); + var xaml = XDocumentXamlParser.Parse(MiniInvalidXaml); + var compilation = CreateBasicCompilation(MiniClass); + var compiler = MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)); + + Assert.Throws(() => compiler.Transform(xaml)); + } + + [Fact] + public void Should_Resolve_Types_From_Simple_Avalonia_Markup() + { + var xaml = XDocumentXamlParser.Parse(AvaloniaXaml); + var compilation = View.CreateAvaloniaCompilation(); + MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml); + + Assert.NotNull(xaml.Root); } + + private static CSharpCompilation CreateBasicCompilation(string source) => + CSharpCompilation + .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)) + .AddReferences(MetadataReference.CreateFromFile(typeof(ISupportInitialize).Assembly.Location)) + .AddReferences(MetadataReference.CreateFromFile(typeof(TypeConverterAttribute).Assembly.Location)) + .AddSyntaxTrees(CSharpSyntaxTree.ParseText(source)); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Tests/OnlyProperties/GeneratedCode/OnlyPropertiesCode.cs b/src/Avalonia.NameGenerator.Tests/OnlyProperties/GeneratedCode/OnlyPropertiesCode.cs index 0651c53..b2c253b 100644 --- a/src/Avalonia.NameGenerator.Tests/OnlyProperties/GeneratedCode/OnlyPropertiesCode.cs +++ b/src/Avalonia.NameGenerator.Tests/OnlyProperties/GeneratedCode/OnlyPropertiesCode.cs @@ -3,32 +3,31 @@ using System.Reflection; using System.Threading.Tasks; -namespace Avalonia.NameGenerator.Tests.OnlyProperties.GeneratedCode +namespace Avalonia.NameGenerator.Tests.OnlyProperties.GeneratedCode; + +public static class OnlyPropertiesCode { - public static class OnlyPropertiesCode - { - 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 const string ControlWithoutWindow = "ControlWithoutWindow.txt"; + 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 const string ControlWithoutWindow = "ControlWithoutWindow.txt"; - public static async Task Load(string generatedCodeResourceName) - { - var assembly = typeof(XamlXNameResolverTests).Assembly; - var fullResourceName = assembly - .GetManifestResourceNames() - .First(name => name.Contains("OnlyProperties") && name.EndsWith(generatedCodeResourceName)); + public static async Task Load(string generatedCodeResourceName) + { + var assembly = typeof(XamlXNameResolverTests).Assembly; + var fullResourceName = assembly + .GetManifestResourceNames() + .First(name => name.Contains("OnlyProperties") && name.EndsWith(generatedCodeResourceName)); - await using var stream = assembly.GetManifestResourceStream(fullResourceName); - using var reader = new StreamReader(stream!); - return await reader.ReadToEndAsync(); - } + 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/OnlyProperties/OnlyPropertiesTests.cs b/src/Avalonia.NameGenerator.Tests/OnlyProperties/OnlyPropertiesTests.cs index 4ecacd2..41471c8 100644 --- a/src/Avalonia.NameGenerator.Tests/OnlyProperties/OnlyPropertiesTests.cs +++ b/src/Avalonia.NameGenerator.Tests/OnlyProperties/OnlyPropertiesTests.cs @@ -6,47 +6,46 @@ using Microsoft.CodeAnalysis.CSharp; using Xunit; -namespace Avalonia.NameGenerator.Tests.OnlyProperties +namespace Avalonia.NameGenerator.Tests.OnlyProperties; + +public class OnlyPropertiesTests { - public class OnlyPropertiesTests + [Theory] + [InlineData(OnlyPropertiesCode.NamedControl, View.NamedControl)] + [InlineData(OnlyPropertiesCode.NamedControls, View.NamedControls)] + [InlineData(OnlyPropertiesCode.XNamedControl, View.XNamedControl)] + [InlineData(OnlyPropertiesCode.XNamedControls, View.XNamedControls)] + [InlineData(OnlyPropertiesCode.NoNamedControls, View.NoNamedControls)] + [InlineData(OnlyPropertiesCode.CustomControls, View.CustomControls)] + [InlineData(OnlyPropertiesCode.DataTemplates, View.DataTemplates)] + [InlineData(OnlyPropertiesCode.SignUpView, View.SignUpView)] + [InlineData(OnlyPropertiesCode.AttachedProps, View.AttachedProps)] + [InlineData(OnlyPropertiesCode.FieldModifier, View.FieldModifier)] + [InlineData(OnlyPropertiesCode.ControlWithoutWindow, View.ControlWithoutWindow)] + public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File(string expectation, string markup) { - [Theory] - [InlineData(OnlyPropertiesCode.NamedControl, View.NamedControl)] - [InlineData(OnlyPropertiesCode.NamedControls, View.NamedControls)] - [InlineData(OnlyPropertiesCode.XNamedControl, View.XNamedControl)] - [InlineData(OnlyPropertiesCode.XNamedControls, View.XNamedControls)] - [InlineData(OnlyPropertiesCode.NoNamedControls, View.NoNamedControls)] - [InlineData(OnlyPropertiesCode.CustomControls, View.CustomControls)] - [InlineData(OnlyPropertiesCode.DataTemplates, View.DataTemplates)] - [InlineData(OnlyPropertiesCode.SignUpView, View.SignUpView)] - [InlineData(OnlyPropertiesCode.AttachedProps, View.AttachedProps)] - [InlineData(OnlyPropertiesCode.FieldModifier, View.FieldModifier)] - [InlineData(OnlyPropertiesCode.ControlWithoutWindow, View.ControlWithoutWindow)] - public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File(string expectation, string markup) - { - var compilation = - View.CreateAvaloniaCompilation() - .WithCustomTextBox(); + var compilation = + View.CreateAvaloniaCompilation() + .WithCustomTextBox(); - var classResolver = new XamlXViewResolver( + var classResolver = new XamlXViewResolver( + new RoslynTypeSystem(compilation), + MiniCompiler.CreateDefault( new RoslynTypeSystem(compilation), - MiniCompiler.CreateDefault( - new RoslynTypeSystem(compilation), - MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); + MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); - var xaml = await View.Load(markup); - var classInfo = classResolver.ResolveView(xaml); - var nameResolver = new XamlXNameResolver(); - var names = nameResolver.ResolveNames(classInfo.Xaml); + var xaml = await View.Load(markup); + var classInfo = classResolver.ResolveView(xaml); + var nameResolver = new XamlXNameResolver(); + var names = nameResolver.ResolveNames(classInfo.Xaml); - var generator = new OnlyPropertiesCodeGenerator(); - var code = generator - .GenerateCode("SampleView", "Sample.App", classInfo.XamlType, names) - .Replace("\r", string.Empty); + var generator = new OnlyPropertiesCodeGenerator(); + var code = generator + .GenerateCode("SampleView", "Sample.App", classInfo.XamlType, names) + .Replace("\r", string.Empty); - var expected = await OnlyPropertiesCode.Load(expectation); - CSharpSyntaxTree.ParseText(code); - Assert.Equal(expected.Replace("\r", string.Empty), code); - } + var expected = await OnlyPropertiesCode.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/Views/View.cs b/src/Avalonia.NameGenerator.Tests/Views/View.cs index 0fca5c7..f028019 100644 --- a/src/Avalonia.NameGenerator.Tests/Views/View.cs +++ b/src/Avalonia.NameGenerator.Tests/Views/View.cs @@ -7,64 +7,63 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -namespace Avalonia.NameGenerator.Tests.Views +namespace Avalonia.NameGenerator.Tests.Views; + +public static class View { - 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 const string ControlWithoutWindow = "ControlWithoutWindow.xml"; + 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 const string ControlWithoutWindow = "ControlWithoutWindow.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 async Task Load(string viewName) + { + var assembly = typeof(XamlXNameResolverTests).Assembly; + var fullResourceName = assembly + .GetManifestResourceNames() + .First(name => name.EndsWith(viewName)); - public static CSharpCompilation CreateAvaloniaCompilation(string excludedPattern = null) - { - 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)); + await using var stream = assembly.GetManifestResourceStream(fullResourceName); + using var reader = new StreamReader(stream!); + return await reader.ReadToEndAsync(); + } - var avaloniaAssemblyLocation = typeof(TextBlock).Assembly.Location; - var avaloniaAssemblyDirectory = Path.GetDirectoryName(avaloniaAssemblyLocation); - var avaloniaAssemblyReferences = Directory - .EnumerateFiles(avaloniaAssemblyDirectory!) - .Where(file => file.EndsWith(".dll") && - file.Contains("Avalonia") && - (string.IsNullOrWhiteSpace(excludedPattern) || !file.Contains(excludedPattern))) - .Select(file => MetadataReference.CreateFromFile(file)) - .ToList(); + public static CSharpCompilation CreateAvaloniaCompilation(string excludedPattern = null) + { + 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)); - return compilation.AddReferences(avaloniaAssemblyReferences); - } + var avaloniaAssemblyLocation = typeof(TextBlock).Assembly.Location; + var avaloniaAssemblyDirectory = Path.GetDirectoryName(avaloniaAssemblyLocation); + var avaloniaAssemblyReferences = Directory + .EnumerateFiles(avaloniaAssemblyDirectory!) + .Where(file => file.EndsWith(".dll") && + file.Contains("Avalonia") && + (string.IsNullOrWhiteSpace(excludedPattern) || !file.Contains(excludedPattern))) + .Select(file => MetadataReference.CreateFromFile(file)) + .ToList(); - public static CSharpCompilation WithCustomTextBox(this CSharpCompilation compilation) => - compilation.AddSyntaxTrees( - CSharpSyntaxTree.ParseText( - "using Avalonia.Controls;" + - "namespace Controls {" + - " public class CustomTextBox : TextBox { }" + - " public class EvilControl { }" + - "}")); + 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/XamlXClassResolverTests.cs b/src/Avalonia.NameGenerator.Tests/XamlXClassResolverTests.cs index a0d9ec4..f0a1114 100644 --- a/src/Avalonia.NameGenerator.Tests/XamlXClassResolverTests.cs +++ b/src/Avalonia.NameGenerator.Tests/XamlXClassResolverTests.cs @@ -4,36 +4,35 @@ using Avalonia.NameGenerator.Tests.Views; using Xunit; -namespace Avalonia.NameGenerator.Tests +namespace Avalonia.NameGenerator.Tests; + +public class XamlXClassResolverTests { - public class XamlXClassResolverTests + [Theory] + [InlineData("Sample.App", "NamedControl", View.NamedControl)] + [InlineData("Sample.App", "AttachedProps", View.AttachedProps)] + [InlineData("Sample.App", "CustomControls", View.CustomControls)] + [InlineData("Sample.App", "DataTemplates", View.DataTemplates)] + [InlineData("Sample.App", "FieldModifier", View.FieldModifier)] + [InlineData("Sample.App", "NamedControls", View.NamedControls)] + [InlineData("Sample.App", "NoNamedControls", View.NoNamedControls)] + [InlineData("Sample.App", "SignUpView", View.SignUpView)] + [InlineData("Sample.App", "xNamedControl", View.XNamedControl)] + [InlineData("Sample.App", "xNamedControls", View.XNamedControls)] + public async Task Should_Resolve_Base_Class_From_Xaml_File(string nameSpace, string className, string markup) { - [Theory] - [InlineData("Sample.App", "NamedControl", View.NamedControl)] - [InlineData("Sample.App", "AttachedProps", View.AttachedProps)] - [InlineData("Sample.App", "CustomControls", View.CustomControls)] - [InlineData("Sample.App", "DataTemplates", View.DataTemplates)] - [InlineData("Sample.App", "FieldModifier", View.FieldModifier)] - [InlineData("Sample.App", "NamedControls", View.NamedControls)] - [InlineData("Sample.App", "NoNamedControls", View.NoNamedControls)] - [InlineData("Sample.App", "SignUpView", View.SignUpView)] - [InlineData("Sample.App", "xNamedControl", View.XNamedControl)] - [InlineData("Sample.App", "xNamedControls", View.XNamedControls)] - public async Task Should_Resolve_Base_Class_From_Xaml_File(string nameSpace, string className, string markup) - { - var xaml = await View.Load(markup); - var compilation = View - .CreateAvaloniaCompilation() - .WithCustomTextBox(); + var xaml = await View.Load(markup); + var compilation = View + .CreateAvaloniaCompilation() + .WithCustomTextBox(); - var types = new RoslynTypeSystem(compilation); - var resolver = new XamlXViewResolver( - types, - MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); + var types = new RoslynTypeSystem(compilation); + var resolver = new XamlXViewResolver( + types, + MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); - var resolvedClass = resolver.ResolveView(xaml); - Assert.Equal(className, resolvedClass.ClassName); - Assert.Equal(nameSpace, resolvedClass.Namespace); - } + var resolvedClass = resolver.ResolveView(xaml); + Assert.Equal(className, resolvedClass.ClassName); + Assert.Equal(nameSpace, resolvedClass.Namespace); } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs b/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs index 8447af4..b6c4b13 100644 --- a/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs +++ b/src/Avalonia.NameGenerator.Tests/XamlXNameResolverTests.cs @@ -8,116 +8,115 @@ using Avalonia.NameGenerator.Tests.Views; using Xunit; -namespace Avalonia.NameGenerator.Tests +namespace Avalonia.NameGenerator.Tests; + +public class XamlXNameResolverTests { - 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 controls = 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 controls = 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() { - [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 controls = 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 controls = 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 View.Load(View.CustomControls); - var controls = 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 controls = ResolveNames(xaml); - - Assert.Empty(controls); - } - - [Fact] - public async Task Should_Not_Resolve_Elements_From_DataTemplates() - { - var xaml = await View.Load(View.DataTemplates); - var controls = 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 xaml = await View.Load(View.SignUpView); - var controls = 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 IReadOnlyList ResolveNames(string xaml) - { - var compilation = - View.CreateAvaloniaCompilation() - .WithCustomTextBox(); - - var classResolver = new XamlXViewResolver( + var xaml = await View.Load(View.CustomControls); + var controls = 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 controls = ResolveNames(xaml); + + Assert.Empty(controls); + } + + [Fact] + public async Task Should_Not_Resolve_Elements_From_DataTemplates() + { + var xaml = await View.Load(View.DataTemplates); + var controls = 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 xaml = await View.Load(View.SignUpView); + var controls = 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 IReadOnlyList ResolveNames(string xaml) + { + var compilation = + View.CreateAvaloniaCompilation() + .WithCustomTextBox(); + + var classResolver = new XamlXViewResolver( + new RoslynTypeSystem(compilation), + MiniCompiler.CreateDefault( new RoslynTypeSystem(compilation), - MiniCompiler.CreateDefault( - new RoslynTypeSystem(compilation), - MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); - - var classInfo = classResolver.ResolveView(xaml); - var nameResolver = new XamlXNameResolver(); - return nameResolver.ResolveNames(classInfo.Xaml); - } + MiniCompiler.AvaloniaXmlnsDefinitionAttribute)); + + var classInfo = classResolver.ResolveView(xaml); + var nameResolver = new XamlXNameResolver(); + return nameResolver.ResolveNames(classInfo.Xaml); } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs b/src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs index 8296353..abfd48b 100644 --- a/src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs +++ b/src/Avalonia.NameGenerator/Compiler/DataTemplateTransformer.cs @@ -1,18 +1,17 @@ using XamlX.Ast; using XamlX.Transform; -namespace Avalonia.NameGenerator.Compiler +namespace Avalonia.NameGenerator.Compiler; + +internal class DataTemplateTransformer : IXamlAstTransformer { - internal class DataTemplateTransformer : IXamlAstTransformer + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { - public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) - { - if (node is XamlAstObjectNode objectNode && - objectNode.Type is XamlAstXmlTypeReference typeReference && - (typeReference.Name == "DataTemplate" || - typeReference.Name == "ControlTemplate")) - objectNode.Children.Clear(); - return node; - } + if (node is XamlAstObjectNode objectNode && + objectNode.Type is XamlAstXmlTypeReference typeReference && + (typeReference.Name == "DataTemplate" || + typeReference.Name == "ControlTemplate")) + objectNode.Children.Clear(); + return node; } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs b/src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs index 5dd5f36..237dcdd 100644 --- a/src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs +++ b/src/Avalonia.NameGenerator/Compiler/MiniCompiler.cs @@ -5,41 +5,40 @@ using XamlX.Transform.Transformers; using XamlX.TypeSystem; -namespace Avalonia.NameGenerator.Compiler +namespace Avalonia.NameGenerator.Compiler; + +internal sealed class MiniCompiler : XamlCompiler { - internal sealed class MiniCompiler : XamlCompiler - { - public const string AvaloniaXmlnsDefinitionAttribute = "Avalonia.Metadata.XmlnsDefinitionAttribute"; + public const string AvaloniaXmlnsDefinitionAttribute = "Avalonia.Metadata.XmlnsDefinitionAttribute"; - public static MiniCompiler CreateDefault(RoslynTypeSystem typeSystem, params string[] additionalTypes) - { - var mappings = new XamlLanguageTypeMappings(typeSystem); - foreach (var additionalType in additionalTypes) - mappings.XmlnsAttributes.Add(typeSystem.GetType(additionalType)); + public static MiniCompiler CreateDefault(RoslynTypeSystem typeSystem, params string[] additionalTypes) + { + var mappings = new XamlLanguageTypeMappings(typeSystem); + foreach (var additionalType in additionalTypes) + mappings.XmlnsAttributes.Add(typeSystem.GetType(additionalType)); - var configuration = new TransformerConfiguration( - typeSystem, - typeSystem.Assemblies[0], - mappings); - return new MiniCompiler(configuration); - } + var configuration = new TransformerConfiguration( + typeSystem, + typeSystem.Assemblies[0], + mappings); + return new MiniCompiler(configuration); + } - private MiniCompiler(TransformerConfiguration configuration) - : base(configuration, new XamlLanguageEmitMappings(), false) - { - Transformers.Add(new NameDirectiveTransformer()); - Transformers.Add(new DataTemplateTransformer()); - Transformers.Add(new KnownDirectivesTransformer()); - Transformers.Add(new XamlIntrinsicsTransformer()); - Transformers.Add(new XArgumentsTransformer()); - Transformers.Add(new TypeReferenceResolver()); - } - - protected override XamlEmitContext InitCodeGen( - IFileSource file, - Func> createSubType, - object codeGen, XamlRuntimeContext context, - bool needContextLocal) => - throw new NotSupportedException(); + private MiniCompiler(TransformerConfiguration configuration) + : base(configuration, new XamlLanguageEmitMappings(), false) + { + Transformers.Add(new NameDirectiveTransformer()); + Transformers.Add(new DataTemplateTransformer()); + Transformers.Add(new KnownDirectivesTransformer()); + Transformers.Add(new XamlIntrinsicsTransformer()); + Transformers.Add(new XArgumentsTransformer()); + Transformers.Add(new TypeReferenceResolver()); } + + protected override XamlEmitContext InitCodeGen( + IFileSource file, + Func> createSubType, + object codeGen, XamlRuntimeContext context, + bool needContextLocal) => + throw new NotSupportedException(); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Compiler/NameDirectiveTransformer.cs b/src/Avalonia.NameGenerator/Compiler/NameDirectiveTransformer.cs index f5bea9b..fc65cb8 100644 --- a/src/Avalonia.NameGenerator/Compiler/NameDirectiveTransformer.cs +++ b/src/Avalonia.NameGenerator/Compiler/NameDirectiveTransformer.cs @@ -2,28 +2,27 @@ using XamlX.Ast; using XamlX.Transform; -namespace Avalonia.NameGenerator.Compiler +namespace Avalonia.NameGenerator.Compiler; + +internal class NameDirectiveTransformer : IXamlAstTransformer { - internal class NameDirectiveTransformer : IXamlAstTransformer + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) { - public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) - { - if (node is not XamlAstObjectNode objectNode) - return node; - - for (var index = 0; index < objectNode.Children.Count; index++) - { - var child = objectNode.Children[index]; - if (child is XamlAstXmlDirective directive && - directive.Namespace == XamlNamespaces.Xaml2006 && - directive.Name == "Name") - objectNode.Children[index] = new XamlAstXamlPropertyValueNode( - directive, - new XamlAstNamePropertyReference(directive, objectNode.Type, "Name", objectNode.Type), - directive.Values); - } - + if (node is not XamlAstObjectNode objectNode) return node; + + for (var index = 0; index < objectNode.Children.Count; index++) + { + var child = objectNode.Children[index]; + if (child is XamlAstXmlDirective directive && + directive.Namespace == XamlNamespaces.Xaml2006 && + directive.Name == "Name") + objectNode.Children[index] = new XamlAstXamlPropertyValueNode( + directive, + new XamlAstNamePropertyReference(directive, objectNode.Type, "Name", objectNode.Type), + directive.Values); } + + return node; } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Compiler/RoslynTypeSystem.cs b/src/Avalonia.NameGenerator/Compiler/RoslynTypeSystem.cs index 11f3482..933072c 100644 --- a/src/Avalonia.NameGenerator/Compiler/RoslynTypeSystem.cs +++ b/src/Avalonia.NameGenerator/Compiler/RoslynTypeSystem.cs @@ -5,269 +5,268 @@ using Microsoft.CodeAnalysis.CSharp; using XamlX.TypeSystem; -namespace Avalonia.NameGenerator.Compiler +namespace Avalonia.NameGenerator.Compiler; + +public class RoslynTypeSystem : IXamlTypeSystem { - public class RoslynTypeSystem : IXamlTypeSystem - { - private readonly List _assemblies = new List(); + private readonly List _assemblies = new(); - public RoslynTypeSystem(CSharpCompilation compilation) - { - _assemblies.Add(new RoslynAssembly(compilation.Assembly)); + public RoslynTypeSystem(CSharpCompilation compilation) + { + _assemblies.Add(new RoslynAssembly(compilation.Assembly)); - var assemblySymbols = compilation - .References - .Select(compilation.GetAssemblyOrModuleSymbol) - .OfType() - .Select(assembly => new RoslynAssembly(assembly)) - .ToList(); + var assemblySymbols = compilation + .References + .Select(compilation.GetAssemblyOrModuleSymbol) + .OfType() + .Select(assembly => new RoslynAssembly(assembly)) + .ToList(); - _assemblies.AddRange(assemblySymbols); - } + _assemblies.AddRange(assemblySymbols); + } - public IReadOnlyList Assemblies => _assemblies; + public IReadOnlyList Assemblies => _assemblies; - public IXamlAssembly FindAssembly(string name) => - Assemblies - .FirstOrDefault(a => string.Equals(a.Name, name, StringComparison.OrdinalIgnoreCase)); + public IXamlAssembly FindAssembly(string name) => + Assemblies + .FirstOrDefault(a => string.Equals(a.Name, name, StringComparison.OrdinalIgnoreCase)); - public IXamlType FindType(string name) => - _assemblies - .Select(assembly => assembly.FindType(name)) - .FirstOrDefault(type => type != null); + public IXamlType FindType(string name) => + _assemblies + .Select(assembly => assembly.FindType(name)) + .FirstOrDefault(type => type != null); - public IXamlType FindType(string name, string assembly) => - _assemblies - .Select(assemblyInstance => assemblyInstance.FindType(name)) - .FirstOrDefault(type => type != null); - } + public IXamlType FindType(string name, string assembly) => + _assemblies + .Select(assemblyInstance => assemblyInstance.FindType(name)) + .FirstOrDefault(type => type != null); +} - public class RoslynAssembly : IXamlAssembly - { - private readonly IAssemblySymbol _symbol; +public class RoslynAssembly : IXamlAssembly +{ + private readonly IAssemblySymbol _symbol; - public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol; + public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol; - public bool Equals(IXamlAssembly other) => - other is RoslynAssembly roslynAssembly && - SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol); + public bool Equals(IXamlAssembly other) => + other is RoslynAssembly roslynAssembly && + SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol); - public string Name => _symbol.Name; + public string Name => _symbol.Name; - public IReadOnlyList CustomAttributes => - _symbol.GetAttributes() - .Select(data => new RoslynAttribute(data, this)) - .ToList(); + public IReadOnlyList 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 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 class RoslynAttribute : IXamlCustomAttribute + public RoslynAttribute(AttributeData data, RoslynAssembly assembly) { - 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 Parameters => - _data.ConstructorArguments - .Select(argument => argument.Value) - .ToList(); - - public Dictionary Properties => - _data.NamedArguments.ToDictionary( - pair => pair.Key, - pair => pair.Value.Value); + _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 Parameters => + _data.ConstructorArguments + .Select(argument => argument.Value) + .ToList(); + + public Dictionary Properties => + _data.NamedArguments.ToDictionary( + pair => pair.Key, + pair => pair.Value.Value); +} - public class RoslynType : IXamlType +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) { - 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; + _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 Name => _symbol.Name; - public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat); + public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat); - public string FullName => $"{Namespace}.{Name}"; + public string FullName => $"{Namespace}.{Name}"; - public IXamlAssembly Assembly => _assembly; + public IXamlAssembly Assembly => _assembly; - public IReadOnlyList Properties => - _symbol.GetMembers() - .Where(member => member.Kind == SymbolKind.Property) - .OfType() - .Select(property => new RoslynProperty(property, _assembly)) - .ToList(); + public IReadOnlyList Properties => + _symbol.GetMembers() + .Where(member => member.Kind == SymbolKind.Property) + .OfType() + .Select(property => new RoslynProperty(property, _assembly)) + .ToList(); - public IReadOnlyList Events { get; } = new List(); + public IReadOnlyList Events { get; } = new List(); - public IReadOnlyList Fields { get; } = new List(); + public IReadOnlyList Fields { get; } = new List(); - public IReadOnlyList Methods { get; } = new List(); + public IReadOnlyList Methods { get; } = new List(); - public IReadOnlyList Constructors => - _symbol.Constructors - .Select(method => new RoslynConstructor(method, _assembly)) - .ToList(); + public IReadOnlyList Constructors => + _symbol.Constructors + .Select(method => new RoslynConstructor(method, _assembly)) + .ToList(); - public IReadOnlyList CustomAttributes { get; } = new List(); + public IReadOnlyList CustomAttributes { get; } = new List(); - public IReadOnlyList GenericArguments { get; } = new List(); + public IReadOnlyList GenericArguments { get; } = new List(); - public bool IsAssignableFrom(IXamlType type) => type == this; + public bool IsAssignableFrom(IXamlType type) => type == this; - public IXamlType MakeGenericType(IReadOnlyList typeArguments) => this; + public IXamlType MakeGenericType(IReadOnlyList typeArguments) => this; - public IXamlType GenericTypeDefinition => this; + public IXamlType GenericTypeDefinition => this; - public bool IsArray => false; + public bool IsArray => false; - public IXamlType ArrayElementType { get; } = null; + public IXamlType ArrayElementType { get; } = null; - public IXamlType MakeArrayType(int dimensions) => null; + public IXamlType MakeArrayType(int dimensions) => null; - public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly); + public IXamlType BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly); - public bool IsValueType { get; } = false; + public bool IsValueType { get; } = false; - public bool IsEnum { get; } = false; + public bool IsEnum { get; } = false; - public IReadOnlyList Interfaces => - _symbol.AllInterfaces - .Select(abstraction => new RoslynType(abstraction, _assembly)) - .ToList(); + public IReadOnlyList Interfaces => + _symbol.AllInterfaces + .Select(abstraction => new RoslynType(abstraction, _assembly)) + .ToList(); - public bool IsInterface => _symbol.IsAbstract; + public bool IsInterface => _symbol.IsAbstract; - public IXamlType GetEnumUnderlyingType() => null; + public IXamlType GetEnumUnderlyingType() => null; - public IReadOnlyList GenericParameters { get; } = new List(); - } + public IReadOnlyList GenericParameters { get; } = new List(); +} - public class RoslynConstructor : IXamlConstructor - { - private readonly IMethodSymbol _symbol; - private readonly RoslynAssembly _assembly; +public class RoslynConstructor : IXamlConstructor +{ + private readonly IMethodSymbol _symbol; + private readonly RoslynAssembly _assembly; - public RoslynConstructor(IMethodSymbol symbol, RoslynAssembly assembly) - { - _symbol = symbol; - _assembly = 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 Equals(IXamlConstructor other) => + other is RoslynConstructor roslynConstructor && + SymbolEqualityComparer.Default.Equals(_symbol, roslynConstructor._symbol); - public bool IsPublic => true; + public bool IsPublic => true; - public bool IsStatic => false; - - public IReadOnlyList Parameters => - _symbol.Parameters - .Select(parameter => parameter.Type) - .OfType() - .Select(type => new RoslynType(type, _assembly)) - .ToList(); - } + public bool IsStatic => false; - public class RoslynProperty : IXamlProperty - { - private readonly IPropertySymbol _symbol; - private readonly RoslynAssembly _assembly; + public IReadOnlyList Parameters => + _symbol.Parameters + .Select(parameter => parameter.Type) + .OfType() + .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 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 bool Equals(IXamlProperty other) => + other is RoslynProperty roslynProperty && + SymbolEqualityComparer.Default.Equals(_symbol, roslynProperty._symbol); - public string Name => _symbol.Name; + public string Name => _symbol.Name; - public IXamlType PropertyType => - _symbol.Type is INamedTypeSymbol namedTypeSymbol - ? new RoslynType(namedTypeSymbol, _assembly) - : null; + 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 Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly); - public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly); + public IXamlMethod Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly); - public IReadOnlyList CustomAttributes { get; } = new List(); + public IReadOnlyList CustomAttributes { get; } = new List(); - public IReadOnlyList IndexerParameters { get; } = new List(); - } + public IReadOnlyList IndexerParameters { get; } = new List(); +} - public class RoslynMethod : IXamlMethod - { - private readonly IMethodSymbol _symbol; - private readonly RoslynAssembly _assembly; +public class RoslynMethod : IXamlMethod +{ + private readonly IMethodSymbol _symbol; + private readonly RoslynAssembly _assembly; - public RoslynMethod(IMethodSymbol symbol, RoslynAssembly assembly) - { - _symbol = symbol; - _assembly = 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 bool Equals(IXamlMethod other) => + other is RoslynMethod roslynMethod && + SymbolEqualityComparer.Default.Equals(roslynMethod._symbol, _symbol); - public string Name => _symbol.Name; + public string Name => _symbol.Name; - public bool IsPublic => true; + public bool IsPublic => true; - public bool IsStatic => false; + public bool IsStatic => false; - public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly); + public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly); - public IReadOnlyList Parameters => - _symbol.Parameters.Select(parameter => parameter.Type) - .OfType() - .Select(type => new RoslynType(type, _assembly)) - .ToList(); + public IReadOnlyList Parameters => + _symbol.Parameters.Select(parameter => parameter.Type) + .OfType() + .Select(type => new RoslynType(type, _assembly)) + .ToList(); - public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly); + public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType, _assembly); - public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) => null; + public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) => null; - public IReadOnlyList CustomAttributes { get; } = new List(); - } + public IReadOnlyList CustomAttributes { get; } = new List(); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Domain/ICodeGenerator.cs b/src/Avalonia.NameGenerator/Domain/ICodeGenerator.cs index 55a53a8..077160e 100644 --- a/src/Avalonia.NameGenerator/Domain/ICodeGenerator.cs +++ b/src/Avalonia.NameGenerator/Domain/ICodeGenerator.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using XamlX.TypeSystem; -namespace Avalonia.NameGenerator.Domain +namespace Avalonia.NameGenerator.Domain; + +internal interface ICodeGenerator { - internal interface ICodeGenerator - { - string GenerateCode(string className, string nameSpace, IXamlType XamlType, IEnumerable names); - } + string GenerateCode(string className, string nameSpace, IXamlType XamlType, IEnumerable names); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Domain/IGlobPattern.cs b/src/Avalonia.NameGenerator/Domain/IGlobPattern.cs index de75f7a..becdad1 100644 --- a/src/Avalonia.NameGenerator/Domain/IGlobPattern.cs +++ b/src/Avalonia.NameGenerator/Domain/IGlobPattern.cs @@ -1,7 +1,6 @@ -namespace Avalonia.NameGenerator.Domain +namespace Avalonia.NameGenerator.Domain; + +public interface IGlobPattern { - public interface IGlobPattern - { - bool Matches(string str); - } + bool Matches(string str); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Domain/INameGenerator.cs b/src/Avalonia.NameGenerator/Domain/INameGenerator.cs index 9581d5a..e31798e 100644 --- a/src/Avalonia.NameGenerator/Domain/INameGenerator.cs +++ b/src/Avalonia.NameGenerator/Domain/INameGenerator.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis; -namespace Avalonia.NameGenerator.Domain +namespace Avalonia.NameGenerator.Domain; + +internal interface INameGenerator { - internal interface INameGenerator - { - IReadOnlyList GenerateNameReferences(IEnumerable additionalFiles); - } + IReadOnlyList GenerateNameReferences(IEnumerable additionalFiles); +} - internal record GeneratedPartialClass(string FileName, string Content); -} \ No newline at end of file +internal record GeneratedPartialClass(string FileName, string Content); \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Domain/INameResolver.cs b/src/Avalonia.NameGenerator/Domain/INameResolver.cs index 64f1730..2fdb158 100644 --- a/src/Avalonia.NameGenerator/Domain/INameResolver.cs +++ b/src/Avalonia.NameGenerator/Domain/INameResolver.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using XamlX.Ast; -namespace Avalonia.NameGenerator.Domain +namespace Avalonia.NameGenerator.Domain; + +internal interface INameResolver { - internal interface INameResolver - { - IReadOnlyList ResolveNames(XamlDocument xaml); - } + IReadOnlyList ResolveNames(XamlDocument xaml); +} - internal record ResolvedName(string TypeName, string Name, string FieldModifier); -} \ No newline at end of file +internal record ResolvedName(string TypeName, string Name, string FieldModifier); \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Domain/IViewResolver.cs b/src/Avalonia.NameGenerator/Domain/IViewResolver.cs index 7422f3c..e46111c 100644 --- a/src/Avalonia.NameGenerator/Domain/IViewResolver.cs +++ b/src/Avalonia.NameGenerator/Domain/IViewResolver.cs @@ -1,13 +1,11 @@ -using System; using XamlX.Ast; using XamlX.TypeSystem; -namespace Avalonia.NameGenerator.Domain +namespace Avalonia.NameGenerator.Domain; + +internal interface IViewResolver { - internal interface IViewResolver - { - ResolvedView ResolveView(string xaml); - } + ResolvedView ResolveView(string xaml); +} - internal record ResolvedView(string ClassName, IXamlType XamlType, string Namespace, XamlDocument Xaml); -} \ No newline at end of file +internal record ResolvedView(string ClassName, IXamlType XamlType, string Namespace, XamlDocument Xaml); \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Domain/IsExternalInit.cs b/src/Avalonia.NameGenerator/Domain/IsExternalInit.cs index a2f2622..8a53989 100644 --- a/src/Avalonia.NameGenerator/Domain/IsExternalInit.cs +++ b/src/Avalonia.NameGenerator/Domain/IsExternalInit.cs @@ -1,5 +1,4 @@ // ReSharper disable once CheckNamespace -namespace System.Runtime.CompilerServices -{ - internal static class IsExternalInit { } -} \ No newline at end of file +namespace System.Runtime.CompilerServices; + +internal static class IsExternalInit { } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator.cs b/src/Avalonia.NameGenerator/Generator.cs index b40ac14..09fd02a 100644 --- a/src/Avalonia.NameGenerator/Generator.cs +++ b/src/Avalonia.NameGenerator/Generator.cs @@ -8,79 +8,78 @@ [assembly: InternalsVisibleTo("Avalonia.NameGenerator.Tests")] -namespace Avalonia.NameGenerator +namespace Avalonia.NameGenerator; + +[Generator] +public class AvaloniaNameSourceGenerator : ISourceGenerator { - [Generator] - public class AvaloniaNameSourceGenerator : ISourceGenerator - { - public void Initialize(GeneratorInitializationContext context) { } + public void Initialize(GeneratorInitializationContext context) { } - public void Execute(GeneratorExecutionContext context) + public void Execute(GeneratorExecutionContext context) + { + try { - try - { - var generator = CreateNameGenerator(context); - var partials = generator.GenerateNameReferences(context.AdditionalFiles); - foreach (var partial in partials) context.AddSource(partial.FileName, partial.Content); - } - catch (Exception exception) - { - ReportUnhandledError(context, exception); - } + var generator = CreateNameGenerator(context); + var partials = generator.GenerateNameReferences(context.AdditionalFiles); + foreach (var partial in partials) context.AddSource(partial.FileName, partial.Content); } - - private static INameGenerator CreateNameGenerator(GeneratorExecutionContext context) + catch (Exception exception) { - var options = new GeneratorOptions(context); - var types = new RoslynTypeSystem((CSharpCompilation)context.Compilation); - var defaultFieldModifier = options.AvaloniaNameGeneratorDefaultFieldModifier.ToString().ToLowerInvariant(); - ICodeGenerator generator = options.AvaloniaNameGeneratorBehavior switch { - Behavior.OnlyProperties => new OnlyPropertiesCodeGenerator(), - Behavior.InitializeComponent => new InitializeComponentCodeGenerator(types), - _ => throw new ArgumentOutOfRangeException() - }; - - var compiler = MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute); - return new AvaloniaNameGenerator( - new GlobPatternGroup(options.AvaloniaNameGeneratorFilterByPath), - new GlobPatternGroup(options.AvaloniaNameGeneratorFilterByNamespace), - new XamlXViewResolver(types, compiler, true, type => ReportInvalidType(context, type)), - new XamlXNameResolver(defaultFieldModifier), - generator); + ReportUnhandledError(context, exception); } + } - private static void ReportUnhandledError(GeneratorExecutionContext context, Exception error) - { - const string message = - "Unhandled exception occured while generating typed Name references. " + - "Please file an issue: https://github.com/avaloniaui/avalonia.namegenerator"; - context.ReportDiagnostic( - Diagnostic.Create( - new DiagnosticDescriptor( - "AXN0002", - message, - error.ToString(), - "Usage", - DiagnosticSeverity.Error, - true), - Location.None)); - } + private static INameGenerator CreateNameGenerator(GeneratorExecutionContext context) + { + var options = new GeneratorOptions(context); + var types = new RoslynTypeSystem((CSharpCompilation)context.Compilation); + var defaultFieldModifier = options.AvaloniaNameGeneratorDefaultFieldModifier.ToString().ToLowerInvariant(); + ICodeGenerator generator = options.AvaloniaNameGeneratorBehavior switch { + Behavior.OnlyProperties => new OnlyPropertiesCodeGenerator(), + Behavior.InitializeComponent => new InitializeComponentCodeGenerator(types), + _ => throw new ArgumentOutOfRangeException() + }; - private static void ReportInvalidType(GeneratorExecutionContext context, string typeName) - { - var message = - $"Avalonia x:Name generator was unable to generate names for type '{typeName}'. " + - $"The type '{typeName}' does not exist in the assembly."; - context.ReportDiagnostic( - Diagnostic.Create( - new DiagnosticDescriptor( - "AXN0001", - message, - message, - "Usage", - DiagnosticSeverity.Error, - true), - Location.None)); - } + var compiler = MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute); + return new AvaloniaNameGenerator( + new GlobPatternGroup(options.AvaloniaNameGeneratorFilterByPath), + new GlobPatternGroup(options.AvaloniaNameGeneratorFilterByNamespace), + new XamlXViewResolver(types, compiler, true, type => ReportInvalidType(context, type)), + new XamlXNameResolver(defaultFieldModifier), + generator); + } + + private static void ReportUnhandledError(GeneratorExecutionContext context, Exception error) + { + const string message = + "Unhandled exception occured while generating typed Name references. " + + "Please file an issue: https://github.com/avaloniaui/avalonia.namegenerator"; + context.ReportDiagnostic( + Diagnostic.Create( + new DiagnosticDescriptor( + "AXN0002", + message, + error.ToString(), + "Usage", + DiagnosticSeverity.Error, + true), + Location.None)); + } + + private static void ReportInvalidType(GeneratorExecutionContext context, string typeName) + { + var message = + $"Avalonia x:Name generator was unable to generate names for type '{typeName}'. " + + $"The type '{typeName}' does not exist in the assembly."; + context.ReportDiagnostic( + Diagnostic.Create( + new DiagnosticDescriptor( + "AXN0001", + message, + message, + "Usage", + DiagnosticSeverity.Error, + true), + Location.None)); } -} +} \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/AvaloniaNameGenerator.cs b/src/Avalonia.NameGenerator/Generator/AvaloniaNameGenerator.cs index c39a983..6b8b410 100644 --- a/src/Avalonia.NameGenerator/Generator/AvaloniaNameGenerator.cs +++ b/src/Avalonia.NameGenerator/Generator/AvaloniaNameGenerator.cs @@ -3,51 +3,50 @@ using Avalonia.NameGenerator.Domain; using Microsoft.CodeAnalysis; -namespace Avalonia.NameGenerator.Generator +namespace Avalonia.NameGenerator.Generator; + +internal class AvaloniaNameGenerator : INameGenerator { - internal class AvaloniaNameGenerator : INameGenerator - { - private readonly IGlobPattern _pathPattern; - private readonly IGlobPattern _namespacePattern; - private readonly IViewResolver _classes; - private readonly INameResolver _names; - private readonly ICodeGenerator _code; + private readonly IGlobPattern _pathPattern; + private readonly IGlobPattern _namespacePattern; + private readonly IViewResolver _classes; + private readonly INameResolver _names; + private readonly ICodeGenerator _code; - public AvaloniaNameGenerator( - IGlobPattern pathPattern, - IGlobPattern namespacePattern, - IViewResolver classes, - INameResolver names, - ICodeGenerator code) - { - _pathPattern = pathPattern; - _namespacePattern = namespacePattern; - _classes = classes; - _names = names; - _code = code; - } + public AvaloniaNameGenerator( + IGlobPattern pathPattern, + IGlobPattern namespacePattern, + IViewResolver classes, + INameResolver names, + ICodeGenerator code) + { + _pathPattern = pathPattern; + _namespacePattern = namespacePattern; + _classes = classes; + _names = names; + _code = code; + } - public IReadOnlyList GenerateNameReferences(IEnumerable additionalFiles) - { - var resolveViews = - from file in additionalFiles - where (file.Path.EndsWith(".xaml") || - file.Path.EndsWith(".paml") || - file.Path.EndsWith(".axaml")) && - _pathPattern.Matches(file.Path) - let xaml = file.GetText()!.ToString() - let view = _classes.ResolveView(xaml) - where view != null && _namespacePattern.Matches(view.Namespace) - select view; + public IReadOnlyList GenerateNameReferences(IEnumerable additionalFiles) + { + var resolveViews = + from file in additionalFiles + where (file.Path.EndsWith(".xaml") || + file.Path.EndsWith(".paml") || + file.Path.EndsWith(".axaml")) && + _pathPattern.Matches(file.Path) + let xaml = file.GetText()!.ToString() + let view = _classes.ResolveView(xaml) + where view != null && _namespacePattern.Matches(view.Namespace) + select view; - var query = - from view in resolveViews - let names = _names.ResolveNames(view.Xaml) - let code = _code.GenerateCode(view.ClassName, view.Namespace, view.XamlType, names) - let fileName = $"{view.ClassName}.g.cs" - select new GeneratedPartialClass(fileName, code); + var query = + from view in resolveViews + let names = _names.ResolveNames(view.Xaml) + let code = _code.GenerateCode(view.ClassName, view.Namespace, view.XamlType, names) + let fileName = $"{view.ClassName}.g.cs" + select new GeneratedPartialClass(fileName, code); - return query.ToList(); - } + return query.ToList(); } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/GlobPattern.cs b/src/Avalonia.NameGenerator/Generator/GlobPattern.cs index 6b6a0c8..9b175dd 100644 --- a/src/Avalonia.NameGenerator/Generator/GlobPattern.cs +++ b/src/Avalonia.NameGenerator/Generator/GlobPattern.cs @@ -1,19 +1,18 @@ using System.Text.RegularExpressions; using Avalonia.NameGenerator.Domain; -namespace Avalonia.NameGenerator.Generator -{ - public class GlobPattern : IGlobPattern - { - private const RegexOptions GlobOptions = RegexOptions.IgnoreCase | RegexOptions.Singleline; - private readonly Regex _regex; +namespace Avalonia.NameGenerator.Generator; - public GlobPattern(string pattern) - { - var expression = "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$"; - _regex = new Regex(expression, GlobOptions); - } +public class GlobPattern : IGlobPattern +{ + private const RegexOptions GlobOptions = RegexOptions.IgnoreCase | RegexOptions.Singleline; + private readonly Regex _regex; - public bool Matches(string str) => _regex.IsMatch(str); + public GlobPattern(string pattern) + { + var expression = "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$"; + _regex = new Regex(expression, GlobOptions); } + + public bool Matches(string str) => _regex.IsMatch(str); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/GlobPatternGroup.cs b/src/Avalonia.NameGenerator/Generator/GlobPatternGroup.cs index c3d5755..00133da 100644 --- a/src/Avalonia.NameGenerator/Generator/GlobPatternGroup.cs +++ b/src/Avalonia.NameGenerator/Generator/GlobPatternGroup.cs @@ -2,17 +2,16 @@ using System.Linq; using Avalonia.NameGenerator.Domain; -namespace Avalonia.NameGenerator.Generator +namespace Avalonia.NameGenerator.Generator; + +public class GlobPatternGroup : IGlobPattern { - public class GlobPatternGroup : IGlobPattern - { - private readonly GlobPattern[] _patterns; + private readonly GlobPattern[] _patterns; - public GlobPatternGroup(IEnumerable patterns) => - _patterns = patterns - .Select(pattern => new GlobPattern(pattern)) - .ToArray(); + public GlobPatternGroup(IEnumerable patterns) => + _patterns = patterns + .Select(pattern => new GlobPattern(pattern)) + .ToArray(); - public bool Matches(string str) => _patterns.Any(pattern => pattern.Matches(str)); - } + public bool Matches(string str) => _patterns.Any(pattern => pattern.Matches(str)); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/InitializeComponentCodeGenerator.cs b/src/Avalonia.NameGenerator/Generator/InitializeComponentCodeGenerator.cs index e83759f..2f26984 100644 --- a/src/Avalonia.NameGenerator/Generator/InitializeComponentCodeGenerator.cs +++ b/src/Avalonia.NameGenerator/Generator/InitializeComponentCodeGenerator.cs @@ -2,12 +2,12 @@ using Avalonia.NameGenerator.Domain; using XamlX.TypeSystem; -namespace Avalonia.NameGenerator.Generator +namespace Avalonia.NameGenerator.Generator; + +internal class InitializeComponentCodeGenerator: ICodeGenerator { - internal class InitializeComponentCodeGenerator: ICodeGenerator - { - private readonly bool _diagnosticsAreConnected; - private const string AttachDevToolsCodeBlock = @" + private readonly bool _diagnosticsAreConnected; + private const string AttachDevToolsCodeBlock = @" #if DEBUG if (attachDevTools) { @@ -15,28 +15,28 @@ internal class InitializeComponentCodeGenerator: ICodeGenerator } #endif "; - private const string AttachDevToolsParameterDocumentation - = @" /// Should the dev tools be attached. + private const string AttachDevToolsParameterDocumentation + = @" /// Should the dev tools be attached. "; - public InitializeComponentCodeGenerator(IXamlTypeSystem types) - { - _diagnosticsAreConnected = types.FindAssembly("Avalonia.Diagnostics") != null; - } + public InitializeComponentCodeGenerator(IXamlTypeSystem types) + { + _diagnosticsAreConnected = types.FindAssembly("Avalonia.Diagnostics") != null; + } - public string GenerateCode(string className, string nameSpace, IXamlType xamlType, IEnumerable names) + public string GenerateCode(string className, string nameSpace, IXamlType xamlType, IEnumerable names) + { + var properties = new List(); + var initializations = new List(); + foreach (var (typeName, name, fieldModifier) in names) { - var properties = new List(); - var initializations = new List(); - foreach (var (typeName, name, fieldModifier) in names) - { - properties.Add($" {fieldModifier} global::{typeName} {name};"); - initializations.Add($" {name} = this.FindControl(\"{name}\");"); - } + properties.Add($" {fieldModifier} global::{typeName} {name};"); + initializations.Add($" {name} = this.FindControl(\"{name}\");"); + } - var attachDevTools = _diagnosticsAreConnected && IsWindow(xamlType); + var attachDevTools = _diagnosticsAreConnected && IsWindow(xamlType); - return $@"// + return $@"// using Avalonia; using Avalonia.Controls; @@ -65,19 +65,18 @@ public void InitializeComponent(bool loadXaml = true{(attachDevTools ? ", bool a }} }} "; - } + } - private static bool IsWindow(IXamlType xamlType) + private static bool IsWindow(IXamlType xamlType) + { + var type = xamlType; + bool isWindow; + do { - var type = xamlType; - bool isWindow; - do - { - isWindow = type.FullName == "Avalonia.Controls.Window"; - type = type.BaseType; - } while (!isWindow && type != null); + isWindow = type.FullName == "Avalonia.Controls.Window"; + type = type.BaseType; + } while (!isWindow && type != null); - return isWindow; - } + return isWindow; } -} +} \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/OnlyPropertiesCodeGenerator.cs b/src/Avalonia.NameGenerator/Generator/OnlyPropertiesCodeGenerator.cs index 77f2271..a45bf4e 100644 --- a/src/Avalonia.NameGenerator/Generator/OnlyPropertiesCodeGenerator.cs +++ b/src/Avalonia.NameGenerator/Generator/OnlyPropertiesCodeGenerator.cs @@ -3,19 +3,19 @@ using Avalonia.NameGenerator.Domain; using XamlX.TypeSystem; -namespace Avalonia.NameGenerator.Generator +namespace Avalonia.NameGenerator.Generator; + +internal class OnlyPropertiesCodeGenerator : ICodeGenerator { - internal class OnlyPropertiesCodeGenerator : ICodeGenerator + public string GenerateCode(string className, string nameSpace, IXamlType xamlType, IEnumerable names) { - public string GenerateCode(string className, string nameSpace, IXamlType XamlType, IEnumerable names) - { - var namedControls = names - .Select(info => " " + - $"{info.FieldModifier} global::{info.TypeName} {info.Name} => " + - $"this.FindControl(\"{info.Name}\");") - .ToList(); - var lines = string.Join("\n", namedControls); - return $@"// + var namedControls = names + .Select(info => " " + + $"{info.FieldModifier} global::{info.TypeName} {info.Name} => " + + $"this.FindControl(\"{info.Name}\");") + .ToList(); + var lines = string.Join("\n", namedControls); + return $@"// using Avalonia.Controls; @@ -27,6 +27,5 @@ partial class {className} }} }} "; - } } } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/XamlXNameResolver.cs b/src/Avalonia.NameGenerator/Generator/XamlXNameResolver.cs index 885e878..dc20610 100644 --- a/src/Avalonia.NameGenerator/Generator/XamlXNameResolver.cs +++ b/src/Avalonia.NameGenerator/Generator/XamlXNameResolver.cs @@ -4,89 +4,88 @@ using XamlX; using XamlX.Ast; -namespace Avalonia.NameGenerator.Generator +namespace Avalonia.NameGenerator.Generator; + +internal class XamlXNameResolver : INameResolver, IXamlAstVisitor { - internal class XamlXNameResolver : INameResolver, IXamlAstVisitor - { - private readonly List _items = new(); - private readonly string _defaultFieldModifier; + private readonly List _items = new(); + private readonly string _defaultFieldModifier; - public XamlXNameResolver(string defaultFieldModifier = "internal") - { - _defaultFieldModifier = defaultFieldModifier; - } + public XamlXNameResolver(string defaultFieldModifier = "internal") + { + _defaultFieldModifier = defaultFieldModifier; + } - public IReadOnlyList ResolveNames(XamlDocument xaml) - { - _items.Clear(); - xaml.Root.Visit(this); - xaml.Root.VisitChildren(this); - return _items; - } + public IReadOnlyList ResolveNames(XamlDocument xaml) + { + _items.Clear(); + xaml.Root.Visit(this); + xaml.Root.VisitChildren(this); + return _items; + } - IXamlAstNode IXamlAstVisitor.Visit(IXamlAstNode node) - { - if (node is not XamlAstObjectNode objectNode) - return node; + IXamlAstNode IXamlAstVisitor.Visit(IXamlAstNode node) + { + if (node is not XamlAstObjectNode objectNode) + return node; - var clrType = objectNode.Type.GetClrType(); - var isAvaloniaControl = clrType - .Interfaces - .Any(abstraction => abstraction.IsInterface && - abstraction.FullName == "Avalonia.Controls.IControl"); + var clrType = objectNode.Type.GetClrType(); + var isAvaloniaControl = clrType + .Interfaces + .Any(abstraction => abstraction.IsInterface && + abstraction.FullName == "Avalonia.Controls.IControl"); - if (!isAvaloniaControl) - return node; + if (!isAvaloniaControl) + return node; - foreach (var child in objectNode.Children) + foreach (var child in objectNode.Children) + { + if (child is XamlAstXamlPropertyValueNode propertyValueNode && + propertyValueNode.Property is XamlAstNamePropertyReference namedProperty && + namedProperty.Name == "Name" && + propertyValueNode.Values.Count > 0 && + propertyValueNode.Values[0] is XamlAstTextNode text) { - if (child is XamlAstXamlPropertyValueNode propertyValueNode && - propertyValueNode.Property is XamlAstNamePropertyReference namedProperty && - namedProperty.Name == "Name" && - propertyValueNode.Values.Count > 0 && - propertyValueNode.Values[0] is XamlAstTextNode text) - { - var fieldModifier = TryGetFieldModifier(objectNode); - var typeName = $@"{clrType.Namespace}.{clrType.Name}"; - var resolvedName = new ResolvedName(typeName, text.Text, fieldModifier); - if (_items.Contains(resolvedName)) - continue; - _items.Add(resolvedName); - } + var fieldModifier = TryGetFieldModifier(objectNode); + var typeName = $@"{clrType.Namespace}.{clrType.Name}"; + var resolvedName = new ResolvedName(typeName, text.Text, fieldModifier); + if (_items.Contains(resolvedName)) + continue; + _items.Add(resolvedName); } - - return node; } - void IXamlAstVisitor.Push(IXamlAstNode node) { } + return node; + } + + void IXamlAstVisitor.Push(IXamlAstNode node) { } - void IXamlAstVisitor.Pop() { } + void IXamlAstVisitor.Pop() { } - private string TryGetFieldModifier(XamlAstObjectNode objectNode) - { - // We follow Xamarin.Forms API behavior in terms of x:FieldModifier here: - // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/field-modifiers - // However, by default we use 'internal' field modifier here for generated - // x:Name references for historical purposes and WPF compatibility. - // - var fieldModifierType = objectNode - .Children - .OfType() - .Where(dir => dir.Name == "FieldModifier" && dir.Namespace == XamlNamespaces.Xaml2006) - .Select(dir => dir.Values[0]) - .OfType() - .Select(txt => txt.Text) - .FirstOrDefault(); + private string TryGetFieldModifier(XamlAstObjectNode objectNode) + { + // We follow Xamarin.Forms API behavior in terms of x:FieldModifier here: + // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/field-modifiers + // However, by default we use 'internal' field modifier here for generated + // x:Name references for historical purposes and WPF compatibility. + // + var fieldModifierType = objectNode + .Children + .OfType() + .Where(dir => dir.Name == "FieldModifier" && dir.Namespace == XamlNamespaces.Xaml2006) + .Select(dir => dir.Values[0]) + .OfType() + .Select(txt => txt.Text) + .FirstOrDefault(); - return fieldModifierType?.ToLowerInvariant() switch - { - "private" => "private", - "public" => "public", - "protected" => "protected", - "internal" => "internal", - "notpublic" => "internal", - _ => _defaultFieldModifier - }; - } + return fieldModifierType?.ToLowerInvariant() switch + { + "private" => "private", + "public" => "public", + "protected" => "protected", + "internal" => "internal", + "notpublic" => "internal", + _ => _defaultFieldModifier + }; } -} +} \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/Generator/XamlXViewResolver.cs b/src/Avalonia.NameGenerator/Generator/XamlXViewResolver.cs index cfec01e..3485bbd 100644 --- a/src/Avalonia.NameGenerator/Generator/XamlXViewResolver.cs +++ b/src/Avalonia.NameGenerator/Generator/XamlXViewResolver.cs @@ -7,89 +7,88 @@ using XamlX.Ast; using XamlX.Parsers; -namespace Avalonia.NameGenerator.Generator +namespace Avalonia.NameGenerator.Generator; + +internal class XamlXViewResolver : IViewResolver, IXamlAstVisitor { - internal class XamlXViewResolver : IViewResolver, IXamlAstVisitor - { - private readonly RoslynTypeSystem _typeSystem; - private readonly MiniCompiler _compiler; - private readonly bool _checkTypeValidity; - private readonly Action _onTypeInvalid; + private readonly RoslynTypeSystem _typeSystem; + private readonly MiniCompiler _compiler; + private readonly bool _checkTypeValidity; + private readonly Action _onTypeInvalid; - private ResolvedView _resolvedClass; - private XamlDocument _xaml; + private ResolvedView _resolvedClass; + private XamlDocument _xaml; - public XamlXViewResolver( - RoslynTypeSystem typeSystem, - MiniCompiler compiler, - bool checkTypeValidity = false, - Action onTypeInvalid = null) - { - _checkTypeValidity = checkTypeValidity; - _onTypeInvalid = onTypeInvalid; - _typeSystem = typeSystem; - _compiler = compiler; - } + public XamlXViewResolver( + RoslynTypeSystem typeSystem, + MiniCompiler compiler, + bool checkTypeValidity = false, + Action onTypeInvalid = null) + { + _checkTypeValidity = checkTypeValidity; + _onTypeInvalid = onTypeInvalid; + _typeSystem = typeSystem; + _compiler = compiler; + } - public ResolvedView ResolveView(string xaml) + public ResolvedView ResolveView(string xaml) + { + _resolvedClass = null; + _xaml = XDocumentXamlParser.Parse(xaml, new Dictionary { - _resolvedClass = null; - _xaml = XDocumentXamlParser.Parse(xaml, new Dictionary - { - {XamlNamespaces.Blend2008, XamlNamespaces.Blend2008} - }); + {XamlNamespaces.Blend2008, XamlNamespaces.Blend2008} + }); - _compiler.Transform(_xaml); - _xaml.Root.Visit(this); - _xaml.Root.VisitChildren(this); - return _resolvedClass; - } + _compiler.Transform(_xaml); + _xaml.Root.Visit(this); + _xaml.Root.VisitChildren(this); + return _resolvedClass; + } - IXamlAstNode IXamlAstVisitor.Visit(IXamlAstNode node) - { - if (node is not XamlAstObjectNode objectNode) - return node; + IXamlAstNode IXamlAstVisitor.Visit(IXamlAstNode node) + { + if (node is not XamlAstObjectNode objectNode) + return node; - var clrType = objectNode.Type.GetClrType(); - var isAvaloniaControl = clrType - .Interfaces - .Any(abstraction => abstraction.IsInterface && - abstraction.FullName == "Avalonia.Controls.IControl"); + var clrType = objectNode.Type.GetClrType(); + var isAvaloniaControl = clrType + .Interfaces + .Any(abstraction => abstraction.IsInterface && + abstraction.FullName == "Avalonia.Controls.IControl"); - if (!isAvaloniaControl) - return node; + if (!isAvaloniaControl) + return node; - foreach (var child in objectNode.Children) + foreach (var child in objectNode.Children) + { + if (child is XamlAstXmlDirective directive && + directive.Name == "Class" && + directive.Namespace == XamlNamespaces.Xaml2006 && + directive.Values[0] is XamlAstTextNode text) { - if (child is XamlAstXmlDirective directive && - directive.Name == "Class" && - directive.Namespace == XamlNamespaces.Xaml2006 && - directive.Values[0] is XamlAstTextNode text) + if (_checkTypeValidity) { - if (_checkTypeValidity) + var existingType = _typeSystem.FindType(text.Text); + if (existingType == null) { - var existingType = _typeSystem.FindType(text.Text); - if (existingType == null) - { - _onTypeInvalid?.Invoke(text.Text); - return node; - } + _onTypeInvalid?.Invoke(text.Text); + return node; } + } - var split = text.Text.Split('.'); - var nameSpace = string.Join(".", split.Take(split.Length - 1)); - var className = split.Last(); + var split = text.Text.Split('.'); + var nameSpace = string.Join(".", split.Take(split.Length - 1)); + var className = split.Last(); - _resolvedClass = new ResolvedView(className, clrType, nameSpace, _xaml); - return node; - } + _resolvedClass = new ResolvedView(className, clrType, nameSpace, _xaml); + return node; } - - return node; } - void IXamlAstVisitor.Push(IXamlAstNode node) { } - - void IXamlAstVisitor.Pop() { } + return node; } -} + + void IXamlAstVisitor.Push(IXamlAstNode node) { } + + void IXamlAstVisitor.Pop() { } +} \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/GeneratorContextExtensions.cs b/src/Avalonia.NameGenerator/GeneratorContextExtensions.cs index f9b4cd6..61b8f4e 100644 --- a/src/Avalonia.NameGenerator/GeneratorContextExtensions.cs +++ b/src/Avalonia.NameGenerator/GeneratorContextExtensions.cs @@ -1,31 +1,30 @@ using System.Linq; using Microsoft.CodeAnalysis; -namespace Avalonia.NameGenerator -{ - internal static class GeneratorContextExtensions - { - private const string SourceItemGroupMetadata = "build_metadata.AdditionalFiles.SourceItemGroup"; +namespace Avalonia.NameGenerator; - public static string GetMSBuildProperty( - this GeneratorExecutionContext context, - string name, - string defaultValue = "") - { - context.AnalyzerConfigOptions.GlobalOptions.TryGetValue($"build_property.{name}", out var value); - return value ?? defaultValue; - } +internal static class GeneratorContextExtensions +{ + private const string SourceItemGroupMetadata = "build_metadata.AdditionalFiles.SourceItemGroup"; - public static string[] GetMSBuildItems(this GeneratorExecutionContext context, string name) - => context - .AdditionalFiles - .Where(f => - context - .AnalyzerConfigOptions - .GetOptions(f) - .TryGetValue(SourceItemGroupMetadata, out var sourceItemGroup) - && sourceItemGroup == name) - .Select(f => f.Path) - .ToArray(); + public static string GetMsBuildProperty( + this GeneratorExecutionContext context, + string name, + string defaultValue = "") + { + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue($"build_property.{name}", out var value); + return value ?? defaultValue; } + + public static string[] GetMsBuildItems(this GeneratorExecutionContext context, string name) + => context + .AdditionalFiles + .Where(f => + context + .AnalyzerConfigOptions + .GetOptions(f) + .TryGetValue(SourceItemGroupMetadata, out var sourceItemGroup) + && sourceItemGroup == name) + .Select(f => f.Path) + .ToArray(); } \ No newline at end of file diff --git a/src/Avalonia.NameGenerator/GeneratorOptions.cs b/src/Avalonia.NameGenerator/GeneratorOptions.cs index 2602ef1..716926e 100644 --- a/src/Avalonia.NameGenerator/GeneratorOptions.cs +++ b/src/Avalonia.NameGenerator/GeneratorOptions.cs @@ -1,94 +1,93 @@ using System; using Microsoft.CodeAnalysis; -namespace Avalonia.NameGenerator +namespace Avalonia.NameGenerator; + +public enum BuildProperties { - public enum BuildProperties - { - AvaloniaNameGeneratorBehavior = 0, - AvaloniaNameGeneratorDefaultFieldModifier = 1, - AvaloniaNameGeneratorFilterByPath = 2, - AvaloniaNameGeneratorFilterByNamespace = 3, - } + AvaloniaNameGeneratorBehavior = 0, + AvaloniaNameGeneratorDefaultFieldModifier = 1, + AvaloniaNameGeneratorFilterByPath = 2, + AvaloniaNameGeneratorFilterByNamespace = 3, +} - public enum DefaultFieldModifier - { - Public = 0, - Private = 1, - Internal = 2, - Protected = 3, - } +public enum DefaultFieldModifier +{ + Public = 0, + Private = 1, + Internal = 2, + Protected = 3, +} - public enum Behavior - { - OnlyProperties = 0, - InitializeComponent = 1, - } +public enum Behavior +{ + OnlyProperties = 0, + InitializeComponent = 1, +} - public class GeneratorOptions - { - private readonly GeneratorExecutionContext _context; +public class GeneratorOptions +{ + private readonly GeneratorExecutionContext _context; - public GeneratorOptions(GeneratorExecutionContext context) => _context = context; + public GeneratorOptions(GeneratorExecutionContext context) => _context = context; - public Behavior AvaloniaNameGeneratorBehavior + public Behavior AvaloniaNameGeneratorBehavior + { + get { - get - { - const Behavior defaultBehavior = Behavior.InitializeComponent; - var propertyValue = _context - .GetMSBuildProperty( - nameof(BuildProperties.AvaloniaNameGeneratorBehavior), - defaultBehavior.ToString()); + const Behavior defaultBehavior = Behavior.InitializeComponent; + var propertyValue = _context + .GetMsBuildProperty( + nameof(BuildProperties.AvaloniaNameGeneratorBehavior), + defaultBehavior.ToString()); - if (!Enum.TryParse(propertyValue, true, out Behavior behavior)) - return defaultBehavior; - return behavior; - } + if (!Enum.TryParse(propertyValue, true, out Behavior behavior)) + return defaultBehavior; + return behavior; } + } - public DefaultFieldModifier AvaloniaNameGeneratorDefaultFieldModifier + public DefaultFieldModifier AvaloniaNameGeneratorDefaultFieldModifier + { + get { - get - { - const DefaultFieldModifier defaultFieldModifier = DefaultFieldModifier.Internal; - var propertyValue = _context - .GetMSBuildProperty( - nameof(BuildProperties.AvaloniaNameGeneratorDefaultFieldModifier), - defaultFieldModifier.ToString()); + const DefaultFieldModifier defaultFieldModifier = DefaultFieldModifier.Internal; + var propertyValue = _context + .GetMsBuildProperty( + nameof(BuildProperties.AvaloniaNameGeneratorDefaultFieldModifier), + defaultFieldModifier.ToString()); - if (!Enum.TryParse(propertyValue, true, out DefaultFieldModifier modifier)) - return defaultFieldModifier; - return modifier; - } + if (!Enum.TryParse(propertyValue, true, out DefaultFieldModifier modifier)) + return defaultFieldModifier; + return modifier; } + } - public string[] AvaloniaNameGeneratorFilterByPath + public string[] AvaloniaNameGeneratorFilterByPath + { + get { - get - { - var propertyValue = _context.GetMSBuildProperty( - nameof(BuildProperties.AvaloniaNameGeneratorFilterByPath), - "*"); + var propertyValue = _context.GetMsBuildProperty( + nameof(BuildProperties.AvaloniaNameGeneratorFilterByPath), + "*"); - if (propertyValue.Contains(";")) - return propertyValue.Split(';'); - return new[] {propertyValue}; - } + if (propertyValue.Contains(";")) + return propertyValue.Split(';'); + return new[] {propertyValue}; } + } - public string[] AvaloniaNameGeneratorFilterByNamespace + public string[] AvaloniaNameGeneratorFilterByNamespace + { + get { - get - { - var propertyValue = _context.GetMSBuildProperty( - nameof(BuildProperties.AvaloniaNameGeneratorFilterByNamespace), - "*"); + var propertyValue = _context.GetMsBuildProperty( + nameof(BuildProperties.AvaloniaNameGeneratorFilterByNamespace), + "*"); - if (propertyValue.Contains(";")) - return propertyValue.Split(';'); - return new[] {propertyValue}; - } + if (propertyValue.Contains(";")) + return propertyValue.Split(';'); + return new[] {propertyValue}; } } } \ No newline at end of file From 9e28f09353a7778c841ef1ad9ac664f7a2b15377 Mon Sep 17 00:00:00 2001 From: Artyom Date: Fri, 7 Jan 2022 15:30:00 +0300 Subject: [PATCH 3/4] Use newer .NET --- .github/workflows/ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 2a3a7f8..d60dad7 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -26,7 +26,7 @@ jobs: - name: Install .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.301 + dotnet-version: 6.0.101 - name: NBGV id: nbgv From cf2b6428e1f11b4e1ccceb3b99475b051fc01039 Mon Sep 17 00:00:00 2001 From: Artyom Date: Fri, 7 Jan 2022 15:35:20 +0300 Subject: [PATCH 4/4] Use .NET 6 --- .../Avalonia.NameGenerator.Sandbox.csproj | 2 +- .../Avalonia.NameGenerator.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj b/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj index cd0e0a4..1e66c7e 100644 --- a/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj +++ b/src/Avalonia.NameGenerator.Sandbox/Avalonia.NameGenerator.Sandbox.csproj @@ -1,7 +1,7 @@ Exe - net5 + net6 preview false true diff --git a/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj b/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj index c2d3d58..8f450e1 100644 --- a/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj +++ b/src/Avalonia.NameGenerator.Tests/Avalonia.NameGenerator.Tests.csproj @@ -1,7 +1,7 @@ Exe - net5 + net6 preview false true