Platform logo

JetBrains Platform

Plugin and extension development for JetBrains products.

IntelliJ Platform Plugins Qodana

Qodana の機能拡張: カスタムコードインスペクションを追加する

Read this post in other languages:

Qodana は JetBrains IDE のコードインスペクションとクイックフィックスを継続的インテグレーションの領域に取り込む静的コード解析ツールです。クラウドで実行することも、Docker コンテナーから実行することも、CI/CD パイプラインに組み込むことも、JetBrains IDE を介して呼び出すことも可能です。

Qodana はすでに優れたインスペクション一式を提供していますが、組み込みのインスペクションに限定されることはありません。カスタムインスペクションを追加することで、プロジェクトの仕様や規則を適用することができます。

たとえば、以下のような特定のコード規則を使用するプロジェクトを想像してください。

service パッケージ内の各 Kotlin クラスには Service 接尾辞を必ず使用する。

この場合、com.jetbrains.service.JetComponent はこの規則に準拠しませんが、com.jetbrains.service.BrainComponentService はまったく問題ありません。以下では、このインスペクションを実装するプラグインをビルドします。これにより、Qodana がこの規則を今後のプロジェクトで適用できるようになります。

このコード規則は、カスタムコードインスペクションを作成して plugin にパッケージ化することで実装できます。Qodana プラグインの開発は JetBrains IDE プラグインとまったく同じですが、Qodana で実行できる IntelliJ Platform プラグインを作成する必要があります。以下はその手順の概要です。

  1. IntelliJ Platform Plugin Template からプロジェクトを初期化します。
  2. プロジェクトプロパティとプラグイン記述子、および必要な依存関係を調整します。
  3. プラグイン記述子でローカルインスペクションを宣言し、Kotlin で実装します。
  4. プラグインをビルドし、パッケージ化します。
  5. プラグインアーティファクトをサンプルプレイグラウンドプロジェクトの適切なディレクトリに配置します。
  6. Qodana の構成ファイルを調整します。
  7. Qodana を実行してレポートを確認しましょう!

プラグインプロジェクトの準備

プロジェクトをブートストラップするには、IntelliJ Platform Plugin Template リポジトリにアクセスし、Use this template(このテンプレートを使用)ボタンをクリックしてプラグインリポジトリを作成します。それを classname-inspection-qodana-plugin と名付け、プロジェクトの URL をコピーし、IntelliJ IDEA で開きます。プロジェクトの準備が完了したら、必要に応じて pluginGrouppluginName、および pluginRepositoryUrl を宣言して gradle.properties をカスタマイズします。変更を適用するため、Sync Gradle Changes(Gradle の変更を同期) フローティングボタンを必ずクリックしましょう。一意のプラグイン識別子を変更するには、プラグイン記述子である plugin.xmlid 要素を変更します。

依存関係を宣言する

このコードインスペクションの対象は Kotlin クラスであるため、Kotlin プラグインを Qodana プラグインの依存関係に追加する必要があります。gradle.properties ファイルで以下を宣言する必要があります。

platformBundledPlugins = org.jetbrains.kotlin

また、以下のようにプラグイン記述子 plugin.xml の依存関係に同じバンドルの Kotlin プラグインを含める必要があります。

org.jetbrains.kotlin

ここでも必ずフローティングボタンをクリックし、Gradle の変更を同期しましょう。

さらに、Kotlin クラスインスペクションは IntelliJ Platform バージョン 2025.1 以降にデフォルトで有効化されるようになった Kotlin K2 コンパイラーをサポートする必要があります。プラグイン記述子で org.jetbrains.kotlin.supportsKotlinPluginMode 拡張機能を宣言してください。

    

コードインスペクションを作成する

Kotlin クラスを対象とするコードインスペクションの実際のコードには、以下の 3 つの手順が必要です。

  1. プラグイン記述子で com.intellij.localInspection 拡張機能を宣言し、必要な属性と実装クラスへの完全修飾参照を指定します。
  2. できれば Kotlin で実装クラスを作成します。
  3. インスペクションの説明、使用方法のガイドライン、およびサンプルを含むスタンドアロンの HTML ファイルを提供します。

拡張機能を宣言する

以下の宣言を plugin.xml プラグイン記述子ファイルに追加します。

    

language 属性は、インスペクションが Kotlin ソースコードファイルに適用されることを示します。インスペクションはデフォルトで明示的に有効化することが重要です。そうでない場合、Qodana はインスペクションを実行しません。次に、レポートと設定に表示される displayName を人間が読み取れる分かりやすい文字列で指定します。groupName 属性には、Qodana のレポートと IDE の設定の両方に表示されるインスペクションカテゴリを設定します。最後に、実装クラスの完全修飾名を指定します。

コードインスペクションのソースコード

Kotlin プラグインには、AbstractKotlinInspection という Kotlin インスペクション用の便利な基本インスペクションクラスが用意されています。buildVisitor メソッドをオーバーライドし、型安全な方法で Kotlin クラス要素を全探索する PSI ビジターインスタンスを指定してください。classVisitor はこの種の PSI ビジターを返し、検査対象プロジェクトの任意の Kotlin クラスで呼び出される DSL のような便利な関数です。

package org.intellij.sdk.codeInspection

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.classVisitor

class ServicePackageClassNameInspection : AbstractKotlinInspection() {
    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = classVisitor { klass ->
        val classNamePsi = klass.nameIdentifier ?: return@classVisitor
        val classFqn = klass.fqName?.asString() ?: return@classVisitor
        if (klass.packageLastComponent == "service" && !classFqn.endsWith("Service")) {
            holder.registerProblem(
                classNamePsi,
                "Class name in the 'service' package must have a 'Service' suffix"
            )
        }
    }

    private val KtClass.packageLastComponent: String
        get() = containingKtFile.packageFqName.shortName().asString()
}

ビジターサブクラスは完全修飾 Kotlin クラス名を抽出し、最も右側のパッケージ要素を検査し、対応する接尾辞を確認します。不適切なクラス名は、PSI 要素としての外側のクラスと人間が読み取れる問題の説明とともに ProblemsHolder インスタンスに報告されます。

インスペクションの説明

各ローカルインスペクションには対応する HTML 形式の記述ファイルが必要です。Create description file ServicePackageClassNameInspection.html(記述ファイル ServicePackageClassNameInspection.html の作成)クイックフィックスを使用すると、適切な場所に src/main/resources/inspectionDescriptions/ServicePackageClassName.html というファイルが作成されます。また、Qodana のレポートと IDE の設定に表示される説明の指定も必要です。

Reports class names in the service packages that lack the Service suffix.

Example:

  package com.example.foo.service
  class SomeComponent {
    /* class members */
  }

プラグインのビルド

準備が完了したら、ビルドに着手しましょう! buildPlugin Gradle タスクを実行し、Gradle の出力ディレクトリにある build/distributions/qodana-code-inspection-0.0.1.zip アーティファクトを確認します。JAR ファイルは Qodana のスキャンで主なアーティファクトとして使用されます。

プラグインのアーティファクトタイプに留意する

Qodana は付加的な JAR アーカイブやサードパーティ依存関係を含むローカルの ZIP プラグインアーティファクトを直接サポートしていません。すべてのプラグインは 1 つの JAR にパッケージ化されているか、特定のディレクトリに解凍されている必要があります。

プレイグラウンドプロジェクトでの Qodana の実行

Kotlin で記述し、プラグインで拡張した Qodana で検査できるプライグラウンドプロジェクトを作成しましょう。Qodana プラグインをローカルで実行するには、システムに以下の 2 つのソフトウェアコンポーネントが必要です。

このプレイグラウンドプロジェクトには src/main/kotlin/org/intellij/sdk/qodana/service/SomeComponent.kt というクラスを含めるべきですが、このクラスは Service 接尾辞がないため、コード規則に従っていません。Qodana プラグインを Qodana に組み込む方法には以下の 2 つがあります。

  • JetBrains Marketplace に公開する。
  • さらに迅速な解決策として、プラグインの JAR アーティファクトをプロジェクトの .qodana ディレクトリに配置する。

ビルド作業を単純化するため、プラグインプロジェクトの build/distributions/qodana-code-inspection-0.0.1.zip ファイルをプレイグラウンドプロジェクトの .qodana/qodana-code-inspection-0.0.1.zip ファイルにコピーしてください。.qodana ディレクトリがない場合は作成しましょう。次に、任意のプログラムかツールでアーカイブを抽出します。Qodana は build/distributions/qodana-code-inspection ディレクトリのプラグインにアクセスできます。

また、カスタムコードインスペクションを含めるように Qodana を構成する必要があります。プレイグラウンドプロジェクトのルートディレクトリの qodana.yaml ファイルを以下のように変更してください。

version: "1.0"
linter: qodana-jvm-community
include:
  - name: org.intellij.sdk.codeInspection.ServicePackageClassNameInspection

include ブロックは、プラグインで使用できるコードインスペクションの完全修飾クラス名を参照する必要があります。ターミナルで以下のコマンドを実行して Qodana を実行しましょう。

qodana scan --volume $PWD/.qodana/qodana-code-inspection:/opt/idea/custom-plugins/qodana-code-inspection

すると、対応する Qodana の Docker イメージがダウンロードされます。Docker コンテナーが作成され、Qodana の構成に基づいて実行されます。Qodana の実行環境内でカスタムプラグインにアクセスできるようにするため、Qodana プラグインをローカルファイルシステムから Qodana の Docker コンテナー内の適切なディレクトリに直接マウントしてください。数分後に Qodana がレポートの要約を生成し、標準出力に出力します。

Qodana - Detailed summary                                                                                                                                                                                                                
Analysis results: 1 problem detected                                                                                                                                                                                                     
By severity: High - 1                                                                                                                                                                                                                    
-------------------------------------------------------                                                                                                                              
Name                           Severity  Problems count                                                                                                                                                             
-------------------------------------------------------                                                                                                                              
SDK: Discouraged class name    High      1                                                                                                                                                             
-------------------------------------------------------

完全なレポートをブラウザーで開いてください。

  Do you want to open the latest report [Y/n]Yes

!  Press Ctrl+C to stop serving the report

  Showing Qodana report from http://localhost:8080/... (10s)

Qodana により、SomeComponent が Qodana プラグインのローカルインスペクションから指定されたコード規則に従っていないことが示されます。

以降の実行のためのヒント

Qodana プラグインが変更されて再ビルドされる際には、Qodana のキャッシュも再作成されるようにする必要があります。そのような場合は、--clear-cache CLI スイッチを使用することで、Qodana 実行環境のすべての依存関係を再読み込みできます。

qodana scan --clear-cache --volume $PWD/.qodana/qodana-code-inspection-0.0.1.jar:/opt/idea/custom-plugins/codeinspection.jar

IDE への Qodana の組み込み

Qodana プラグインは IDE に対してディスクからインストールできます。すると、プラグインのコードインスペクションが自動的に有効化され、プロジェクト内の任意の Kotlin クラスで呼び出されるようになります。プラグインの動作を確認するため、プレイグラウンドプロジェクトに戻って org.intellij.sdk.qodana.service.SomeComponent クラスを開き、問題のあるクラス名に下線が付くことを確認してください。利便性を考慮し、クラス名にマウスポインターを合わせた際にはコードインスペクションの結果と問題の説明が表示されます。Problems(問題)ツールウィンドウを開き、コードインスペクションによって報告されたすべての問題のリストで問題を確認することもできます。

このコードインスペクションが IDE が提供する他のインスペクションと同様に動作するようになりました。Settings(設定)| Editor (エディター)| Inspections(インスペクション)| Kotlin には、SDK: Discouraged class name インスペクションに加えて、前に指定した HTML ファイルから取得された説明が表示されます。

IDE 内で Qodana を実行する

プラグインがインストールされたことで、IDE からも Qodana を実行できるようになりました。Problems(問題)ツールウィンドウの Qodana タブに移動し、Try Locally(ローカルで試す)ボタンをクリックしてください。Qodana が qodana.yaml ファイルを使用して構成され、実行されます。Qodana のレポートはツールウィンドウ内で直接確認できます。

Qodana プラグインと JetBrains Marketplace

ビルドとテストが適切に行われたカスタムインスペクションプラグインを JetBrains Marketplace で公開すると、.qodana ディレクトリから配布する必要がなくなります。ただし、Qodana の構成がプラグイン記述子の id 要素に一致する公開プラグイン識別子を確実に指定する必要があります。

version: "1.0"
linter: qodana-jvm-community
plugins: 
  - id: com.github.novotnyr.qodanacodeinspection

Qodana の scan コマンドは、.qodana ディレクトリのマウントが不要になったため、単純化されています。

qodana scan

Qodana はこのプラグインを JetBrains Marketplace からダウンロードし、そのすべてのインスペクションを実行し、コンソールの出力とウェブブラウザーで表示可能な HTML レポートの両方を生成します。

まとめ

特定のコード規則を確認するコードインスペクションを含む Qodana プラグインを作成しました。また、それを実行する方法は以下のようにいくつか存在します。

  • .qodana ディレクトリに JAR として配置し、Qodana YAML ファイルにインクルードする形で実行する。
  • JetBrains Marketplace の公開プラグインへの参照として実行する。
  • IDE に JAR としてインストールして実行する(インスペクションをローカルの Qodana の実行に適用させる)。
  • IDE に JAR としてインストールして実行する(インスペクションを組み込みのコードインスペクションにインクルードさせる)。

Qodana プラグインとプレイグラウンドプロジェクトの簡潔な例については、IntelliJ SDK のコードサンプルをご覧ください。

オリジナル(英語)ブログ投稿記事の作者:

Róbert Novotný

Róbert Novotný

Róbert Novotný is a Developer Advocate, specializing in the IntelliJ Platform and plugin development.

image description