tokuhirom's blog

AssertJ 用の IntelliJ postfix completion plugin を書いた

AssertJ の assertThat() をpostfix completion で入力したいと常々思っていて、誰か書いてくれないかなと思っていたけど誰も書いてくれないし、IntelliJ のプラグインというものを書いてみたかったので書いてみた。

https://github.com/tokuhirom/assertj-postfix-plugin

↑レポジトリはこちら。

IntelliJ のプラグインの情報は少なくて、どうもどこが本家なのかよくわからなかった。 http://www.jetbrains.org/intellij/sdk/docs/index.html たぶんこの辺。だと思う。

とりあえず検索してみたところ IntelliJ IDEAのPostfix補完プラグインを作るという記事が見つかったので、「こりゃあ、真似すれば簡単に実装できそうだなー」と思ったのが運の尽き。

どーも、ここに書いてある情報だけでは実装は無理っぽい。。 なんか実行時例外出るし。。そもそも俺 intellij plugin の書き方が全くわかってないし。。

で、いろいろ調べてみたところ、IntelliJ のプラグインは gradle つかって開発するのが楽っぽい。

gradle-intellij-plugin ってので、簡単にビルドできる。./gradlew runIdea ってすると、gradle が intellij idea のコミュニティエディションをダウンロードしてきてそれを実行してくれる。

https://github.com/JetBrains/gradle-intellij-plugin

build.gradle は以下の様な感じ。


plugins {
    id "org.jetbrains.intellij" version "0.0.32"
}

apply plugin: 'org.jetbrains.intellij'
apply plugin: 'java'


sourceCompatibility = 1.8

intellij {
    pluginName 'AssertJ Postfix Plugin'
}

group 'me.geso.assertj_postfix_plugin'
version '0.0.1'

task wrapper(type: Wrapper) {
    gradleVersion = '2.6'
}

repositories {
    mavenCentral()
}

ディレクトリ構成は以下のようになっている。

src
└── main
    ├── java
    │   └── me
    │       └── geso
    │           └── assertj_postfix_plugin
    │               ├── AssertJPostfixTemplateProvider.java
    │               └── AssertJTemplate.java
    └── resources
        ├── META-INF
        │   └── plugin.xml
        └── postfixTemplates
            └── AssertJTemplate
                ├── after.java.template
                ├── before.java.template
                └── description.html

src/main/resources/postfixTemplates/ 以下にテンプレートのデータが入っている。これは実際になにかの役に立つわけではないが、入れておかないと例外があがって死ぬ。各テンプレートごとに after.java.template, before.java.template, description.html の3つのファイルが必要だ。 これらのテンプレートは、以下のような場所に表示される。表示されるだけなので、動作には関係ない。

META-INF/plugin.xml がプラグインのメタデータ。プラグインのIDやらなんやらの基本的な設定がここに書いてある。

codeInsight.template.postfixTemplateProvider のあたりが重要で、テンプレートプロバイダを設定している。あとは他のプラグインでも一緒。

<idea-plugin version="2">
    <id>me.geso.assertj_postfix_plugin</id>
    <name>AssertJ Postfix Completion Plugin</name>
    <version>0.0.1</version>
    <vendor email="tokuhirom+intellij@gmail.com" url="http://64p.org">tokuhirom</vendor>

    <description><![CDATA[
        This plugin adds postfix completion template for assertj.
    ]]></description>

    <change-notes><![CDATA[
        <ul>
        <li>0.0.1 - The first release.
        </ul>
    ]]>
    </change-notes>

    <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
    <idea-version since-build="141.0"/>

    <extensions defaultExtensionNs="com.intellij">
        <codeInsight.template.postfixTemplateProvider language="JAVA"
                                                      implementationClass="me.geso.assertj_postfix_plugin.AssertJPostfixTemplateProvider"/>
    </extensions>

    <actions>
    </actions>

</idea-plugin>

で、テンプレートの入り口は以下のとおり。テンプレートのセットを作って返しているだけ。

package me.geso.assertj_postfix_plugin;

import java.util.HashSet;
import java.util.Set;

import org.jetbrains.annotations.NotNull;

import com.intellij.codeInsight.template.postfix.templates.JavaPostfixTemplateProvider;
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;

public class AssertJPostfixTemplateProvider extends JavaPostfixTemplateProvider {
    private final HashSet<PostfixTemplate> templates;

    public AssertJPostfixTemplateProvider() {
        templates = new HashSet<>();
        templates.add(new AssertJTemplate());
    }

    @NotNull
    @Override
    public Set<PostfixTemplate> getTemplates() {
        return templates;
    }
}

テンプレートの実装、以下の様な感じになる。StringBasedPostfixTemplate ってやつを継承することにより、なんかそれっぽいテンプレートを書くだけでよいという感じになっている。

package me.geso.assertj_postfix_plugin;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.intellij.codeInsight.template.Template;
import com.intellij.codeInsight.template.Template.Property;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate;
import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils;
import com.intellij.psi.PsiElement;

public class AssertJTemplate extends StringBasedPostfixTemplate {

    public AssertJTemplate() {
        super("assertThat", "assertThat(expr);",
             JavaPostfixTemplatesUtils.selectorAllExpressionsWithCurrentOffset(JavaPostfixTemplatesUtils.IS_NON_VOID));
    }

    @Override
    public Template createTemplate(TemplateManager templateManager, String s) {
        Template template = super.createTemplate(templateManager, s);
        template.setValue(Property.USE_STATIC_IMPORT_IF_POSSIBLE, true);
        return template;
    }

    @Nullable
    @Override
    public String getTemplateString(@NotNull PsiElement psiElement) {
        return  "org.assertj.core.api.Assertions.assertThat($expr$);$END$";
    }
}

デバッガで実行すれば、プラグインが読み込まれた状態の IDEA が起動するので、動作を確認すればOK。普通の java アプリと同様に、デバッガでステップ実行とかもできる。便利。

出来上がったら、 https://plugins.jetbrains.com/ で publish すれば OK.

./gradlew publishPlugin やらでアップロードできるが、とりあえずは普通に ./gradlew buildPlugin して、form からアップロードした。plugin.xml が含まれてないと言われてアップロード失敗したので、./gradlew jar した jar をアップした。二回目からは publishPlugin でやれる。と思う。

そういうわけで、アップしてみてある。ご利用ください。と言いたいところだけど、まだ承認待なのでした。 https://plugins.jetbrains.com/plugin/8093?pr=

Created: 2015-12-19 10:14:59 +0900
Updated: 2015-12-19 10:14:59 +0900