From 1e42792d2adf919e57a657aaf1031ba0c8ac5f1b Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 6 Dec 2024 10:47:01 +0000 Subject: [PATCH] experimenting with @Signal macro --- Sources/SwiftGodot/MacroDefs.swift | 4 + .../SwiftGodotMacroLibrary/MacroGodot.swift | 1 + .../NuSignalMacro.swift | 91 +++++++++++++++++++ Tests/SwiftGodotTests/SignalTests.swift | 16 +++- 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftGodot/MacroDefs.swift b/Sources/SwiftGodot/MacroDefs.swift index 7d80e0637..11d7f273b 100644 --- a/Sources/SwiftGodot/MacroDefs.swift +++ b/Sources/SwiftGodot/MacroDefs.swift @@ -248,4 +248,8 @@ public macro signal(_ signalName: String, arguments: Dictionary [AccessorDeclSyntax] + { + guard let varDecl = declaration.as(VariableDeclSyntax.self) else { + let invalidUsageErr = Diagnostic(node: node.root, message: ProviderDiagnostic.invalidDeclaration) + context.diagnose(invalidUsageErr) + return [] + } + + guard let signalType = varDecl.bindings.first?.typeAnnotation?.type else { + let missingAnnotationErr = Diagnostic(node: node.root, message: ProviderDiagnostic.missingTypeAnnotation) + context.diagnose(missingAnnotationErr) + return [] + } + + guard let nodeIdentifier = varDecl.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier else { + fatalError("No identifier for this expression could be found.") + } + + var signalName = SignalName(godotName: nodeIdentifier.text.camelCaseToSnakeCase(), swiftName: nodeIdentifier.text) + var arguments = [(name: String, type: String)]() + +// for (index, argument) in node.arguments { +// if index == 0 { +// signalName = argument.expression.signalName() +// } +// if index == 1 { +// if argument.expression.description == ".init()" { +// // its an empty dictionary, so no arguments +// continue +// } +// +// guard let dictSyntax = DictionaryExprSyntax(argument.expression) else { +// throw ProviderDiagnostic.argumentsInUnexpectedSyntax +// } +// +// if case .colon = dictSyntax.content { +// // its an empty dictionary, so no arguments +// continue +// } +// +// guard let pairList = DictionaryElementListSyntax(dictSyntax.content) else { +// throw ProviderDiagnostic.argumentsInUnexpectedSyntax +// } +// +// for pair in pairList { +// guard let typeName = pair.value.typeName() else { +// throw ProviderDiagnostic.argumentsInUnexpectedSyntax +// } +// arguments.append((pair.key.description, typeName)) +// } +// } +// } + +// let genericTypeList = arguments.map { $0.type }.joined(separator: ", ") +// +// let signalWrapperType = arguments.isEmpty ? "GenericSignal< /* no args */ >" : "GenericSignal<\(genericTypeList)>" + + return [ + """ + get { \(raw: signalType)(target: self, signalName: \"\(raw: signalName.godotName)\") } + """ + ] + } + +} diff --git a/Tests/SwiftGodotTests/SignalTests.swift b/Tests/SwiftGodotTests/SignalTests.swift index 42d44167c..7cbf66371 100644 --- a/Tests/SwiftGodotTests/SignalTests.swift +++ b/Tests/SwiftGodotTests/SignalTests.swift @@ -6,6 +6,7 @@ import SwiftGodotTestability private class TestSignalNode: Node { #signal("mySignal", arguments: ["age": Int.self, "name": String.self]) #nusignal("nuSignal", arguments: ["age": Int.self, "name": String.self]) + @Signal var anotherSignal: GenericSignal< /* no args */ > var receivedInt: Int? = nil var receivedString: String? = nil @@ -32,7 +33,7 @@ final class SignalTests: GodotTestCase { } func testNuSignal() { - let node = TestNode() + let node = TestSignalNode() var signalReceived = false node.nuSignal.connect { age, name in @@ -44,6 +45,19 @@ final class SignalTests: GodotTestCase { XCTAssertTrue (signalReceived, "signal should have been received") } + func testNuSignal2() { + let node = TestSignalNode() + var signalReceived = false + + node.nuSignal.connect { age, name in + XCTAssertEqual (age, 22) + XCTAssertEqual (name, "Sam") + signalReceived = true + } + node.nuSignal.emit(22, "Sam") + XCTAssertTrue (signalReceived, "signal should have been received") + } + func testBuiltInSignalWithNoArgument() { let node = Node() var signalReceived = false