1*3c321d95SSadaf EbrahimiKSP Extensions for KotlinPoet 2*3c321d95SSadaf Ebrahimi============== 3*3c321d95SSadaf Ebrahimi 4*3c321d95SSadaf Ebrahimi`interop:ksp` is an interop API for converting 5*3c321d95SSadaf Ebrahimi[Kotlin Symbol Processing][ksp] (KSP) types to KotlinPoet types and 6*3c321d95SSadaf Ebrahimiwriting to KSP `CodeGenerator`. 7*3c321d95SSadaf Ebrahimi 8*3c321d95SSadaf Ebrahimi```kotlin 9*3c321d95SSadaf Ebrahimidependencies { 10*3c321d95SSadaf Ebrahimi implementation("com.squareup:kotlinpoet-ksp:<version>") 11*3c321d95SSadaf Ebrahimi} 12*3c321d95SSadaf Ebrahimi``` 13*3c321d95SSadaf Ebrahimi 14*3c321d95SSadaf Ebrahimi### Examples 15*3c321d95SSadaf Ebrahimi 16*3c321d95SSadaf EbrahimiExamples are based on reading the following property as a `KSProperty`: 17*3c321d95SSadaf Ebrahimi 18*3c321d95SSadaf Ebrahimi```kotlin 19*3c321d95SSadaf Ebrahimiclass Taco { 20*3c321d95SSadaf Ebrahimi internal inline val seasoning: String get() = "spicy" 21*3c321d95SSadaf Ebrahimi} 22*3c321d95SSadaf Ebrahimi``` 23*3c321d95SSadaf Ebrahimi 24*3c321d95SSadaf Ebrahimi**Convert a `KSType` to a `TypeName`** 25*3c321d95SSadaf Ebrahimi 26*3c321d95SSadaf Ebrahimi```kotlin 27*3c321d95SSadaf Ebrahimi// returns a `ClassName` of value `kotlin.String` 28*3c321d95SSadaf EbrahimiseasoningKsProperty.type.toTypeName() 29*3c321d95SSadaf Ebrahimi``` 30*3c321d95SSadaf Ebrahimi 31*3c321d95SSadaf Ebrahimi**Convert a `Modifier` to a `KModifier`** 32*3c321d95SSadaf Ebrahimi 33*3c321d95SSadaf Ebrahimi```kotlin 34*3c321d95SSadaf Ebrahimi// returns `[KModifier.INLINE]` 35*3c321d95SSadaf EbrahimiseasoningKsProperty.modifiers.mapNotNull { it.toKModifier() } 36*3c321d95SSadaf Ebrahimi``` 37*3c321d95SSadaf Ebrahimi 38*3c321d95SSadaf Ebrahimi**Convert a `Visibility` to a `KModifier`** 39*3c321d95SSadaf Ebrahimi 40*3c321d95SSadaf Ebrahimi```kotlin 41*3c321d95SSadaf Ebrahimi// returns `KModifier.INTERNAL` 42*3c321d95SSadaf EbrahimiseasoningKsProperty.getVisibility().toKModifier() 43*3c321d95SSadaf Ebrahimi``` 44*3c321d95SSadaf Ebrahimi 45*3c321d95SSadaf Ebrahimi**Write to `CodeGenerator`** 46*3c321d95SSadaf Ebrahimi 47*3c321d95SSadaf EbrahimiTo write a `FileSpec` to a KSP `CodeGenerator`, simply call the `FileSpec.writeTo(CodeGenerator, ...)` 48*3c321d95SSadaf Ebrahimiextension function. 49*3c321d95SSadaf Ebrahimi 50*3c321d95SSadaf Ebrahimi```kotlin 51*3c321d95SSadaf EbrahimifileSpec.writeTo(codeGenerator) 52*3c321d95SSadaf Ebrahimi``` 53*3c321d95SSadaf Ebrahimi 54*3c321d95SSadaf Ebrahimi### Type Parameters 55*3c321d95SSadaf Ebrahimi 56*3c321d95SSadaf EbrahimiType parameters can be declared on classes, functions, and typealiases. These parameters are then 57*3c321d95SSadaf Ebrahimiavailable to all of its enclosed elements. In order for these elements to resolve these in KSP, you 58*3c321d95SSadaf Ebrahimimust be able to reference these type parameters by their _index_. 59*3c321d95SSadaf Ebrahimi 60*3c321d95SSadaf EbrahimiIn `kotlinpoet-ksp` this is orchestrated by the `TypeParameterResolver` API, which can be passed 61*3c321d95SSadaf Ebrahimiinto most `toTypeName()` (or similar) functions to give them access to enclosing type parameters 62*3c321d95SSadaf Ebrahimithat they may reference. 63*3c321d95SSadaf Ebrahimi 64*3c321d95SSadaf EbrahimiThe canonical way to create an instance of this is to call `toTypeParameterResolver()` on a 65*3c321d95SSadaf Ebrahimi`List<KSTypeParameter>`. 66*3c321d95SSadaf Ebrahimi 67*3c321d95SSadaf EbrahimiConsider the following class and function 68*3c321d95SSadaf Ebrahimi 69*3c321d95SSadaf Ebrahimi```kotlin 70*3c321d95SSadaf Ebrahimiabstract class Taco<T> { 71*3c321d95SSadaf Ebrahimi abstract val seasoning: T 72*3c321d95SSadaf Ebrahimi} 73*3c321d95SSadaf Ebrahimi``` 74*3c321d95SSadaf Ebrahimi 75*3c321d95SSadaf EbrahimiTo properly resolve the type of `seasoning`, we need to pass the class `TypeParameterResolver` to 76*3c321d95SSadaf Ebrahimi`toTypeName()` so that it can properly resolve it. 77*3c321d95SSadaf Ebrahimi 78*3c321d95SSadaf Ebrahimi```kotlin 79*3c321d95SSadaf Ebrahimival classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver() 80*3c321d95SSadaf Ebrahimi// returns `T` 81*3c321d95SSadaf Ebrahimival seasoningType = seasoningKsProperty.type.toTypeName(classTypeParams) 82*3c321d95SSadaf Ebrahimi``` 83*3c321d95SSadaf Ebrahimi 84*3c321d95SSadaf Ebrahimi`TypeParameterResolver` is also composable to allow for multi-level nesting. `toTypeParameterResolver()` 85*3c321d95SSadaf Ebrahimihas an optional `parent` parameter to provide a parent instance. 86*3c321d95SSadaf Ebrahimi 87*3c321d95SSadaf EbrahimiConsider our previous example again, but this time with a function that defines its own type parameters. 88*3c321d95SSadaf Ebrahimi 89*3c321d95SSadaf Ebrahimi```kotlin 90*3c321d95SSadaf Ebrahimiclass Taco<T> { 91*3c321d95SSadaf Ebrahimi fun <E> getShellOfType(param1: E, param2: T) { 92*3c321d95SSadaf Ebrahimi 93*3c321d95SSadaf Ebrahimi } 94*3c321d95SSadaf Ebrahimi} 95*3c321d95SSadaf Ebrahimi``` 96*3c321d95SSadaf Ebrahimi 97*3c321d95SSadaf EbrahimiTo resolve its parameters, we need to create a `TypeParameterResolver` from the function's 98*3c321d95SSadaf Ebrahimi`typeParameters` and _compose_ it with the enclosing class's type parameters as a `parent`. 99*3c321d95SSadaf Ebrahimi 100*3c321d95SSadaf Ebrahimi```kotlin 101*3c321d95SSadaf Ebrahimival classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver() 102*3c321d95SSadaf Ebrahimival functionTypeParams = ksFunction.typeParameters.toTypeParameterResolver(parent = classTypeParams) 103*3c321d95SSadaf Ebrahimi// returns `[E, T]` 104*3c321d95SSadaf Ebrahimival seasoningType = ksFunction.parameterTypes.map { it.toTypeName(functionTypeParams) } 105*3c321d95SSadaf Ebrahimi``` 106*3c321d95SSadaf Ebrahimi 107*3c321d95SSadaf Ebrahimi### Incremental Processing 108*3c321d95SSadaf Ebrahimi 109*3c321d95SSadaf EbrahimiKSP supports [incremental processing][incremental] as 110*3c321d95SSadaf Ebrahimilong as symbol processors properly indicate originating files in generated new files and whether or 111*3c321d95SSadaf Ebrahiminot they are `aggregating`. `kotlinpoet-ksp` supports this via `OriginatingKSFiles`, which is a simple 112*3c321d95SSadaf EbrahimiAPI that sits atop KotlinPoet's `Taggable` API. To use this, simply add relevant originating files to 113*3c321d95SSadaf Ebrahimiany `TypeSpec`, `TypeAliasSpec`, `PropertySpec`, or `FunSpec` builders. 114*3c321d95SSadaf Ebrahimi 115*3c321d95SSadaf Ebrahimi```kotlin 116*3c321d95SSadaf Ebrahimival functionBuilder = FunSpec.builder("sayHello") 117*3c321d95SSadaf Ebrahimi .addOriginatingKSFile(sourceKsFile) 118*3c321d95SSadaf Ebrahimi .build() 119*3c321d95SSadaf Ebrahimi``` 120*3c321d95SSadaf Ebrahimi 121*3c321d95SSadaf EbrahimiLike KotlinPoet's _originating elements_ support for javac annotation processors, calling the 122*3c321d95SSadaf Ebrahimi`FileSpec.writeTo(CodeGenerator, ...)` function will automatically collect and de-dupe these originating 123*3c321d95SSadaf Ebrahimi`KSFile` references and automatically assemble them in the underlying `Dependencies` for KSP's reference. 124*3c321d95SSadaf Ebrahimi 125*3c321d95SSadaf EbrahimiOptionally you can define your own collection of files and pass them to the `writeTo` function, but usually 126*3c321d95SSadaf Ebrahimiyou don't need to do this manually. 127*3c321d95SSadaf Ebrahimi 128*3c321d95SSadaf EbrahimiLastly - `FileSpec.writeTo(CodeGenerator, ...)` also requires you to specify if your processor is 129*3c321d95SSadaf Ebrahimi_aggregating_ or not via required parameter by the same name. 130*3c321d95SSadaf Ebrahimi 131*3c321d95SSadaf Ebrahimi### TypeAlias Handling 132*3c321d95SSadaf Ebrahimi 133*3c321d95SSadaf EbrahimiFor `typealias` types, KSP interop will store a `TypeAliasTag` in the `TypeName`'s tags with a reference to the abbreviated type. This can be useful for APIs that want to resolve all un-aliased types. 134*3c321d95SSadaf Ebrahimi 135*3c321d95SSadaf Ebrahimi [ksp]: https://github.com/google/ksp 136*3c321d95SSadaf Ebrahimi [incremental]: https://github.com/google/ksp/blob/main/docs/incremental.md 137