拡張機能での正しくないコンパニオンオブジェクトの使用箇所を報告します。

Kotlin コンパニオンオブジェクトはそれに含まれるクラスをロードしようとすると常に作成されるため、拡張ポイントの実装は低コストで作成できるはずです。 プラグインでの過度なクラスのロードは IDE の起動にとって大きな問題です。

不正なパターン:


  class KotlinDocumentationProvider : AbstractDocumentationProvider(), ExternalDocumentationProvider {

      companion object {
          private val LOG = Logger.getInstance(KotlinDocumentationProvider::class.java)
          private val javaDocumentProvider = JavaDocumentationProvider()
      }
  }

以下の KotlinDocumentationProviderplugin.xml に登録されている拡張機能です。


  <lang.documentationProvider language="kotlin"
                              implementationClass="org.jetbrains.kotlin.idea.KotlinDocumentationProvider"
                              order="first"/>

この例では new KotlinDocumentationProvider() を呼び出されるだけで JavaDocumentationProvider がディスクからロードされます。

拡張ポイント実装内の Kotlin コンパニオンオブジェクトはロガーと単純な定数のみを含むことができます。 その他の宣言は過剰なクラスのロードや大量のリソース (例: TokenSet、Regex など) の早期初期化を 発生させる可能性があります。 @JvmStatic が付いた宣言もコンパニオンオブジェクトに余計なクラスを生成するため、計算コストが高くなる可能性があることに注意してください。

このような宣言はコンパニオンオブジェクトに格納するのではなく、トップレベルにするか、オブジェクト内に格納する必要があります。

FAQ

ConfigurationType の実行を書き換える方法

宣言をトップレベルに移動します。


  // 適切。トップレベルの fun を使用
  internal fun mnRunConfigurationType(): MnRunConfigurationType = runConfigurationType<MnRunConfigurationType>()

  internal class MnRunConfigurationType : ConfigurationType {
    companion object { // 不適切
      fun getInstance(): MnRunConfigurationType = runConfigurationType<MnRunConfigurationType>()
    }
    ...

FileType を書き換える方法

前:


  internal class SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {
    companion object {
      val FILE_TYPE = SpringBootImportsFileType()
      ...

後:


  internal object SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {
  ...

plugin.xmlINSTANCE fieldName を使用します。


  <fileType name="Spring Boot Imports"
              fieldName="INSTANCE"
              implementationClass="com.intellij.spring.boot.spi.SpringBootImportsFileType"/>

CounterUsagesCollector の書き換え方

内部 API

前:


  class AntActionsUsagesCollector : CounterUsagesCollector() {
    override fun getGroup(): EventLogGroup = GROUP

    companion object {
      private val GROUP = EventLogGroup("build.ant.actions", 1)

      @JvmField
      val runSelectedBuildAction = GROUP.registerEvent("RunSelectedBuild")
   }
}

後:


 object AntActionsUsagesCollector : CounterUsagesCollector() {
  override fun getGroup(): EventLogGroup = GROUP

  private val GROUP = EventLogGroup("build.ant.actions", 1)

  @JvmField
  val runSelectedBuildAction = GROUP.registerEvent("RunSelectedBuild")
}

2023.3 の新機能です