You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改Kotlin的PSI?IntelliJ插件开发技术求助

解决Kotlin PSI修改问题:为接口自动生成实现自身的伴生对象

需求明确

需要开发IntelliJ IDEA插件,让IDE解析Kotlin接口时,自动将以下代码:

interface TestInterface {
    var testProperty: Int
}

识别为包含实现自身的伴生对象的版本:

interface TestInterface {
    var testProperty: Int

    companion object: TestInterface {
        override var testProperty: Int
            get() {
                TODO("Do something")
            }
            set(value) {
                TODO("Do something")
            }
    }
}

为什么kapt无法实现

kapt是Kotlin编译期注解处理器,核心作用是生成物理源文件,而你的需求是IDE层面的动态PSI扩展——让IDE在代码分析时“虚拟”识别出伴生对象,不修改实际文件,这不属于kapt的处理范畴,因此方向错误。

正确实现方案:基于IntelliJ Kotlin PSI扩展

使用KtPsiAugmentProvider实现动态PSI元素添加,这是IntelliJ Kotlin插件用于扩展代码洞察的标准方式。

1. 实现PsiAugmentProvider

import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject

class InterfaceCompanionAugmentProvider : KtPsiAugmentProvider() {
    override fun <Psi : PsiElement> collectAugments(
        element: PsiElement,
        type: Class<Psi>,
        context: PsiFile
    ): List<Psi> {
        // 仅处理Kotlin接口
        val ktInterface = element as? KtClass ?: return emptyList()
        if (!ktInterface.isInterface()) return emptyList()
        // 避免重复生成伴生对象
        if (ktInterface.companionObjects.isNotEmpty()) return emptyList()

        val companionObject = generateCompanionObject(ktInterface.project, ktInterface) ?: return emptyList()
        @Suppress("UNCHECKED_CAST")
        return if (type.isInstance(companionObject)) listOf(companionObject as Psi) else emptyList()
    }

    private fun generateCompanionObject(project: Project, ktInterface: KtClass): KtObjectDeclaration? {
        val interfaceName = ktInterface.name ?: return null
        val factory = KtPsiFactory(project)

        // 构建伴生对象基础代码
        val companionBaseText = """
            companion object : $interfaceName {
                ${ktInterface.properties.joinToString("\n") { prop ->
                    "override var ${prop.name}: ${prop.typeReference?.text ?: "Unit"}"
                }}
            }
        """.trimIndent()

        val objectDeclaration = factory.createObjectDeclaration(companionBaseText)
        // 为每个属性添加自定义getter/setter
        objectDeclaration.properties.forEach { prop ->
            val getter = factory.createPropertyGetter("TODO(\"Do something\")")
            val setter = factory.createPropertySetter("value", "TODO(\"Do something\")")
            prop.add(getter)
            prop.add(setter)
        }
        return objectDeclaration
    }
}

2. 注册扩展

在插件的plugin.xml中添加以下配置,让IntelliJ识别这个扩展:

<extensions defaultExtensionNs="org.jetbrains.kotlin">
    <psiAugmentProvider implementation="io.github.sgpublic.exspplugin.core.kt.InterfaceCompanionAugmentProvider"/>
</extensions>

关键注意点

  • 必须使用KtPsiAugmentProvider而非直接修改PSI:直接修改PSI会破坏IDE的代码一致性,而AugmentProvider是官方提供的动态扩展入口,确保代码分析、补全、跳转等功能正常。
  • 校验重复生成:检查接口是否已有伴生对象,避免重复添加。
  • 使用KtPsiFactory构建PSI元素:手动拼接文本容易出现语法错误,通过工厂类生成的PSI元素能保证符合Kotlin语法规范。

内容的提问来源于stack exchange,提问作者Madray Haven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 09:25:36