IntelliJ plugin を更新する

intellij plugin を久しぶりに更新した時のメモです。

自作のintellij pluginがエラーを吐くようになったので久しぶりに修正しようと思います。
(起動時にエラーポップアップが出るだけで動く)

バージョンアップ

まずは、全体的にバージョンが古いのでアップグレードしていきます。
プラグイン自体の実装は変更せずにライブラリのアップグレードだけで動くことを確認します。

下記のexampleを参考にして実装していきます。
https://plugins.jetbrains.com/docs/intellij/creating-plugin-project.html
https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin.html

intellij pluginは過去はdevkitプラグインというものを利用して開発環境をセットアップしてプラグインを開発してたので、
devkit使っていた人は下記のmigrationのドキュメントを参考。
https://plugins.jetbrains.com/docs/intellij/migrating-plugin-devkit-to-gradle.html
https://blog.jetbrains.com/platform/2021/12/migrating-from-devkit-to-the-gradle-build-system/

まずは下記をアップグレード。

  • java: 11->17
  • kotlin: 1.7->2.2
  • intellij platform gradle plugin: 1系->2.6.0

とりあえず下記のみの変更でビルドを通します。

build.gradle.kts

plugins {
    id("org.jetbrains.intellij.platform") version "2.6.0"
    kotlin("jvm") version "2.2.0"
}

group = "com.github.pppurple"
version = "1.1.0"

repositories {
    mavencentral()
}

gradleも8.14にアップグレード。

gradle-wrapper.properties

distributionbase=gradle_user_home
distributionpath=wrapper/dists
distributionurl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
zipstorebase=gradle_user_home
zipstorepath=wrapper/dists

上記がビルドが通ったので下記のようにintellijplatformを利用するように変更します。

build.gradle.kts

import org.jetbrains.kotlin.gradle.dsl.jvmtarget

plugins {
    id("org.jetbrains.intellij.platform") version "2.6.0"
    kotlin("jvm") version "2.2.0"
}

group = "com.github.pppurple"
version = "1.1.0"

repositories {
    mavencentral()
    intellijplatform {
        defaultrepositories()
    }
}

// configure gradle intellij plugin
// read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin.html
dependencies {
    intellijplatform {
        intellijideacommunity("2025.1.2")
        testframework(org.jetbrains.intellij.platform.gradle.testframeworktype.platform)
    }
}

intellijplatform {
    pluginconfiguration {
        ideaversion {
            // https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
            // https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html#earlier-versions
            // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijplatform-pluginconfiguration-ideaversion
            sincebuild = "201"
        }

        changenotes =
            """
                adapt to 2025.1.2
            """.trimindent()
    }
}

kotlin {
    compileroptions {
        jvmtarget.set(jvmtarget.jvm_21)
    }
}

target versionは下記から選択します。
https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions

新しいバージョンではplugin.xmlで設定している内容をbuild.gradle.ktsで設定出来るので、
build.gradle.kts側に移せる内容は移していきます。
(すべてbuild.gradle.ktsに設定出来るわけではないので、plugin.xmlは残ります)

plugin.xml

<idea-plugin>
  <!-- https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->
  <id>com.github.pppurple.locate-opened-file</id>
  <name>locate opened file</name>
  <change-notes>initial release of the plugin.</change-notes>
  <version>1.0.0</version>
  <vendor url="https://github.com/pppurple">pppurple</vendor>

上記の設定を build.gradle.ktsに移します。

build.gradle.kts

intellijplatform {
    pluginconfiguration {
        id = "com.github.pppurple.locate-opened-file"
        name = "locate opened file"
        vendor {
            name = "pppurple"
            url = "https://github.com/pppurple"
        }
        description =
            """
            <h1>locate opened file</h1>
            <p>this plugin provides scrolling to opened file without focusing on the project view.</p>
            <h2>usage</h2>
            <ol>
              <li>install the plugin</li>
              <li>default keymap is <code>shift + ctrl + s</code></li>
            </ol>
            """.trimindent()

        ideaversion {
            // https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
            // https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html#earlier-versions
            // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijplatform-pluginconfiguration-ideaversion
            sincebuild = "201"
        }

        changenotes =
            """
            adapt to 2025.1.2
            """.trimindent()
    }
}

プラグイン修正

上記で gradle が通ったらプラグインのアクション側の修正をしていきます。
まずは、context.selectin(target, false) がdeprecatedなので変更。

locateopenedfileaction.kt

target.selectin(context, /* requestfocus */ false)

そして、エラーが出ている根本原因を修正してきます。
下記エラーが現状出ています。

com.intellij.diagnostic.pluginexception: pppurple.locateopenedfileaction (pppurple.locateopenedfileaction): group with id "filechooser" isn't registered so the action won't be added to it; the action can be invoked via "find action" (module=plugindescriptor(name=locate opened file, id=com.github.pppurple.locate-opened-file, descriptorpath=plugin.xml, path=~/library/application support/jetbrains/intellijidea2025.1/plugins/locate-opened-file, version=1.1.0, package=null, isbundled=false))
    at com.intellij.openapi.actionsystem.impl.actionmanagerimplkt.reportactionerror(actionmanagerimpl.kt:1598)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimplkt.access$reportactionerror(actionmanagerimpl.kt:1)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimpl.getparentgroup(actionmanagerimpl.kt:795)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimpl.processaddtogroupnode(actionmanagerimpl.kt:754)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimpl.processactionelement(actionmanagerimpl.kt:495)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimpl.registerpluginactions(actionmanagerimpl.kt:361)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimpl.doregisteractions(actionmanagerimpl.kt:230)
    at com.intellij.openapi.actionsystem.impl.actionmanagerimpl.<init>(actionmanagerimpl.kt:131)
    at com.jetbrains.rdserver.ui.actionpopupmenu.backendactionmanager.<init>(backendactionmanager.kt:40)
    at java.base/java.lang.invoke.methodhandle.invokewitharguments(methodhandle.java:733)
    at com.intellij.platform.instancecontainer.instantiation.instantiatekt.instantiate$lambda$0(instantiate.kt:47)
    at com.intellij.platform.instancecontainer.instantiation.instantiatekt.instantiate$lambda$8$lambda$7(instantiate.kt:289)
    at com.intellij.platform.instancecontainer.instantiation.instantiatekt.withstoredtemporarycontext(instantiate.kt:306)
    at com.intellij.platform.instancecontainer.instantiation.instantiatekt.instantiate(instantiate.kt:288)
    at com.intellij.platform.instancecontainer.instantiation.instantiatekt.instantiate(instantiate.kt:40)
    at com.intellij.servicecontainer.serviceinstanceinitializer.createinstance$suspendimpl(serviceinstanceinitializer.kt:30)
    at com.intellij.servicecontainer.serviceinstanceinitializer.createinstance(serviceinstanceinitializer.kt)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder$initialize$1$1.invokesuspend(lazyinstanceholder.kt:163)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder$initialize$1$1.invoke(lazyinstanceholder.kt)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder$initialize$1$1.invoke(lazyinstanceholder.kt)
    at kotlinx.coroutines.intrinsics.undispatchedkt.startundispatchedorreturn(undispatched.kt:62)
    at kotlinx.coroutines.builderskt__builders_commonkt.withcontext(builders.common.kt:163)
    at kotlinx.coroutines.builderskt.withcontext(unknown source)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder$initialize$1.invokesuspend(lazyinstanceholder.kt:161)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder$initialize$1.invoke(lazyinstanceholder.kt)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder$initialize$1.invoke(lazyinstanceholder.kt)
    at kotlinx.coroutines.intrinsics.undispatchedkt.startcoroutineundispatched(undispatched.kt:27)
    at kotlinx.coroutines.coroutinestart.invoke(coroutinestart.kt:90)
    at kotlinx.coroutines.abstractcoroutine.start(abstractcoroutine.kt:123)
    at kotlinx.coroutines.builderskt__builders_commonkt.launch(builders.common.kt:52)
    at kotlinx.coroutines.builderskt.launch(unknown source)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder.initialize(lazyinstanceholder.kt:146)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder.access$initialize(lazyinstanceholder.kt:14)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder.tryinitialize(lazyinstanceholder.kt:136)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder.getinstance(lazyinstanceholder.kt:96)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder.getinstance$suspendimpl(lazyinstanceholder.kt:84)
    at com.intellij.platform.instancecontainer.internal.lazyinstanceholder.getinstance(lazyinstanceholder.kt)
    at com.intellij.platform.instancecontainer.internal.instancecontainerimpl.instance(instancecontainerimpl.kt:67)
    at com.intellij.servicecontainer.componentmanagerimpl.getserviceasync(componentmanagerimpl.kt:700)
    at com.intellij.openapi.components.serviceskt.serviceasync(services.kt:81)
    at com.intellij.platform.ide.bootstrap.applicationloader$preloadnonheadlessservices$2$5.invokesuspend(applicationloader.kt:598)
    at kotlin.coroutines.jvm.internal.basecontinuationimpl.resumewith(continuationimpl.kt:33)
    at kotlinx.coroutines.dispatchedtask.run(dispatchedtask.kt:104)
    at kotlinx.coroutines.scheduling.coroutinescheduler.runsafely(coroutinescheduler.kt:608)
    at kotlinx.coroutines.scheduling.coroutinescheduler$worker.executetask(coroutinescheduler.kt:873)
    at kotlinx.coroutines.scheduling.coroutinescheduler$worker.runworker(coroutinescheduler.kt:763)
    at kotlinx.coroutines.scheduling.coroutinescheduler$worker.run(coroutinescheduler.kt:750)

原因はadd-to-groupで指定しているgroupのfilechooserが存在しないため。
(恐らくどこかのバージョンでなくなった?)
https://plugins.jetbrains.com/docs/intellij/creating-actions-tutorial.html#registering-an-action-with-the-new-action-form

add-to-groupはそのpluginのアクションが追加される場所なので、存在しているgroupから適切なgroupを選んで指定します。
しかし、指定可能なgroupがドキュメント化されていない…
https://plugins.jetbrains.com/docs/intellij/action-system.html#action-declaration-reference

下記がこのプラグインとしては候補なので、editorpopupmenuを選択する。

  • editorpopupmenu - エディタの右クリックメニュー
  • projectviewpopupmenu - プロジェクトビューの右クリックメニュー
  • toolsmenu - ツールメニュー
  • editmenu - 編集メニュー
  • viewmenu - 表示メニュー

そして既存のエディタの右クリックメニューで「compare with clipboard」の前に配置したいので、そのaction id を指定します。
(action idは下記から検索出来るが、action groupなどでグループ化されているものもあり、定義ルールがよく分からず探しづらい)
https://github.com/jetbrains/intellij-community/blob/idea/251.26094.121/platform/ide-core/src/com/intellij/openapi/actionsystem/ideactions.java
https://github.com/jetbrains/intellij-community/blob/3534a3d6bc7799514c194e7b1d9fb076a3de3548/platform/platform-resources/src/idea/langactions.xml

または下記のui inspectorでaction idを検索出来る。
https://plugins.jetbrains.com/docs/intellij/internal-ui-inspector.html

今回はui inspectorで検索していきます。
使うためにまずはinternal modeをonにします。
https://plugins.jetbrains.com/docs/intellij/enabling-internal.html

idea.properties

idea.is.internal=true

tools->internal actions->ui->ui inspectorを選択して有効化します。

ui inspectorでメニューを挿入したい箇所をクリックすることで詳細画面が表示されます。詳細画面からaction idを検索します。

action idがcompareclipboardwithselectionと分かったので、
下記の通りそのactionの前に追加するように設定し、想定通りに動くどうか確認します。

plugin.xml

<add-to-group group-id="editorpopupmenu" anchor="before" relative-to-action="compareclipboardwithselection" />

デバッグ

https://plugins.jetbrains.com/docs/intellij/creating-plugin-project.html#running-a-plugin-with-the-runide-gradle-task
gradle runide を実行します。
デバッグ用の intellij が立ち上がるので、そこで動作確認します。

shift control s で想定通りに動くことを確認します。
そして、エディタの右クリックメニューで「compare with clipboard」の前に「locate opened file」が表示されるようになることを確認します。

verify

gradleタスクで互換性のテストをすることが出来ます。
gradle verifyplugin を実行することで検証することが出来ますが、sinceからのバージョンをダウンロードして検証していくのでかなり時間がかかります。
publishしてもmarket place側で検証してくれるので、ローカルでのvefiryは諦めました。

参考
https://blog.jetbrains.com/platform/2021/12/migrating-from-devkit-to-the-gradle-build-system/
https://plugins.jetbrains.com/docs/intellij/configuring-gradle.html#verifying-plugin
https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijplatform-pluginverification-ides

publish

初回の公開はmarketplaceにログインして、手動でアップロードする必要がありますが、
新バージョンの公開は手動でアップロードも出来ますが、gradle タスクでも公開出来ます。
https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html

今回はgradleタスクでアップロードしてみます。
まずはhttps://plugins.jetbrains.com/author/me/tokens から token を作成します。

そして、tokenを環境変数にセットします。

export org_gradle_project_intellijplatformpublishingtoken='your_token'

(実際は intellij の edit configuration から gradle publishplugin の設定の環境変数にセットして実行)

gradle publishplugin を実行すると下記エラーとなりました。

failure: build failed with an exception.

what went wrong: execution failed for task ':publishplugin'.
failed to upload plugin: upload failed: the vendor's trader/non-trader status has not been declared. please do so by visiting this link: https://plugins.jetbrains.com/author/me.

https://plugins.jetbrains.com/author/me
から vendor status を設定する必要があるようです。
エラーメッセージによると、jetbrains marketplace にプラグインを公開するためには、ベンダーの trader/non-trader ステータスを宣言する必要があります。

設定後再度gradle publishpluginを実行するとうまく pulish されました。
plugin のページを見るとサポートされているバージョンに対して compatibility verification が実行されます。

jetbrains is reviewing this update. please expect it to be approved within two business days of the upload.
と記述がある通り、jetbrains 側でレビューされて公開されます。

すぐに approve されて無事公開されました。

下記の通り、plugin ページの "what’s new" なども更新されています。

intellij 側でもプラグイン一覧で更新可能になりました。

おわり。

プラグインページ

plugins.jetbrains.com

ソースのリポジトリ

github.com