diff --git a/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingNodeFactoryTest.kt b/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingNodeFactoryTest.kt index adb830f189..9da229a28f 100644 --- a/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingNodeFactoryTest.kt +++ b/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingNodeFactoryTest.kt @@ -342,14 +342,14 @@ class DiffConsumingNodeFactoryTest { var onChange: ((String) -> Unit)? = null private set - override fun onChange(onChange: ((String) -> Unit)?) { + override fun onChange(onChange: (String) -> Unit) { this.onChange = onChange } var onChangeCustomType: ((Duration) -> Unit)? = null private set - override fun onChangeCustomType(onChangeCustomType: ((Duration) -> Unit)?) { + override fun onChangeCustomType(onChangeCustomType: (Duration) -> Unit) { this.onChangeCustomType = onChangeCustomType } diff --git a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeGeneration.kt b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeGeneration.kt index 8a8c298605..9ddf7fdbfb 100644 --- a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeGeneration.kt +++ b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeGeneration.kt @@ -107,7 +107,9 @@ internal fun generateComposable( } is Event -> { ParameterSpec.builder(trait.name, trait.lambdaType) - .defaultValue(trait.defaultExpression ?: "null") + .apply { + trait.defaultExpression?.let { defaultValue(it) } + } .build() } is Children -> { diff --git a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeProtocolGeneration.kt b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeProtocolGeneration.kt index 8aa737e38d..814df4d79c 100644 --- a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeProtocolGeneration.kt +++ b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/composeProtocolGeneration.kt @@ -350,7 +350,7 @@ internal fun generateProtocolWidget( } is ProtocolEvent -> { addProperty( - PropertySpec.builder(trait.name, trait.lambdaType, PRIVATE) + PropertySpec.builder(trait.name, trait.lambdaType.copy(nullable = true), PRIVATE) .mutable() .initializer("null") .build(), @@ -359,16 +359,24 @@ internal fun generateProtocolWidget( FunSpec.builder(trait.name) .addModifiers(OVERRIDE) .addParameter(trait.name, trait.lambdaType) - .addStatement("val %1NSet = %1N != null", trait.name) - .beginControlFlow("if (%1NSet != (this.%1N != null))", trait.name) - .addStatement( - "this.state.append(%T(this.id, %T(%L), %M(%NSet)))", - Protocol.PropertyDiff, - Protocol.PropertyTag, - trait.tag, - KotlinxSerialization.JsonPrimitive, - trait.name, - ) + .apply { + val newValue = if (trait.isNullable) { + addStatement("val %1NSet = %1N != null", trait.name) + beginControlFlow("if (%1NSet != (this.%1N != null))", trait.name) + trait.name + "Set" + } else { + beginControlFlow("if (this.%1N == null)", trait.name) + "true" + } + addStatement( + "this.state.append(%T(this.id, %T(%L), %M(%L)))", + Protocol.PropertyDiff, + Protocol.PropertyTag, + trait.tag, + KotlinxSerialization.JsonPrimitive, + newValue, + ) + } .endControlFlow() .addStatement("this.%1N = %1N", trait.name) .build(), diff --git a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/diffConsumingGeneration.kt b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/diffConsumingGeneration.kt index d91084e855..40cb32f6b4 100644 --- a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/diffConsumingGeneration.kt +++ b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/diffConsumingGeneration.kt @@ -264,7 +264,11 @@ internal fun generateDiffConsumingWidget( ) } nextControlFlow("else") - addStatement("null") + if (trait.isNullable) { + addStatement("null") + } else { + addStatement("throw %T()", Stdlib.AssertionError) + } endControlFlow() addStatement("widget.%1N(%1N)", trait.name) endControlFlow() diff --git a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/sharedHelpers.kt b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/sharedHelpers.kt index dfd46be619..c7312120d0 100644 --- a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/sharedHelpers.kt +++ b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/sharedHelpers.kt @@ -51,14 +51,16 @@ internal val FqType.flatName: String internal val Event.lambdaType: TypeName get() { - parameterType?.let { parameterType -> - return LambdaTypeName.get(null, parameterType.asTypeName(), returnType = UNIT) - .copy(nullable = true) - } - return noArgumentEventLambda + val lambda = parameterType + ?.let { parameterType -> + LambdaTypeName.get(null, parameterType.asTypeName(), returnType = UNIT) + } + ?: noArgumentEventLambda + + return lambda.copy(nullable = isNullable) } -private val noArgumentEventLambda = LambdaTypeName.get(returnType = UNIT).copy(nullable = true) +private val noArgumentEventLambda = LambdaTypeName.get(returnType = UNIT) internal fun Schema.composePackage(host: Schema? = null): String { return if (host == null) { diff --git a/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/ComposeGenerationTest.kt b/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/ComposeGenerationTest.kt index 29d04ff647..49c614f026 100644 --- a/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/ComposeGenerationTest.kt +++ b/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/ComposeGenerationTest.kt @@ -90,7 +90,7 @@ class ComposeGenerationTest { val fileSpec = generateComposable(schema, schema.widgets.single()) assertThat(fileSpec.toString()).apply { contains("trait: String = \"test\"") - contains("onEvent: (() -> Unit)? = { error(\"test\") }") + contains("onEvent: () -> Unit = { error(\"test\") }") contains("block: @Composable () -> Unit = {}") } } diff --git a/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/TestingGenerationTest.kt b/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/TestingGenerationTest.kt index 09786b1e6d..dcd439e4e9 100644 --- a/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/TestingGenerationTest.kt +++ b/redwood-tooling-codegen/src/test/kotlin/app/cash/redwood/tooling/codegen/TestingGenerationTest.kt @@ -98,7 +98,7 @@ class TestingGenerationTest { | this.trait = trait | } | - | public override fun onEvent(onEvent: (() -> Unit)?): Unit { + | public override fun onEvent(onEvent: () -> Unit): Unit { | this.onEvent = onEvent | } | @@ -106,7 +106,7 @@ class TestingGenerationTest { | TestingGenerationTestBasicWidgetValue( | layoutModifiers = layoutModifiers, | trait = trait!!, - | onEvent = onEvent, + | onEvent = onEvent!!, | block = block.map { it.`value`.snapshot() }, | ) |} @@ -119,7 +119,7 @@ class TestingGenerationTest { |public class TestingGenerationTestBasicWidgetValue( | public override val layoutModifiers: LayoutModifier = LayoutModifier, | public val trait: String = "test", - | public val onEvent: (() -> Unit)? = { error("test") }, + | public val onEvent: () -> Unit = { error("test") }, | public val block: List = listOf(), |) : WidgetValue { | public override val childrenLists: List> diff --git a/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schema.kt b/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schema.kt index 21ae20e94e..056329493c 100644 --- a/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schema.kt +++ b/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schema.kt @@ -84,6 +84,7 @@ public interface Widget { public interface Event : Trait { public val parameterType: FqType? + public val isNullable: Boolean } public interface Children : Trait { diff --git a/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaClasses.kt b/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaClasses.kt index c8a15562da..8f3c6678a4 100644 --- a/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaClasses.kt +++ b/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaClasses.kt @@ -94,6 +94,7 @@ internal data class ParsedProtocolEvent( override val tag: Int, override val name: String, override val parameterType: FqType?, + override val isNullable: Boolean, override val defaultExpression: String? = null, override val deprecation: ParsedDeprecation? = null, ) : ProtocolEvent diff --git a/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaParser.kt b/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaParser.kt index 42870c73b2..7b91dd3515 100644 --- a/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaParser.kt +++ b/redwood-tooling-schema/src/main/kotlin/app/cash/redwood/tooling/schema/schemaParser.kt @@ -230,6 +230,7 @@ private fun parseWidget( tag = property.tag, name = name, parameterType = arguments.singleOrNull()?.type?.toFqType(), + isNullable = type.isMarkedNullable, defaultExpression = defaultExpression, deprecation = deprecation, ) diff --git a/samples/emoji-search/schema/src/main/kotlin/com/example/redwood/emojisearch/schema.kt b/samples/emoji-search/schema/src/main/kotlin/com/example/redwood/emojisearch/schema.kt index 3edbd96c03..0d64fba674 100644 --- a/samples/emoji-search/schema/src/main/kotlin/com/example/redwood/emojisearch/schema.kt +++ b/samples/emoji-search/schema/src/main/kotlin/com/example/redwood/emojisearch/schema.kt @@ -47,7 +47,7 @@ data class TextInput( val hint: String, @Property(3) @Default("null") - val onChange: (TextFieldState) -> Unit, + val onChange: ((TextFieldState) -> Unit)?, ) @Widget(2) diff --git a/test-schema/src/main/kotlin/example/redwood/schema.kt b/test-schema/src/main/kotlin/example/redwood/schema.kt index a2b9dbb637..87897808c7 100644 --- a/test-schema/src/main/kotlin/example/redwood/schema.kt +++ b/test-schema/src/main/kotlin/example/redwood/schema.kt @@ -66,7 +66,7 @@ public data class Text( @Widget(4) public data class Button( @Property(1) val text: String?, - @Property(2) val onClick: () -> Unit, + @Property(2) val onClick: (() -> Unit)?, ) @Widget(5)