-
Notifications
You must be signed in to change notification settings - Fork 5
Switch to KotlinPoet Code Generation #28
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
Merged
+316
−244
Merged
Changes from 41 commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
1a14877
conditionally sign
sugarmanz 4a04b8f
wip
sugarmanz 69fc4f5
wip on version catalog
sugarmanz a9b3423
wip ksp
sugarmanz b2f2d7c
finish compiler plugin re-org
sugarmanz 149aab4
fix all tests for compiler plugin and enhance validations
sugarmanz 0115548
re-enable docs + example-application
sugarmanz eb94c1f
gradle plugin
sugarmanz 42fc7be
maven plugin
sugarmanz 0d8a02f
fix some links
sugarmanz 06ef099
fix gradle and maven readmes and lint
sugarmanz dd055b1
try to fix signing
sugarmanz e840096
fix dependency
sugarmanz a093a2a
hacky way to fix test dependency
sugarmanz c909271
api dump
sugarmanz e22a41b
remove unnecessary option
sugarmanz 51fa2eb
add missing deps to version catalog
sugarmanz 28a752a
Get SyncHook working with Kotlin Poet
stabbylambda 5d2e121
Implement AsyncSeriesHook
stabbylambda bf0aff7
SyncBailHook and AsyncSeriesBailHook
stabbylambda e516b7a
SyncWaterfallHook
stabbylambda 4b3242a
SyncLoopHook
stabbylambda 0ddca19
AsyncParallelBailHook
stabbylambda 06c0a23
Make all the tests pass! 🎉
stabbylambda 8f140f7
Cleanup
stabbylambda 826dc8d
compiler-plugin -> processor
sugarmanz 768a268
lint,dump,knit
sugarmanz c789985
make knit happy (and me sad :()
sugarmanz 9895f28
upgrade knit
sugarmanz 30311d5
Fix the generics test
stabbylambda 3036757
upgrade arrow meta and handle no requested version a bit more gracefully
sugarmanz bdae47e
Merge remote-tracking branch 'origin/ksp' into poet
stabbylambda 102e812
remove example-library api folder
sugarmanz d29a221
Remove all the string-based codegen
stabbylambda 7ff5810
Merge branch 'ksp' into poet
stabbylambda 5492d62
Fix compiler issues
stabbylambda e595089
Merge remote-tracking branch 'origin/main' into poet
stabbylambda ef7fbc2
Make the apiCheck happy
stabbylambda f3d7ae1
Make the linter happy
stabbylambda ed8b732
Remove all the excess Poet descriptors
stabbylambda 9561222
Merge remote-tracking branch 'origin/main' into poet
stabbylambda 4132d5d
Pass TypeResolvers through to the signature
stabbylambda 892252b
Move all KSP -> Poet stuff into the validators
stabbylambda 0837c34
Split all the ksp/poet stuff into different parts
stabbylambda 36f213c
Clean up duplicate cases
stabbylambda cadcac6
Fix external api
stabbylambda 6073b81
More cleanup
stabbylambda de2b1fa
Switch from withEither to andThen
stabbylambda File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 34 additions & 26 deletions
60
processor/src/main/kotlin/com/intuit/hooks/plugin/codegen/HookInfo.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,62 @@ | ||
package com.intuit.hooks.plugin.codegen | ||
|
||
import com.google.devtools.ksp.getVisibility | ||
import com.google.devtools.ksp.symbol.* | ||
import com.intuit.hooks.plugin.ksp.text | ||
import com.intuit.hooks.plugin.ksp.validation.HookAnnotation | ||
import com.squareup.kotlinpoet.KModifier | ||
import com.squareup.kotlinpoet.ksp.TypeParameterResolver | ||
import com.squareup.kotlinpoet.ksp.toKModifier | ||
import com.squareup.kotlinpoet.ksp.toTypeName | ||
import com.squareup.kotlinpoet.ksp.toTypeParameterResolver | ||
|
||
internal data class HookMember( | ||
val name: String, | ||
val visibility: String, | ||
) | ||
|
||
internal data class HookSignature( | ||
val text: String, | ||
val isSuspend: Boolean, | ||
val returnType: String, | ||
/** For hooks that return a wrapped result, like [BailResult], this is the inner type */ | ||
val returnTypeType: String?, | ||
val hookFunctionSignatureType: KSTypeReference, | ||
val hookFunctionSignatureReference: KSCallableReference | ||
) { | ||
val nullableReturnTypeType = "${returnTypeType}${if (returnTypeType?.last() == '?') "" else "?"}" | ||
|
||
override fun toString() = text | ||
val isSuspend get() = hookFunctionSignatureType.modifiers.contains(Modifier.SUSPEND) | ||
val returnTypeText get() = hookFunctionSignatureReference.returnType.text | ||
val returnType get() = hookFunctionSignatureReference.returnType.toTypeName() | ||
val returnTypeType | ||
get() = hookFunctionSignatureReference.returnType.element?.typeArguments?.firstOrNull()?.toTypeName()!! | ||
val nullableReturnTypeType get() = returnTypeType.copy(nullable = true) | ||
val parameters get() = hookFunctionSignatureReference.functionParameters | ||
|
||
override fun toString() = hookFunctionSignatureType.text | ||
} | ||
|
||
internal class HookParameter( | ||
val name: String?, | ||
val type: String, | ||
val parameter: KSValueParameter, | ||
val position: Int, | ||
) | ||
|
||
internal val HookParameter.withType get() = "$withoutType: $type" | ||
internal val HookParameter.withoutType get() = name ?: "p$position" | ||
) { | ||
val name: String? get() = parameter.name?.asString() | ||
val type: String get() = parameter.type.text | ||
val withType get() = "$withoutType: $type" | ||
val withoutType get() = name ?: "p$position" | ||
} | ||
|
||
internal data class HookInfo( | ||
val property: HookMember, | ||
val hookType: HookType, | ||
val hookSignature: HookSignature, | ||
val params: List<HookParameter>, | ||
|
||
val propertyDeclaration: KSPropertyDeclaration, | ||
val annotation: HookAnnotation | ||
) { | ||
// TODO: Should this actually default to public? | ||
val propertyVisibility get() = propertyDeclaration.getVisibility().toKModifier() ?: KModifier.PUBLIC | ||
val zeroArity = params.isEmpty() | ||
val isAsync = hookType.properties.contains(HookProperty.Async) | ||
val parentResolver get() = (this.propertyDeclaration.parent as? KSClassDeclaration)?.typeParameters?.toTypeParameterResolver() ?: TypeParameterResolver.EMPTY | ||
} | ||
|
||
internal val HookInfo.tapMethod get() = if (!zeroArity) """ | ||
public fun tap(name: String, f: $hookSignature): String? = tap(name, generateRandomId(), f) | ||
public fun tap(name: String, id: String, f: $hookSignature): String? = super.tap(name, id) { _: HookContext, $paramsWithTypes -> f($paramsWithoutTypes) } | ||
""".trimIndent() else "" | ||
internal val HookInfo.paramsWithTypes get() = params.joinToString(transform = HookParameter::withType) | ||
internal val HookInfo.paramsWithoutTypes get() = params.joinToString(transform = HookParameter::withoutType) | ||
internal fun HookInfo.generateClass() = this.hookType.generateClass(this) | ||
internal fun HookInfo.generateProperty() = (if (hookType == HookType.AsyncParallelBailHook) "@kotlinx.coroutines.ExperimentalCoroutinesApi\n" else "") + | ||
"override val ${property.name}: $className = $className()" | ||
internal fun HookInfo.generateImports(): List<String> = emptyList() | ||
|
||
internal val HookInfo.superType get() = this.hookType.toString() | ||
|
||
internal val HookInfo.className get() = "${property.name.replaceFirstChar(Char::titlecase)}$superType" | ||
internal val HookInfo.typeParameter get() = "(${if (isAsync) "suspend " else ""}(HookContext, $paramsWithTypes) -> ${hookSignature.returnType})" | ||
internal val HookInfo.interceptParameter get() = "${if (isAsync) "suspend " else ""}(HookContext, $paramsWithTypes) -> Unit" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I was migrating from meta -> KSP in the first place, I tried to pull out all the intermediate data into pure codegen constructs, rather than a dependence on the modeling framework. This would ensure the codegen code is truly agnostic and you could potentially use the codegen module without needing the KSP part. i.e. if we want to use another processing framework (live javax.processing), or change the actual way we represent the DSL, or generate hooks via CLI.
I would like to try to continue following the strict separation of concerns. They can certainly be updated from simple String representations to whatever poet representation works best (
TypeName
, etc.).Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this earlier, but just putting this here for posterity. Today, we have multiple areas where we reach into the type resolver to get the information necessary to codegen the new class. When @sugarmanz looked at this, some of that was happening in the codegen, some in the validator, some in the processor. Instead what we discussed was making the whole pipeline a bit cleaner by changing it to be:
That way any potential frontend (we're not switching away from KSP anytime soon) just has to convert to KotlinPoet classes.