この記事は、JetBrainsIDE Advent Calendar 2019 の20日目の記事です。

JetBrainsIDE Advent Calendar 2019
https://qiita.com/advent-calendar/2019/jetbrains

IntelliJ IDEAのプラグイン開発ではいろいろな事が出来るのですが、
いろいろな事が出来てしまうために、
プラグイン開発の敷居が高いようにも感じています。

決まったパターンの開発から入ると敷居が少しは下がるかなと思い、
このエントリでは、
「IDE上から、特定のファイルに対して何らかの処理をしたい」というケースを想定して。
ファイルを選んだコンテキストメニューから、
そのファイルに対して何かを実行するというプラグインの作り方を、順を追って説明します。

プラグインプロジェクトの作成

では、早速プラグインを作っていきます。

IntelliJ IDEAを起動し、「Create New Project」を選びます。

図1

左メニューから「Gradle」を選び、
「Java」「IntelliJ Platform Plugin」にチェックを入れ、「Next」を押下します。

図2

GroupId, ArtifactIDを適当に決めて、「Next」を押下します。

図3

次の画面では、そのまま「Finish」を押下します。

準備が出来るまでしばらく待ちます。

図4

アクション定義ファイルの作成

次に、ファイルに対する処理を実装するクラスと、
その処理へのエントリ情報を定義するファイルを作成します。

src/main/java のコンテキストメニューで、
「New」→「Plugin DevKit」→「Action」を選びます。

図5

「Action ID」「Class Name」「Name」「Description」を適当に指定し、
「Add to Group」の
「Groups」に「ProjectViewPopupMenu」、「Anchor」に「Last」を指定します。

図6

この操作で、次のファイルが作成されます。

  • src/main/java/FileSelectAction.java
  • src/main/resources/META-INF/plugin.xml

「FileSelectAction.java」は処理を実装するクラスです。
「plugin.xml」には、
次のように、先ほど指定したアクションのエントリ情報が書かれています。

plugin.xml抜粋

<actions>
    <!-- Add your actions here -->
    <action id="file_select_action" class="FileSelectAction" text="File Select Action"
      description="file select action">
        <add-to-group group-id="ProjectViewPopupMenu" anchor="last"/>
    </action>
</actions>

処理が未実装のプラグインを実行

まだ、ファイルに対する処理は実装していませんが、
この段階でもエントリポイントは出来ているので、
プラグインを実行することで、エントリポイントを確認することが出来ます。

「Run」→「Run: Plugin」を選びます。

もう一つIntelliJ IDEAが起動するので、
適当にプロジェクトを作るか、既存のプロジェクトを開くかして、
プロジェクトが開いた状態にします。

プロジェクトビューで適当なファイルのコンテキストメニューを開くと、
「File Select Action」が追加されている事が確認出来ます。
# アクション定義作成時「Name」に指定した名称のメニューが出来ます。

図7

この状態では、
メニューを選んでも何も起きないので、処理を実装していきます。

アクションの実装

メニューを選んだときの処理は「FileSelectAction.java」に実装します。

ここまでの説明の操作を行った状態では、
以下のように、何も処理が実装されていないひな形が出来ています。

FileSelectAction.java

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;

public class FileSelectAction extends AnAction {

  @Override
  public void actionPerformed(AnActionEvent e) {
    // TODO: insert action logic here
  }
}

ファイルを選んだコンテキストメニューから何らかの処理を実行となると、
パターンが決まってくると思うので、以下に実装例を示します。

コメントに書いていますが、この例では、以下のような事をしています。

  • 拡張子を見てメニューの表示/非表示を切り替える
  • 処理実施前に、実施して良いかの確認ダイアログ表示
  • 処理完了後に、処理完了の通知を表示
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;

public class FileSelectAction extends AnAction {

  // メニュー表示/非表示の制御
  @Override
  public void update(AnActionEvent event) {
    VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE);
    // ここでは、拡張子「py」の場合のみメニューを表示している
    boolean active = vf != null && "py".equals(vf.getExtension());
    event.getPresentation().setEnabledAndVisible(active);
  }

  // メニューを選択した時の処理
  @Override
  public void actionPerformed(AnActionEvent event) {
    Project currentProject = event.getProject();
    VirtualFile vf = event.getData(CommonDataKeys.VIRTUAL_FILE);
    String fullPath = vf.getCanonicalPath();
    String basePath = currentProject.getBasePath();
    String filePath = fullPath.substring(basePath.length());

    // 処理を実施して良いかの確認ダイアログ表示
    int rtn = Messages.showOkCancelDialog(currentProject,
        String.format("%sに対して処理をします", filePath),
        "ファイル対する処理", "Ok", "Cancel", Messages.getQuestionIcon());

    if (rtn == Messages.OK) {
      // TODO: ここにファイルに対するなんらかの処理を実装

      // 処理完了を通知
      final Notification notification = new Notification(
          "FileSelectActionNotification",
          "ファイル対する処理",
          String.format("%sに対する処理を完了しました", filePath),
          NotificationType.INFORMATION);
      notification.notify(currentProject);
    }
  }
}

以上のように処理を実装したら、
「処理が未実装のプラグインを実行」の時と同じ流れで動作を確認することが出来ます。

プラグインのビルドとインストール

仕上げに作成したプラグインをビルドしてインストールします。

Terminalから以下のコマンドで、
作成したプラグインのビルドが出来ます。

$ gradle buildPlugin

ビルドが終わったら、
「Preferences」→「Plugins」→歯車→「Install Plugin from Disk」から、
build/distributions/以下に生成されたjarを選んでインストールします。

図8

おわりに

このように、やることのパターンがある程度決まっていれば、
プラグイン開発も始めやすいのかなと感じています。

IDEから移動せずに、
ファイルを選んで、何らかの処理を適用したいことって結構あると思うので、
このエントリを参考に、便利なプラグインを作ってみてはいかがでしょうか?