Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Sample Option Attributes #336

Merged
merged 30 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
109ff7f
Adding SliderOption
niels9001 Dec 12, 2022
c260fae
Replacing RadioButtons with ComboBox to save space
niels9001 Dec 12, 2022
bc7899a
Adding TextOption
niels9001 Dec 12, 2022
4e18757
Change order
niels9001 Dec 12, 2022
c4774c7
Fix comment
niels9001 Dec 12, 2022
72530e7
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
6a57732
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
de50901
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
60f29a0
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Attributes/…
Arlodotexe Dec 12, 2022
e1a8c94
Address feedback
niels9001 Dec 13, 2022
31946ab
Revert change
niels9001 Dec 13, 2022
c45d2db
Revert
niels9001 Dec 13, 2022
0d4f88f
Delete MyExperimentNameHere.sln
niels9001 Dec 13, 2022
0d0139d
XAML formatting
niels9001 Dec 13, 2022
8153711
Merge branch 'user/niels9001/more-options' of https://github.com/Comm…
niels9001 Dec 13, 2022
747f0aa
Adress feedback
niels9001 Dec 15, 2022
5ad467b
Update common/CommunityToolkit.Labs.Core.SourceGenerators/Metadata/To…
Arlodotexe Dec 26, 2022
81b5401
Template updates
niels9001 Dec 27, 2022
2dc79b1
Fix
niels9001 Jan 2, 2023
0812e69
Consistent parameter naming
niels9001 Jan 2, 2023
bd451f7
Merge branch 'main' into user/niels9001/more-options
niels9001 Jan 20, 2023
ff489f5
Update ToolkitSampleMetadataGenerator.Sample.cs
niels9001 Jan 21, 2023
19709c2
CI fixes
niels9001 Jan 25, 2023
db13e4f
Numeric option
niels9001 Jan 25, 2023
ae176e0
Project template
niels9001 Jan 25, 2023
919d0e4
Fix tests
niels9001 Jan 31, 2023
fc82791
Test fixes
niels9001 Jan 31, 2023
ecb6693
Add failing test-case for missing Attribute Property Title
michael-hawker Feb 1, 2023
549c6a1
Fix issue with source generator not generating sample registry to tes…
michael-hawker Feb 1, 2023
4c7528c
Fix issue with extra Title property on Attribute not being setup prop…
michael-hawker Feb 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel.DataAnnotations;

namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests;

[TestClass]
public partial class ToolkitSampleMetadataTests
{
[TestMethod]
public void PaneOption_GeneratesProperty()
public void PaneOption_GeneratesWithoutDiagnostics()
{
var source = $@"
using System.ComponentModel;
Expand All @@ -24,8 +25,8 @@ public void PaneOption_GeneratesProperty()

namespace MyApp
{{
[ToolkitSampleBoolOption(""Test"", ""Toggle y"", false)]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")]
[ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle y"")]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")]

[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
Expand All @@ -46,6 +47,50 @@ public class UserControl {{ }}
VerifyGeneratedDiagnostics<ToolkitSampleOptionGenerator>(source, string.Empty);
}

[TestMethod]
public void PaneOption_GeneratesTitleProperty()
{
var source = """
using System.ComponentModel;
using CommunityToolkit.Labs.Core.SourceGenerators;
using CommunityToolkit.Labs.Core.SourceGenerators.Attributes;

namespace MyApp
{
[ToolkitSampleNumericOption("TextSize", 12, 8, 48, 2, false, Title = "FontSize")]
[ToolkitSample(id: nameof(Sample), "Test Sample", description: "")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
{
public Sample()
{
var x = this.Test;
var y = this.TextFontFamily;
}
}
}

namespace Windows.UI.Xaml.Controls
{
public class UserControl { }
}
""";

var result = """
#nullable enable
namespace CommunityToolkit.Labs.Core.SourceGenerators;

public static class ToolkitSampleRegistry
{
public static System.Collections.Generic.Dictionary<string, CommunityToolkit.Labs.Core.SourceGenerators.Metadata.ToolkitSampleMetadata> Listing
{ get; } = new() {
["Sample"] = new CommunityToolkit.Labs.Core.SourceGenerators.Metadata.ToolkitSampleMetadata("Sample", "Test Sample", "", typeof(MyApp.Sample), () => new MyApp.Sample(), null, null, new CommunityToolkit.Labs.Core.SourceGenerators.Metadata.IGeneratedToolkitSampleOptionViewModel[] { new CommunityToolkit.Labs.Core.SourceGenerators.Metadata.ToolkitSampleNumericOptionMetadataViewModel(name: "TextSize", initial: 12, min: 8, max: 48, step: 2, showAsNumberBox: false, title: "FontSize") })
};
}
""";

VerifyGenerateSources("MyApp.Tests", source, new[] { new ToolkitSampleMetadataGenerator() }, ignoreDiagnostics: true, ("ToolkitSampleRegistry.g.cs", result));
}

// https://github.com/CommunityToolkit/Labs-Windows/issues/175
[TestMethod]
public void PaneOption_GeneratesProperty_DuplicatePropNamesAcrossSampleClasses()
Expand All @@ -57,8 +102,8 @@ public void PaneOption_GeneratesProperty_DuplicatePropNamesAcrossSampleClasses()

namespace MyApp
{{
[ToolkitSampleBoolOption(""Test"", ""Toggle y"", false)]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")]
[ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle y"")]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")]

[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
Expand All @@ -70,8 +115,8 @@ public Sample()
}}
}}

[ToolkitSampleBoolOption(""Test"", ""Toggle y"", false)]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")]
[ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle y"")]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")]

[ToolkitSample(id: nameof(Sample2), ""Test Sample"", description: """")]
public partial class Sample2 : Windows.UI.Xaml.Controls.UserControl
Expand Down Expand Up @@ -101,7 +146,7 @@ public void PaneOptionOnNonSample()

namespace MyApp
{
[ToolkitSampleBoolOption(""BindToMe"", ""Toggle visibility"", false)]
[ToolkitSampleBoolOption(""BindToMe"", false, Title = ""Toggle visibility"")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
{
}
Expand Down Expand Up @@ -129,7 +174,7 @@ public void PaneOptionWithBadName(string name)
namespace MyApp
{{
[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
[ToolkitSampleBoolOption(""{name}"", ""Toggle visibility"", false)]
[ToolkitSampleBoolOption(""{name}"", false, Title = ""Toggle visibility"")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
{{
}}
Expand All @@ -153,7 +198,7 @@ public void PaneOptionWithConflictingPropertyName()

namespace MyApp
{{
[ToolkitSampleBoolOption(""IsVisible"", ""Toggle x"", false)]
[ToolkitSampleBoolOption(""IsVisible"", false, Title = ""Toggle x"")]
[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
{{
Expand All @@ -179,7 +224,7 @@ public void PaneOptionWithConflictingInheritedPropertyName()

namespace MyApp
{{
[ToolkitSampleBoolOption(""IsVisible"", ""Toggle x"", false)]
[ToolkitSampleBoolOption(""IsVisible"", false, Title = ""Toggle x"")]
[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Base
{{
Expand Down Expand Up @@ -209,9 +254,9 @@ public void PaneOptionWithDuplicateName()

namespace MyApp
{{
[ToolkitSampleBoolOption(""test"", ""Toggle x"", false)]
[ToolkitSampleBoolOption(""test"", ""Toggle y"", false)]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")]
[ToolkitSampleBoolOption(""test"", false, Title = ""Toggle x"")]
[ToolkitSampleBoolOption(""test"", false, Title = ""Toggle y"")]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", Title = ""Text foreground"")]

[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
Expand All @@ -237,14 +282,14 @@ public void PaneOptionWithDuplicateName_AllowedBetweenSamples()

namespace MyApp
{{
[ToolkitSampleBoolOption(""test"", ""Toggle y"", false)]
[ToolkitSampleBoolOption(""test"", false, Title = ""Toggle y"")]

[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
{{
}}

[ToolkitSampleBoolOption(""test"", ""Toggle y"", false)]
[ToolkitSampleBoolOption(""test"", false, Title = ""Toggle y"")]

[ToolkitSample(id: nameof(Sample2), ""Test Sample"", description: """")]
public partial class Sample2 : Windows.UI.Xaml.Controls.UserControl
Expand All @@ -270,7 +315,7 @@ public void PaneMultipleChoiceOptionWithNoChoices()

namespace MyApp
{{
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"")]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", Title = ""Font family"")]

[ToolkitSample(id: nameof(Sample), ""Test Sample"", description: """")]
public partial class Sample : Windows.UI.Xaml.Controls.UserControl
Expand All @@ -296,8 +341,8 @@ public void SampleGeneratedOptionAttributeOnUnsupportedType()

namespace MyApp
{{
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", title: ""Text foreground"", ""Segoe UI"", ""Arial"")]
[ToolkitSampleBoolOption(""Test"", ""Toggle visibility"", false)]
[ToolkitSampleMultiChoiceOption(""TextFontFamily"", ""Segoe UI"", ""Arial"", ""Consolas"", Title = ""Font family"")]
[ToolkitSampleBoolOption(""Test"", false, Title = ""Toggle visibility"")]
public partial class Sample
{{
}}
Expand Down Expand Up @@ -444,6 +489,55 @@ from assembly in AppDomain.CurrentDomain.GetAssemblies()
GC.KeepAlive(sampleAttributeType);
}

//// See: https://github.com/CommunityToolkit/dotnet/blob/c2053562d1a4d4829fc04b1cb86d1564c2c4a03c/tests/CommunityToolkit.Mvvm.SourceGenerators.UnitTests/Test_SourceGeneratorsCodegen.cs#L103
/// <summary>
/// Generates the requested sources
/// </summary>
/// <param name="source">The input source to process.</param>
/// <param name="generators">The generators to apply to the input syntax tree.</param>
/// <param name="results">The source files to compare.</param>
private static void VerifyGenerateSources(string assemblyName, string source, IIncrementalGenerator[] generators, bool ignoreDiagnostics = false, params (string Filename, string Text)[] results)
{
// Ensure our types are loaded
Type sampleattributeObjectType = typeof(ToolkitSampleAttribute);

// Get all assembly references for the loaded assemblies (easy way to pull in all necessary dependencies)
IEnumerable<MetadataReference> references =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
where !assembly.IsDynamic
let reference = MetadataReference.CreateFromFile(assembly.Location)
select reference;

SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));

// Create a syntax tree with the input source
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
new SyntaxTree[] { syntaxTree },
references,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

GeneratorDriver driver = CSharpGeneratorDriver.Create(generators).WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options);

// Run all source generators on the input source code
_ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outputCompilation, out ImmutableArray<Diagnostic> diagnostics);

// Ensure that no diagnostics were generated
if (!ignoreDiagnostics)
{
CollectionAssert.AreEquivalent(Array.Empty<Diagnostic>(), diagnostics);
}

foreach ((string filename, string text) in results)
{
SyntaxTree generatedTree = outputCompilation.SyntaxTrees.Single(tree => Path.GetFileName(tree.FilePath) == filename);

Assert.AreEqual(text, generatedTree.ToString());
}

GC.KeepAlive(sampleattributeObjectType);
}

// From: https://github.com/dotnet/roslyn/blob/main/src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs
internal class InMemoryAdditionalText : AdditionalText
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,14 @@ public sealed class ToolkitSampleBoolOptionAttribute : ToolkitSampleOptionBaseAt
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="defaultState">The initial value for the bound property.</param>
/// <param name="title">A title to display on top of this option.</param>
public ToolkitSampleBoolOptionAttribute(string bindingName, string label, bool defaultState, string? title = null)
: base(bindingName, defaultState, title)
public ToolkitSampleBoolOptionAttribute(string bindingName, bool defaultState)
: base(bindingName, defaultState)
{
Label = label;

}

/// <summary>
/// The source generator-friendly type name used for casting.
/// </summary>
internal override string TypeName { get; } = "bool";

/// <summary>
/// A label to display along the boolean option.
/// </summary>
public string Label { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Attributes;
public sealed class ToolkitSampleMultiChoiceOptionAttribute : ToolkitSampleOptionBaseAttribute
{
/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleBoolOptionAttribute"/>.
/// Creates a new instance of <see cref="ToolkitSampleMultiChoiceOptionAttribute"/>.
/// </summary>
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="choices">A list of the choices to display to the user. Can be literal values, or labeled values. Use a " : " separator (single colon surrounded by at least 1 whitespace) to separate a label from a value.</param>
/// <param name="title">A title to display on top of this option.</param>
public ToolkitSampleMultiChoiceOptionAttribute(string bindingName, string? title = null, params string[] choices)
: base(bindingName, null, title)
public ToolkitSampleMultiChoiceOptionAttribute(string bindingName, params string[] choices)
: base(bindingName, null)
{
Choices = choices.Select(x =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace CommunityToolkit.Labs.Core.SourceGenerators.Attributes;

/// <summary>
/// Represents a boolean sample option.
/// </summary>
/// <remarks>
/// Using this attribute will automatically generate an <see cref="INotifyPropertyChanged"/>-enabled property
/// that you can bind to in XAML, and displays an options pane alonside your sample which allows the user to manipulate the property.
/// <para/>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ToolkitSampleNumericOptionAttribute : ToolkitSampleOptionBaseAttribute
{
/// <summary>
/// Creates a new instance of <see cref="ToolkitSampleNumericOptionAttribute"/>.
/// </summary>
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="choices">A list of the choices to display to the user. Can be literal values, or labeled values. Use a " : " separator (single colon surrounded by at least 1 whitespace) to separate a label from a value.</param>
/// <param name="title">A title to display on top of this option.</param>
public ToolkitSampleNumericOptionAttribute(string bindingName, double initial = 0, double min = 0, double max = 10, double step = 1, bool showAsNumberBox = false)
: base(bindingName, null)
{
Initial = initial;
Min = min;
Max = max;
Step = step;
ShowAsNumberBox = showAsNumberBox;
}

/// <summary>
/// The default start value.
/// </summary>
public double Initial { get; }

/// <summary>
/// The minimal value.
/// </summary>
public double Min { get; }

/// <summary>
/// The maximum value.
/// </summary>
public double Max { get; }

/// <summary>
/// The step value.
/// </summary>
public double Step { get; }

/// <summary>
/// Determines if a Slider or NumberBox is shown.
/// </summary>
public bool ShowAsNumberBox { get; }

/// <summary>
/// The source generator-friendly type name used for casting.
/// </summary>
internal override string TypeName { get; } = "double";
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ public abstract class ToolkitSampleOptionBaseAttribute : Attribute
/// <param name="bindingName">The name of the generated property, which you can bind to in XAML.</param>
/// <param name="defaultState">The initial value for the bound property.</param>
/// <param name="title">A title to display on top of this option.</param>
public ToolkitSampleOptionBaseAttribute(string bindingName, object? defaultState, string? title = null)
public ToolkitSampleOptionBaseAttribute(string bindingName, object? defaultState)
{
Title = title;
Name = bindingName;
DefaultState = defaultState;
}
Expand All @@ -35,7 +34,7 @@ public ToolkitSampleOptionBaseAttribute(string bindingName, object? defaultState
/// <summary>
/// A title to display on top of the option.
/// </summary>
public string? Title { get; }
public string? Title { get; set; }

/// <summary>
/// The source generator-friendly type name used for casting.
Expand Down
Loading