跳到内容

KotlinPoet 的 KSP 扩展

`interop:ksp` 是一个互操作 API,用于将 Kotlin Symbol Processing (KSP) 类型转换为 KotlinPoet 类型,并写入到 KSP `CodeGenerator`。

dependencies {
  implementation("com.squareup:kotlinpoet-ksp:<version>")
}

示例

以下示例基于将以下属性读取为 `KSProperty`

class Taco {
  internal inline val seasoning: String get() = "spicy"
}

将 `KSType` 转换为 `TypeName`

// returns a `ClassName` of value `kotlin.String`
seasoningKsProperty.type.toTypeName()

将 `Modifier` 转换为 `KModifier`

// returns `[KModifier.INLINE]`
seasoningKsProperty.modifiers.mapNotNull { it.toKModifier() }

将 `Visibility` 转换为 `KModifier`

// returns `KModifier.INTERNAL`
seasoningKsProperty.getVisibility().toKModifier()

写入到 `CodeGenerator`

要将 `FileSpec` 写入到 KSP `CodeGenerator`,只需调用 `FileSpec.writeTo(CodeGenerator, ...)` 扩展函数即可。

fileSpec.writeTo(codeGenerator)

类型参数

类型参数可以在类、函数和类型别名上声明。然后,这些参数可用于其所有封闭元素。为了使这些元素在 KSP 中解析这些类型参数,您必须能够通过它们的 索引 来引用这些类型参数。

在 `kotlinpoet-ksp` 中,这由 `TypeParameterResolver` API 来协调,该 API 可以传递给大多数 `toTypeName()`(或类似)函数,以便它们可以访问可能引用的封闭类型参数。

创建此实例的标准方法是在 `List<KSTypeParameter>` 上调用 `toTypeParameterResolver()`。

考虑以下类和函数

abstract class Taco<T> {
  abstract val seasoning: T
}

为了正确解析 `seasoning` 的类型,我们需要将类的 `TypeParameterResolver` 传递给 `toTypeName()`,以便它可以正确地解析它。

val classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver()
// returns `T`
val seasoningType = seasoningKsProperty.type.toTypeName(classTypeParams)

`TypeParameterResolver` 也是可组合的,允许多层嵌套。`toTypeParameterResolver()` 有一个可选的 `parent` 参数,用于提供父实例。

再次考虑我们之前的示例,但这次是一个定义了自己类型参数的函数。

class Taco<T> {
  fun <E> getShellOfType(param1: E, param2: T) {

  }
}

为了解析其参数,我们需要从函数的 `typeParameters` 创建一个 `TypeParameterResolver`,并将其与封闭类的类型参数 组合 为一个 `parent`。

val classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver()
val functionTypeParams = ksFunction.typeParameters.toTypeParameterResolver(parent = classTypeParams)
// returns `[E, T]`
val seasoningType = ksFunction.parameterTypes.map { it.toTypeName(functionTypeParams) }

增量处理

只要符号处理器在生成的新文件中正确指示源文件并说明它们是否正在进行 `aggregating`,KSP 就支持增量处理。`kotlinpoet-ksp` 通过 `OriginatingKSFiles` 支持这一点,这是一个位于 KotlinPoet 的 `Taggable` API 之上的简单 API。要使用它,只需将相关的源文件添加到任何 `TypeSpec`、`TypeAliasSpec`、`PropertySpec` 或 `FunSpec` 构建器中。

val functionBuilder = FunSpec.builder("sayHello")
  .addOriginatingKSFile(sourceKsFile)
  .build()

与 KotlinPoet 为 javac 注解处理器提供的 源元素 支持类似,调用 `FileSpec.writeTo(CodeGenerator, ...)` 函数将自动收集并去重这些源 `KSFile` 引用,并自动将它们组装到底层的 `Dependencies` 中供 KSP 引用。

您也可以选择定义自己的文件集合并将其传递给 `writeTo` 函数,但通常不需要手动执行此操作。

最后,`FileSpec.writeTo(CodeGenerator, ...)` 还要求您通过同名的必需参数指定您的处理器是否正在进行 aggregating

类型别名处理

对于 `typealias` 类型,KSP 互操作会在 `TypeName` 的标签中存储一个 `TypeAliasTag`,其中包含对缩写类型的引用。这对于想要解析所有非别名类型的 API 非常有用。