Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Silksong.ModMenu/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
It should follow the format major.minor.patch (semantic versioning). If you publish your mod
as a library to NuGet, this version will also be used as the package version.
-->
<Version>0.7.0</Version>
<Version>0.7.1</Version>
</PropertyGroup>
</Project>
14 changes: 13 additions & 1 deletion Silksong.ModMenu/Generator/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,19 @@ public class ElementFactoryAttribute<T> : Attribute
public class ModMenuIncludeAttribute : Attribute { }

/// <summary>
/// Attribute to apply to a any numeric property, to specify a minimum and maximum.
/// Attribute to apply to any property with a finite number of acceptable values.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class ModMenuOptionsAttribute(params object[] options) : Attribute
{
/// <summary>
///
/// </summary>
public readonly object[] Options = options;
}

/// <summary>
/// Attribute to apply to any numeric property, to specify a minimum and maximum.
/// Dynamic mins/maxes are not yet supported.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
Expand Down
2 changes: 1 addition & 1 deletion Silksong.ModMenu/Plugin/ConfigEntryFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ TreeNode<LocalizedText, ElementTreeNode> tree
}

return ArrangeScreen(
elements.OrderBy(e => e.path).ToList(),
[.. elements.OrderBy(e => e.path)],
subpageNames.LastOrDefault() ?? menuName
);
}
Expand Down
133 changes: 124 additions & 9 deletions Silksong.ModMenuAnalyzerTest/GeneratorTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Silksong.ModMenu.Generator;
using Silksong.ModMenuAnalyzers;
Expand All @@ -24,13 +22,13 @@ public class TestData
""";

string gen = /*lang=c#-test*/
"""
$$"""
#nullable enable

namespace Test;

/// Custom menu class generated for Test.TestData.
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "1.0.0")]
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "{{ModMenuGenerator.VERSION}}")]
public class TestDataMenu : Silksong.ModMenu.Generator.ICustomMenu<Test.TestData>
{
public Silksong.ModMenu.Elements.SelectableValueElement<int> MyInt
Expand Down Expand Up @@ -116,13 +114,13 @@ public class SubData
""";

string gen1 = /*lang=c#-test*/
"""
$$"""
#nullable enable

namespace Test;

/// Custom menu class generated for Test.TestData.
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "1.0.0")]
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "{{ModMenuGenerator.VERSION}}")]
public class TestDataMenu : Silksong.ModMenu.Generator.ICustomMenu<Test.TestData>
{
public Silksong.ModMenu.Generator.SubMenuElement<Test.SubData, SubDataMenu> SubData
Expand Down Expand Up @@ -184,13 +182,13 @@ private void InvokeValueChanged(Silksong.ModMenu.Generator.CustomMenuValueChange
""";

string gen2 = /*lang=c#-test*/
"""
$$"""
#nullable enable

namespace Test;

/// Custom menu class generated for Test.SubData.
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "1.0.0")]
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "{{ModMenuGenerator.VERSION}}")]
public class SubDataMenu : Silksong.ModMenu.Generator.ICustomMenu<Test.SubData>
{
public Silksong.ModMenu.Elements.SelectableValueElement<string> MyString
Expand Down Expand Up @@ -254,6 +252,123 @@ private void InvokeValueChanged(Silksong.ModMenu.Generator.CustomMenuValueChange
await ExpectSourceCode(source, ("TestDataMenu.g.cs", gen1), ("SubDataMenu.g.cs", gen2));
}

[Fact]
public async Task TestOptions()
{
string source = /*lang=c#-test*/
"""
namespace Test;

public enum TestEnum
{
ONE,
TWO,
THREE,
OMITTED,
}

[Silksong.ModMenu.Generator.GenerateMenu]
public class TestData
{
[Silksong.ModMenu.Generator.ModMenuOptions(2, 3, 5, 7, 11, 13, 17, 19)]
public int PrimeInt = 2;
[Silksong.ModMenu.Generator.ModMenuOptions(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE)]
public TestEnum MyEnum = TestEnum.ONE;
}
""";

string gen = /*lang=c#-test*/
$$"""
#nullable enable

namespace Test;

/// Custom menu class generated for Test.TestData.
[System.CodeDom.Compiler.GeneratedCode("ModMenuGenerator", "{{ModMenuGenerator.VERSION}}")]
public class TestDataMenu : Silksong.ModMenu.Generator.ICustomMenu<Test.TestData>
{
public Silksong.ModMenu.Elements.SelectableValueElement<int> PrimeInt
{
get => _PrimeInt;
set
{
if (value == null) throw new System.ArgumentNullException(nameof(PrimeInt));
if (_PrimeInt == value) return;

if (_PrimeInt != null)
_PrimeInt.OnValueChanged -= _PrimeInt_subscriber;
_PrimeInt = value;
_PrimeInt.OnValueChanged += _PrimeInt_subscriber;
}
}
private Silksong.ModMenu.Elements.SelectableValueElement<int> _PrimeInt;
public Silksong.ModMenu.Elements.SelectableValueElement<Test.TestEnum> MyEnum
{
get => _MyEnum;
set
{
if (value == null) throw new System.ArgumentNullException(nameof(MyEnum));
if (_MyEnum == value) return;

if (_MyEnum != null)
_MyEnum.OnValueChanged -= _MyEnum_subscriber;
_MyEnum = value;
_MyEnum.OnValueChanged += _MyEnum_subscriber;
}
}
private Silksong.ModMenu.Elements.SelectableValueElement<Test.TestEnum> _MyEnum;

/// An aggregate event notified whenever any menu element in this class has its value changed.
public event System.Action<Silksong.ModMenu.Generator.CustomMenuValueChangedEvent>? OnValueChanged;

public TestDataMenu()
{
_PrimeInt_subscriber = value => InvokeValueChanged(new(nameof(PrimeInt), value));
PrimeInt = new Silksong.ModMenu.Elements.ChoiceElement<int>("Prime Int", Silksong.ModMenu.Models.ChoiceModels.ForValues([2, 3, 5, 7, 11, 13, 17, 19]), "");
_MyEnum_subscriber = value => InvokeValueChanged(new(nameof(MyEnum), value));
MyEnum = new Silksong.ModMenu.Elements.ChoiceElement<Test.TestEnum>("My Enum", Silksong.ModMenu.Models.ChoiceModels.ForValues([Test.TestEnum.ONE, Test.TestEnum.TWO, Test.TestEnum.THREE]), "");
}

/// <inheritdoc />
public void ExportTo(Test.TestData data)
{
data.PrimeInt = PrimeInt.Value;
data.MyEnum = MyEnum.Value;
}

/// <inheritdoc />
public void ApplyFrom(Test.TestData data)
{
using (notifySubscribers.Suppress())
{
PrimeInt.Value = data.PrimeInt;
MyEnum.Value = data.MyEnum;
}
}

/// <inheritdoc />
public System.Collections.Generic.IEnumerable<Silksong.ModMenu.Elements.MenuElement> Elements()
{
yield return PrimeInt;
yield return MyEnum;
}

private readonly Silksong.ModMenu.Util.EventSuppressor notifySubscribers = new();

private void InvokeValueChanged(Silksong.ModMenu.Generator.CustomMenuValueChangedEvent args)
{
if (notifySubscribers.Suppressed) return;
OnValueChanged?.Invoke(args);
}

private readonly System.Action<int> _PrimeInt_subscriber;
private readonly System.Action<Test.TestEnum> _MyEnum_subscriber;
}
""";

await ExpectSourceCode(source, ("TestDataMenu.g.cs", gen));
}

// TODO: Add Diagnostic tests.

private static Task ExpectSourceCode(
Expand Down
10 changes: 9 additions & 1 deletion Silksong.ModMenuAnalyzers/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

## Release 1.0.0
## Release 0.7.1

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
SSMM0012 | SilksongModMenu | Error | Diagnostics

## Release 0.7.0

### New Rules

Expand Down
11 changes: 11 additions & 0 deletions Silksong.ModMenuAnalyzers/Diagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,15 @@ internal static DiagnosticDescriptorWrapper ModMenuRangeBoundError(string typeNa
);

internal static DiagnosticDescriptorWrapper IncludedPublicField => new(includedPublicField, []);

private static readonly DiagnosticDescriptor invalidOptions = new(
id: "SSMM0012",
title: "Invalid ModMenuOptions",
messageFormat: "Could not handle ModMenuOptions argument",
category: CATEGORY,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true
);

internal static DiagnosticDescriptorWrapper InvalidOptions => new(invalidOptions, []);
}
Loading