アレについて記す

IntelliJのpluginをScalaでつくる

Posted on March 03, 2019 at 20:15 (JST)

IntelliJ用のカスタムプラグインを作成してみたので、メモを残す。
ScalaMock用のプラグインを作成、せっかくなのでScalaで書いてみた。
ビルドにはGradleを使用した。

今回作成したコードはこちら

動作環境

OS: macOS Mojave ver. 10.14.1
Scala: 2.12.7

作成したプラグイン

scalamock-template-intelliJ-plugin

  • エディタ上でメソッドのシグネチャを選択して右クリックして、メニューから機能を呼び出す
  • 選択部分を文字列解析してメソッド名や引数などを取得し、あらかじめ用意していたテンプレートに埋め込み整形する
  • 結果をクリップボードにCopyする

プラグイン使用方法

クリップボードに保存された内容

(mock.execute[A](userId: Id[A], createdAt: ZonedDateTime)(implicit config: ApplicationConfig))
  .expects(*, *, *)
  .once()
  .returns(Try[Result])

※ テスト用に書いたメソッドなのでツッコミはご遠慮ください :-P


プロジェクト作成

公式のGetting StartedKotlinで書くIntelliJ Pluginを参考にIDEAからプラグイン用のプロジェクトを生成。
その後、GradleでScalaを使うためのセッティングを追加した。

GradleでScalaを動かす設定

[build.gradle(抜粋)]

buildscript {
    ext {
        scalaVersion = "2.12.7"
    }
    repositories {
        mavenCentral()
    }
}

apply plugin: 'scala'

dependencies {
    compile "org.scala-lang:scala-library:${scalaVersion}"
}


機能の実装

1. AnActionクラスを継承したクラスを作成する

  • com.intellij.openapi.actionSystem.AnAction を継承し、必要なメソッドをオーバーライドして機能を拡張する。
  • #actionPerformed には実際に行う処理(今回は選択した文字列を取り出して整形し、クリップボードにコピー)を記述する。
  • #update はメニューの活性/非活性を制御するための処理を記述。下記のコードではエディタで文字列を選択している場合に活性となる。
trait TemplateGenerator extends AnAction {

  def generateTemplate(text: String): String

  override def actionPerformed(e: AnActionEvent): Unit = {
    val editor         = e.getRequiredData(CommonDataKeys.EDITOR)
    val selectionModel = editor.getSelectionModel
    Option(selectionModel.getSelectedText) match {
      case Some(text) if !text.isEmpty =>
        val template = generateTemplate(text)
        CopyPasteManager.getInstance().setContents(new StringSelection(template))
      case _ => ()
    }
  }

  override def update(e: AnActionEvent): Unit = {
    val project = e.getProject
    val editor  = e.getData(CommonDataKeys.EDITOR)

    e.getPresentation.setVisible(
      project != null
      && editor != null
      && editor.getSelectionModel.hasSelection
    )
  }
}

2. 設定ファイルにてIntelliJのどの操作にマッピングするかを記述する。

画面操作(トリガー)と機能のマッピングは actions 要素に記載する

[src/main/resources/META-INF/plugin.xml(抜粋)]

<idea-plugin>
    <depends>com.intellij.modules.lang</depends>

    <actions>
        <group id="ScalaMockTemplate.Group.Main" text="ScalaMock Template"
               description="Group of ScalaMockTemplate"
               popup="true">
            <add-to-group group-id="EditMenu" anchor="last"/>

            <!-- 実装したクラスとのマッピング -->
            <action id="ScalaMockTemplate.Mock" class="MockTemplateAction" text="Mock Template"
                    description="Illustrates how to plug an action in" />
            <action id="ScalaMockTemplate.Stub" class="StubTemplateAction" text="Stub Template"
                    description="Illustrates how to plug an action in" />
        </group>

        <group id="MyPlugin.ScalaMock" text="ScalaMock template"
               description="ScalaMock menu"
               popup="false">
            <!-- エディタ上で右クリックした時に現れるメニューの最後に追加 -->
            <add-to-group group-id="EditorPopupMenu" anchor="last"/>

            <separator/>
            <!-- メニューに階層構造を持たせる -->
            <reference ref="ScalaMockTemplate.Group.Main"/>
        </group>
    </actions>
</idea-plugin>

3. 動作確認

Gradleタスクの runIde をクリックするとプラグインを適用した状態のIntelliJが立ち上がるので、実際に操作を行い動作確認ができる。

動作確認


単体テストについて

必要なライブラリ追加は build.gradle に記述する。

dependencies {
    testCompile group: 'org.scalatest', name: 'scalatest_2.12', version: '3.0.6'
    testCompile group: 'org.scalamock', name: 'scalamock_2.12', version: '4.1.0'
}

テストクラスに @RunWith でテストランナーを指定しないとテストクラスとして認識されないので注意。

@RunWith(classOf[JUnitRunner])
class MethodSignatureSpec extends FreeSpec {

下記コマンドでテストを実行する。
-i オプションはテスト結果詳細(アサーション失敗時の差分やプリントデバッグの内容など)を表示する。

$ ./gradlew test -i


ビルドについて

下記コマンドを実行すると build/distributions ディレクトリの下にzipファイルが出来る。

$ ./gradlew buildPlugin

Macなら IntelliJ IDEA => PreferencesPlugins にある歯車アイコンをクリックし、Install Plugin from Disc から作成したプラグインを選択し、再起動すれば使えるようになる。

プラグイン設定画面 プラグイン設定画面2

※ JetBrainsアカウントを持っていれば IntelliJ Plugin Repository に作成したプラグインを登録し配布することができる。(審査あり)
Publishing a plugin


以上。


参考URL