「はてなエンジニア Advent Calendar 2024」の2025/01/07 の記事です。
イメージ
モデルはペットのおこげです。 ゆえに okoge-progress-barです。かわいい。
Marketplace
ソース
IntelliJ Pluginの 仕組み
IntelliJ Platform SDK
IntelliJ Platform SDKは、JetBrains製IDE(IntelliJ IDEA、PyCharm、WebStormなど)用のプラグインを開発するためのツールとAPIを提供する開発者向けキットです。
SDKを使用することで、次のようなことが実現できます:
- カスタムUIの追加(今回のプログレスバーのような機能)
- アクションやリスナーを通じてイベントを処理
- メニューやツールウィンドウの拡張
intellij-platform-plugin-template
intellij-platform-plugin-template は、JetBrainsが提供するプラグイン開発のためのテンプレートリポジトリです。プラグイン開発の初期設定とサンプルコード、公開のための設定が入ってます。
plugin.xml
plugin.xmlは、プラグインの設定や動作を定義するファイルです。
IntelliJ Platform では イベント駆動型アーキテクチャを採用しているようで、さまざまなイベントに応じてリスナーを登録できます。
このファイルでは、そのListenerを登録することで、IntelliJプラットフォームのイベント(アプリケーション、プロジェクト、UI変更など)にフックしてカスタム処理を実行できます。
<idea-plugin>
<id>kk.kkhouse777.okogeprogressbar</id>
<name>okoge-progress-bar</name>
<vendor>kk__777</vendor>
<depends>com.intellij.modules.platform</depends>
<description><![CDATA[
Pretty progress bars featuring a dog named Okoge, designed for IJ-based IDEs.
]]></description>
<applicationListeners>
<listener class="kk.kkhouse777.okogeprogressbar.listeners.OkogeProgressListener"
topic="com.intellij.ide.ui.LafManagerListener"/> <!-- おそらく不要 -->
<listener class="kk.kkhouse777.okogeprogressbar.listeners.OkogeProgressListener"
topic="com.intellij.openapi.application.ApplicationActivationListener"/>
</applicationListeners>
</idea-plugin>
ほかにも PluginのWindowで表示される説明文等のmetadataの設定もここで行います。
実装
素材を用意
知り合い作。3232 と 6464 で 準備。
サイズ感は先駆者👇にならいました。
NyanProgressBar
javax.swing.BasicProgressBarUIを拡張する
IntelliJのプログレスバーはjavax.swing.JProgressBarをベースにしているため、BasicProgressBarUIを拡張してカスタマイズします。
参考: javax.swing
import com.intellij.openapi.ui.GraphicsConfig;
import com.intellij.util.ui.GraphicsUtil;
import com.intellij.util.ui.JBUI;
import kk.kkhouse777.okogeprogressbar.okoge.OkogeProgressBarState;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicProgressBarUI;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.RoundRectangle2D;
public class OkogeProgressBarUI extends BasicProgressBarUI {
@SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"})
public static ComponentUI createUI(JComponent c) {
return new OkogeProgressBarUI();
}
@Override
protected void paintIndeterminate(Graphics g2d, JComponent c) {
// ~~略
}
@Override
protected void paintDeterminate(Graphics g, JComponent c) {
// ~~略
}
// ...
}
-
paintIndeterminate
- プログレスバーが進行状態を特定できない場合に描画される
- paintDeterminate
- プログレスバーの進行状況が特定できる場合に描画される
描画部分は javax.swing に倣えばよいので、例えばシンプルにオレンジ色のバーを表示するならこんな感じになります。
@Override
protected void paintDeterminate(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
try {
// 背景の描画
g2.setColor(Color.LIGHT_GRAY);
g2.fillRect(0, 0, c.getWidth(), c.getHeight());
// 進捗バーの描画
int progress = progressBar.getValue();
int width = (int) (c.getWidth() * (progress / 100.0));
g2.setColor(Color.ORANGE);
g2.fillRect(0, 0, width, c.getHeight());
} finally {
g2.dispose();
}
}
拡張したProgressBar を Listener で 登録する
import com.intellij.ide.ui.LafManager
import com.intellij.ide.ui.LafManagerListener
import com.intellij.openapi.application.ApplicationActivationListener
import com.intellij.openapi.wm.IdeFrame
import kk.kkhouse777.okogeprogressbar.OkogeProgressBarUI
import javax.swing.UIManager
class OkogeProgressListener :
LafManagerListener,
ApplicationActivationListener {
override fun applicationActivated(ideFrame: IdeFrame) {
updateProgressBar()
}
override fun lookAndFeelChanged(p0: LafManager) {
updateProgressBar()
}
companion object {
private const val PROGRESS_BAR_UI_KEY = "ProgressBarUI"
private const val PROGRESS_BAR_UI_CLASS_NAME = "kk.kkhouse777.okogeprogressbar.ui.OkogeProgressBarUI"
private fun updateProgressBar() {
UIManager.put(PROGRESS_BAR_UI_KEY, PROGRESS_BAR_UI_CLASS_NAME)
UIManager.getDefaults().put(PROGRESS_BAR_UI_CLASS_NAME, OkogeProgressBarUI::class.java)
}
}
}
-
LafManagerListener
- テーマが変更されたときに、カスタムプログレスバーの外観や設定が反映されるようにします
- (一応 入れているのですが おそらく不要)
-
ApplicationActivationListener
- IDEがアクティブ化された際に、UI設定を再適用することで、プログレスバーが正しく表示されるようにします
UIManagerで指定するkey は 以下を参考にしました。
https://github.com/kagof/intellij-pokemon-progress/blob/master/src/main/java/com/kagof/intellij/plugins/pokeprogress/PokemonProgressListener.java
plugin.xmlにListener を宣言する
<idea-plugin>
<id>kk.kkhouse777.okogeprogressbar</id>
<name>okoge-progress-bar</name>
<vendor>kk__777</vendor>
<depends>com.intellij.modules.platform</depends>
<description><![CDATA[
Pretty progress bars featuring a dog named Okoge, designed for IJ-based IDEs.
]]></description>
<applicationListeners>
<listener class="kk.kkhouse777.okogeprogressbar.listeners.OkogeProgressListener"
topic="com.intellij.ide.ui.LafManagerListener"/> <!-- おそらく不要 -->
<listener class="kk.kkhouse777.okogeprogressbar.listeners.OkogeProgressListener"
topic="com.intellij.openapi.application.ApplicationActivationListener"/>
</applicationListeners>
</idea-plugin>
公開
intellij-platform-plugin-template が すでにいろいろ準備してくれています。
credential周り
intellij plugin の公開タスク等をまとめている gradle plugin org.jetbrains.intellij.platform
が すでに入っていて以下設定があります。
...
intellijPlatform {
pluginConfiguration {
...
signing {
certificateChain = providers.environmentVariable("CERTIFICATE_CHAIN")
privateKey = providers.environmentVariable("PRIVATE_KEY")
password = providers.environmentVariable("PRIVATE_KEY_PASSWORD")
}
この環境変数へ それぞれのcredential に設定します。
詳細は上記 を参照で良さそうですが、さっくり
- private key を作成
openssl genpkey\
-aes-256-cbc\
-algorithm RSA\
-out private_encrypted.pem\
-pkeyopt rsa_keygen_bits:4096
- 自己署名証明書を作成
openssl rsa -in private_encrypted.pem -out private.pem
openssl req -key private.pem -new -x509 -days 365 -out chain.crt
- IntelliJ MarketPlaceのAPIトークンを取得
- 環境変数(GithubAction含む)に登録する
となります。
github action
templateには ワークフローが いくつか定義されています。
- main への 変更をフックに releaseのドラフトが作られる
- 上記を publish する と MarketPlaceに公開される(初回のみ手動で公開する必要がある
その他必要なこと
ReadMeの修正 や MarketPlaceへのmeta dataの登録