diff --git a/.vscode/launch.json b/.vscode/launch.json index b3837e3..6381c20 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Chickensoft.LogicBlocks.Example/bin/Debug/net7.0/Chickensoft.LogicBlocks.Example.dll", + "program": "${workspaceFolder}/Chickensoft.LogicBlocks.Example/bin/Debug/net8.0/Chickensoft.LogicBlocks.Example.dll", "args": [ // "${input:args}" ], @@ -17,4 +17,4 @@ "enableStepFiltering": false, }, ] -} \ No newline at end of file +} diff --git a/Chickensoft.LogicBlocks.CodeFixes/Chickensoft.LogicBlocks.CodeFixes.csproj b/Chickensoft.LogicBlocks.CodeFixes/Chickensoft.LogicBlocks.CodeFixes.csproj index 7b7ef98..06bfd18 100644 --- a/Chickensoft.LogicBlocks.CodeFixes/Chickensoft.LogicBlocks.CodeFixes.csproj +++ b/Chickensoft.LogicBlocks.CodeFixes/Chickensoft.LogicBlocks.CodeFixes.csproj @@ -25,21 +25,21 @@ LogicBlocks CodeFixes release. icon.png state management;bloc;godot;game;state machine - README.md + README.md LICENSE https://github.com/chickensoft-games/LogicBlocks - + - + diff --git a/Chickensoft.LogicBlocks.DiagramGenerator.Tests/test_cases/OverriddenHandlers.cs b/Chickensoft.LogicBlocks.DiagramGenerator.Tests/test_cases/OverriddenHandlers.cs new file mode 100644 index 0000000..2b4f82d --- /dev/null +++ b/Chickensoft.LogicBlocks.DiagramGenerator.Tests/test_cases/OverriddenHandlers.cs @@ -0,0 +1,43 @@ +namespace Chickensoft.LogicBlocks.ScratchPad; + +using Chickensoft.Introspection; + +[Meta, LogicBlock(typeof(State), Diagram = true)] +public partial class OverriddenHandlers : LogicBlock { + public override Transition GetInitialState() => To(); + + public static class Input { + public readonly record struct SomeInput(); + public readonly record struct SomeOtherInput(); + + } + + public static class Output { + public readonly record struct SomeOutput(); + public readonly record struct SomeOtherOutput(); + } + + public abstract record State : StateLogic, + IGet, IGet { + + public virtual Transition On(in Input.SomeInput input) { + Output(new Output.SomeOutput()); + + return ToSelf(); + } + + public Transition On(in Input.SomeOtherInput input) { + Output(new Output.SomeOtherOutput()); + + return ToSelf(); + } + + public record Idle : State { + public override Transition On(in Input.SomeInput input) { + Output(new Output.SomeOtherOutput()); + + return ToSelf(); + } + } + } +} diff --git a/Chickensoft.LogicBlocks.DiagramGenerator.Tests/test_cases/OverriddenHandlers.g.puml b/Chickensoft.LogicBlocks.DiagramGenerator.Tests/test_cases/OverriddenHandlers.g.puml new file mode 100644 index 0000000..4317ae5 --- /dev/null +++ b/Chickensoft.LogicBlocks.DiagramGenerator.Tests/test_cases/OverriddenHandlers.g.puml @@ -0,0 +1,15 @@ +@startuml OverriddenHandlers +state "OverriddenHandlers State" as Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State { + state "Idle" as Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State_Idle +} + +Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State --> Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State : SomeInput +Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State --> Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State : SomeOtherInput +Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State_Idle --> Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State_Idle : SomeInput + +Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State : OnSomeInput → SomeOutput +Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State : OnSomeOtherInput → SomeOtherOutput +Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State_Idle : OnSomeInput → SomeOtherOutput + +[*] --> Chickensoft_LogicBlocks_ScratchPad_OverriddenHandlers_State_Idle +@enduml \ No newline at end of file diff --git a/Chickensoft.LogicBlocks.DiagramGenerator/src/Diagrammer.cs b/Chickensoft.LogicBlocks.DiagramGenerator/src/Diagrammer.cs index 8761f81..c73827d 100644 --- a/Chickensoft.LogicBlocks.DiagramGenerator/src/Diagrammer.cs +++ b/Chickensoft.LogicBlocks.DiagramGenerator/src/Diagrammer.cs @@ -499,7 +499,7 @@ INamedTypeSymbol stateBaseType // Get all of the handled inputs by looking at the implemented input // handler interfaces. - var handledInputInterfaces = type.Interfaces.Where( + var handledInputInterfaces = type.AllInterfaces.Where( (interfaceType) => CodeService.GetNameFullyQualifiedWithoutGenerics( interfaceType, interfaceType.Name ) is @@ -507,6 +507,10 @@ INamedTypeSymbol stateBaseType interfaceType.TypeArguments.Length == 1 ); + var interfaces = new HashSet( + type.Interfaces, SymbolEqualityComparer.Default + ); + // Get all syntax nodes comprising this type declaration. var syntaxNodes = type.DeclaringSyntaxReferences .Select(syntaxRef => syntaxRef.GetSyntax(token)); @@ -542,16 +546,37 @@ INamedTypeSymbol stateBaseType continue; } + var onTypeItself = interfaces.Contains(handledInputInterface); + + if (!onTypeItself) { + // method is not on the current type (so it must be implemented on a + // base type). + // + // we have to check for this case since Roslyn doesn't return + // overridden methods on the derived type when asking for an interface's + // member implementation method — we have to look up the overrides + // ourselves :/ + + // find any equivalent, overridden method on the current derived type + methodSymbol = type.GetMembers() + .OfType() + .FirstOrDefault( + member => SymbolEqualityComparer.Default.Equals( + member.OverriddenMethod, methodSymbol + ) + ); + + if (methodSymbol is null) { + continue; + } + } + var handlerMethodSyntaxes = methodSymbol .DeclaringSyntaxReferences .Select(syntaxRef => syntaxRef.GetSyntax(token)) .OfType() .ToImmutableArray(); - if (handlerMethodSyntaxes.Length == 0) { - continue; - } - foreach (var methodSyntax in handlerMethodSyntaxes) { inputHandlerMethods.Add(methodSyntax); var inputId = CodeService.GetNameFullyQualifiedWithoutGenerics(