IntellijJの言語プラグインを作る方法

Kotlin開発で使うIDEと言えば[IntelliJ](https://www.jetbrains.com/idea/download/)。他の選択肢がそもそもありません（AndroidStudioもIntelliJ）。幸いなことに無料のCommunity Editionも存在します。その無料バージョンがなければ、Kotlinがそれほど使われてないでしょう。

他のIDEと同じく、IntelliJも[プラグイン](https://plugins.jetbrains.com/)で拡張できるIDEです。IntelliJのプラグインは誰でも作ることができます。それでは、今回はIntelliJの言語プラグインを作る方法を紹介したいと思います。

# IntelliJの言語プラグイン

そもそもIntelliJの言語プラグインとはなんですか？

あるプログラミング言語を全般的に対応するとは簡単に言えますが、具体的に下記のいずれかの機能を追加することによってIntelliJの言語プラグインになります：

- ファイルのパーサー
- ファイルのシンタックスハイライター
- オートコンプリーション機能
- IntelliJ IDEならではの追加情報提供
    - 行数の隣に何かを出す(Line Marker)
    - アノテーション
    - ...
- フォーマッター／リンター
- ...

例えば、`.json`ファイルのシンタックスハイライトするプラグインでも言語プラグインといいます。

対応できる機能は[Documentation](https://www.jetbrains.org/intellij/sdk/docs/tutorials/custom_language_support_tutorial.html)で確認できます。

# 言語プラグインを作る

さあ、プラグインはどうやって作れるんでしょうか？

開発するため、IntelliJが色々なツールを提供しています。gradleプラグインを追加するとそのツールは使えます。

```gradle
plugins {
    id 'org.jetbrains.intellij' version '0.4.15'
}
intellij {
    version '2019.3'
}
```

上記の記述だとIntelliJ 2019.3向けのプラグインになります。pluginsブロックで指定するバージョンはgradle plugin自体のバージョンであり、最新のものであればより良いでしょう。

## 機能の定義

プラグインの基本情報とプラグインは何ができるかを定義する必要があります。今回は[前の記事](https://qiita.com/lukasjapan/items/e0af8218e378ee17369c)に紹介したテンプレートエンジン[Koreander](https://github.com/lukasjapan/koreander)のシンタックスハイライターを作ります。

```xml
<idea-plugin>
    <id>de.cvguy.kotlin.koreander.intellij</id>
    <version>0.10</version>
    <name>Koreander Syntax Highlighter</name>
    <vendor email="lukas@cvguy.de" url="https://github.com/lukasjapan/koreander">Koreander</vendor>

    <description><![CDATA[
        Language Support for the Koreander Template Engine.

        Currently only Syntax highlighting is supported.
    ]]></description>

    <depends>org.jetbrains.kotlin</depends>

    <extensions defaultExtensionNs="com.intellij">
        <fileType implementationClass="de.cvguy.kotlin.koreander.intellij.KoreanderFileType" name="Koreander" language="Koreander" extensions="kor" />
        <lang.syntaxHighlighterFactory language="Koreander" implementationClass="de.cvguy.kotlin.koreander.intellij.KoreanderSyntaxHighlighterFactory"/>
    </extensions>
</idea-plugin>
```

見るだけで大体わかると思います。基本情報に関しては説明することは特にないと思います。プラグインの機能は`<extensions>`内で定義し、実装はどこにあるかは`implementationClass`のattributeで書きます。具体的に説明すると`<fileType>`でプラグインが動作するファイル拡張子を定義し、`<lang.syntaxHighligherFactor>`でシンタックスハイライターの機能を定義します。どっちにも`language="Koreander"`のattributeがあるので紐付けができています。

IntelliJプラグイン開発の約束としてはこのファイルを`resources/META-INF/plugin.xml`に置きます。

他の機能を提供したい場合[こちらを参照](https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_configuration_file.html)してください。

## 実装

`implementationClass`のattributeで定義したクラスを実装しなければなりません。それぞれの機能に応じて、特定なインターフェースを実装する必要があります。

例えば`<fileType>`では`com.intellij.openapi.fileTypes.FileType`のインターフェースを実装する必要があります。gradleが用意したIntelliJのSDKにも色々なヘルパークラスがあり、`LanguageFile`のクラスを使いました。

```kotlin
class KoreanderLanguage : Language("Koreander") {
    companion object {
        val INSTANCE = KoreanderLanguage()
    }
}

class KoreanderFileType : LanguageFileType(KoreanderLanguage.INSTANCE) {
    override fun getIcon() = IconLoader.getIcon("/koreander32.png");
    override fun getName() = "Koreander"
    override fun getDefaultExtension() = "kor"
    override fun getDescription() = "Koreander template"
}
```

このコードを見るとアイコンの画像も定義ができ、ファイル一覧などで使われています。

例）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536104/ed00171c-9392-c04e-5527-e43c2ac63587.png)

シンタックスハイライター機能は`com.intellij.openapi.fileTypes.SyntaxHighlighterFactory`というabstract classの継承のよって実装できます。ここはFactory Patternでできているようです。機能によって、インターフェースの設計思想は違います。具体的な実装を紹介すると結構長くなるので省略します。

ですが、全般的な考えた方は前者の例と一緒です。[Documentation](https://www.jetbrains.org/intellij/sdk/docs/reference_guide/custom_language_support.html)を読んで、提供されているインターフェースを実装することによってIDEの拡張機能を提供します。

興味がある方は[Koreanderプラグインのソースコード](https://github.com/lukasjapan/koreander-intellij/tree/master/src/main/kotlin/de/cvguy/kotlin/koreander/intellij)をGitHubで参照してください。

# まとめ

抽象的で考えるとIntelliJのプラグインの開発は簡単です。

- gradleでヘルパーをインストール
- 提供したい機能を`plugins.xml`で定義
- 該当インターフェースを実装

それだけです。

しかし、インターフェースはやはり機能によって複雑になります。Documentationを慎重に読んで、実装するしかないです。でもそれがOSS開発の一つの楽しみであると思います。