Platform logo

JetBrains Platform

Plugin and extension development for JetBrains products.

IntelliJ Platform Plugins Qodana

Qodana erweitern: Benutzerdefinierte Codeinspektionen erstellen

Read this post in other languages:

Qodana ist ein statisches Codeanalyse-Tool, das Codeinspektionen und Quick-Fixes aus JetBrains-IDEs in Continuous-Integration-Systeme einbindet. Es kann in der Cloud oder in einem Docker-Container ausgeführt, in CI/CD-Pipelines integriert oder in einer JetBrains-IDE aufgerufen werden.

Qodana bringt von Haus aus eine beeindruckende Reihe von Inspektionen mit, ist jedoch nicht auf diese beschränkt. Sie können selbst definierte Inspektionen hinzufügen, um projektspezifische Anforderungen und Konventionen durchzusetzen.

Nehmen wir zum Beispiel an, Sie haben ein Projekt mit folgender Codekonvention:

Jede Kotlin-Klasse in einem service -Paket muss auf Service enden.

com.jetbrains.service.JetComponent würde nicht dieser Konvention entsprechen, com.jetbrains.service.BrainComponentService hingegen schon. Im Folgenden erstellen wir ein Plugin, das diese Inspektion implementiert, sodass Qodana diese Konvention in zukünftigen Projekten durchsetzen kann.

Wir können diese Codekonvention implementieren, indem wir eine benutzerdefinierte Codeinspektion erstellen, die in einem Plugin bereitgestellt wird. Qodana-Plugins werden ähnlich wie JetBrains-IDE-Plugins entwickelt, das heißt, wir müssen einfach ein IntelliJ-Plattform-Plugin erstellen, das in Qodana ausgeführt werden kann. Hier ist ein kurzer Überblick über die Schritte, die wir durchführen werden:

  1. Projekt aus dem IntelliJ Platform Plugin Template initialisieren.
  2. Die Projekteigenschaften und die Plugin-Beschreibung anpassen, zusammen mit den benötigten Abhängigkeiten.
  3. Die lokale Inspektion in der Plugin-Beschreibung deklarieren und in Kotlin implementieren.
  4. Das Plugin kompilieren und ein Paket erstellen.
  5. Im Beispielprojekt das Plugin-Artefakt in ein geeignetes Verzeichnis kopieren.
  6. Die Qodana-Konfigurationsdatei anpassen.
  7. Qodana starten und den Bericht überprüfen.

Plugin-Projekt vorbereiten

Um das Projekt zu starten, besuchen Sie das Repository IntelliJ Platform Plugin Template und klicken Sie auf die Schaltfläche Use this template, um ein Plugin-Repository zu erstellen. Geben Sie als Namen classname-inspection-qodana-plugin ein, kopieren Sie die Projekt-URL und öffnen Sie sie in IntelliJ IDEA. Wenn das Projekt einsatzbereit ist, passen Sie die Datei gradle.properties an, indem Sie pluginGroup, pluginName und pluginRepositoryUrl entsprechend deklarieren. Vergessen Sie nicht, auf die schwebende Schaltfläche Sync Gradle Changes zu klicken, um die Änderungen zu übernehmen. Um den eindeutigen Plugin-Bezeichner zu ändern, modifizieren Sie den Eintrag id in der Plugin-Beschreibungsdatei plugin.xml.

Abhängigkeiten deklarieren

Unsere Codeinspektion gilt für Kotlin-Klassen, daher müssen wir das Kotlin-Plugin zu den Abhängigkeiten des Qodana-Plugins hinzufügen. In der Datei gradle.properties müssen Sie Folgendes deklarieren:

platformBundledPlugins = org.jetbrains.kotlin

Darüber hinaus muss in den Abhängigkeiten der Plugin-Beschreibungsdatei plugin.xml dasselbe gebündelte Kotlin-Plugin enthalten sein:

org.jetbrains.kotlin

Achten Sie wieder darauf, die Gradle-Änderungen zu synchronisieren, indem Sie auf die schwebende Schaltfläche klicken.

Darüber hinaus muss die Kotlin-Klasseninspektion den Kotlin-Compiler K2 unterstützen, der seit Version 2025.1 der IntelliJ-Plattform standardmäßig aktiviert ist. Deklarieren Sie in der Plugin-Beschreibungsdatei die Erweiterung org.jetbrains.kotlin.supportsKotlinPluginMode.

    

Codeinspektion erstellen

Der tatsächliche Code für jede Codeinspektion für Kotlin-Klassen erfordert drei Schritte:

  1. Eine com.intellij.localInspection-Erweiterung in der Plugin-Beschreibungsdatei deklarieren, zusammen mit den benötigten Attributen und einem vollständig qualifizierten Verweis auf die Implementierungsklasse.
  2. Eine Implementierungsklasse erstellen – vorzugsweise in Kotlin.
  3. Eine separate HTML-Datei mit einer Beschreibung der Inspektion, der Nutzungsanleitung und einem Beispiel anlegen.

Erweiterung deklarieren

Tragen Sie die folgende Deklaration in die Plugin-Beschreibungsdatei plugin.xml ein:

    

Das Attribut language gibt an, dass die Inspektion für Kotlin-Quellcodedateien gilt. Es ist wichtig, die Inspektion explizit als standardmäßig aktiviert zu deklarieren, da Qodana sie andernfalls nicht ausführt. Geben Sie anschließend einen leicht lesbaren, aussagekräftigen Anzeigenamen unter displayName ein, der im Bericht und in den Einstellungen angezeigt werden soll. Das Attribut groupName legt die Inspektionskategorie fest, die sowohl im Qodana-Bericht als auch in den IDE-Einstellungen angezeigt wird. Geben Sie schließlich einen vollständig qualifizierten Namen für die Implementierungsklasse ein.

Quellcode der Codeinspektion

Das Kotlin-Plugin stellt eine nützliche Basis-Inspektionsklasse für Kotlin-Inspektionen bereit: AbstractKotlinInspection. Überschreiben Sie die Methode buildVisitor und stellen Sie eine PSI-Visitor-Instanz bereit, die die Kotlin-Klassenelemente auf typsichere Weise durchläuft. classVisitor ist eine praktische DSL-ähnliche Funktion, die den benötigten PSI-Visitor zurückgibt und für jede Kotlin-Klasse im analysierten Projekt aufgerufen wird.

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()
}

Die Visitor-Unterklasse extrahiert den vollständig qualifizierten Kotlin-Klassennamen, untersucht das äußerste rechte Paketelement und prüft dieses auf das entsprechende Suffix. Unzulässige Klassennamen werden an die ProblemsHolder-Instanz gemeldet, eingeschlossen in eine PSI-Elementklasse und mit einer für Menschen lesbaren Problembeschreibung.

Inspektionsbeschreibung

Für jede lokale Inspektion muss eine Beschreibungsdatei im HTML-Format bereitgestellt werden. Wenn Sie den Quick-Fix Create description file ServicePackageClassNameInspection.html anwenden, wird im richtigen Verzeichnis eine Datei mit dem Namen src/main/resources/inspectionDescriptions/ServicePackageClassName.html angelegt. Sie müssen auch eine Beschreibung bereitstellen, die im Qodana-Bericht und in den IDE-Einstellungen angezeigt wird.

Reports class names in the service packages that lack the Service suffix. Example:
  package com.example.foo.service
  class SomeComponent {
    /* class members */
  }

Plugin kompilieren

Jetzt ist alles vorbereitet – wir können die Kompilierung starten! Führen Sie den Gradle-Task buildPlugin aus und überprüfen Sie das Artefakt build/distributions/qodana-code-inspection-0.0.1.zip, das im Gradle-Ausgabeverzeichnis bereitgestellt wird. Die JAR-Datei wird als Hauptartefakt im Qodana-Scan verwendet.

Typ des Plugin-Artefakts beachten

Qodana unterstützt keine lokalen ZIP-Plugin-Artefakte, die zusätzliche JAR-Archive oder Drittanbieter-Abhängigkeiten enthalten. Jedes Plugin muss als einzelne JAR-Datei vorliegen oder in ein bestimmtes Verzeichnis entpackt werden.

Playground-Projekt mit Qodana prüfen

Nachdem wir nun Qodana mit unserem Plugin erweitert haben, erstellen wir ein Playground-Projekt in Kotlin, das wir mit Qodana analysieren können. Um das Qodana-Plugin lokal auszuführen, stellen Sie sicher, dass zwei Softwarekomponenten auf Ihrem System verfügbar sind:

Unser Playground-Projekt sollte eine Klasse namens src/main/kotlin/org/intellij/sdk/qodana/service/SomeComponent.kt enthalten, die nicht unserer Codekonvention entspricht, da sie nicht auf Service endet. Es gibt zwei Möglichkeiten, das Qodana-Plugin in Qodana einzubinden:

  • Im JetBrains Marketplace veröffentlichen.
  • Das JAR-Artefakt des Plugins in das .qodana-Verzeichnis Ihres Projekts kopieren – die schnellere Methode.

Um den Buildvorgang zu vereinfachen, kopieren Sie die Datei build/distributions/qodana-code-inspection-0.0.1.zip aus dem Plugin-Projekt in die Datei .qodana/qodana-code-inspection-0.0.1.zip im Playground-Projekt. Erstellen Sie das Verzeichnis .qodana, falls noch nicht vorhanden. Entpacken Sie anschließend das Archiv mit Ihrem bevorzugten Programm oder Tool. Qodana kann auf das Plugin im Verzeichnis build/distributions/qodana-codeinspection zugreifen.

Darüber hinaus muss Qodana so konfiguriert werden, dass es unsere benutzerdefinierte Codeinspektion verwendet. Nehmen Sie in der Datei qodana.yaml im Stammverzeichnis des Playground-Projekts die folgenden Änderungen vor:

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

Der include-Block muss auf den vollständig qualifizierten Klassennamen der vom Plugin bereitgestellten Codeinspektion verweisen. Führen Sie nun im Terminal den folgenden Befehl aus, um Qodana zu starten:

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

Dadurch wird das entsprechende Qodana-Docker-Image heruntergeladen. Ein Docker-Container wird erstellt und unter Verwendung der Qodana-Konfiguration ausgeführt. Damit das benutzerdefinierte Plugin für Qodana erreichbar ist, mounten Sie das Qodana-Plugin-Verzeichnis aus dem lokalen Dateisystem in das entsprechende Verzeichnis des Qodana-Docker-Containers. Nach einigen Minuten erstellt Qodana eine Berichtszusammenfassung und gibt diese über die Standardausgabe aus.

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

Öffnen Sie den vollständigen Bericht in Ihrem Browser:

  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 meldet, dass SomeComponent nicht mit der Codekonvention übereinstimmt, die von unserer lokalen Inspektion im Qodana-Plugin vorgegeben wurde.

Tipps für weitere Durchläufe

Wenn das Qodana-Plugin geändert und neu kompiliert wird, muss auch der Qodana-Cache neu erstellt werden. Verwenden Sie in solchen Fällen den Befehlszeilenschalter --clear-cache, um alle Abhängigkeiten des Qodana-Laufs neu zu laden.

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

Qodana in Ihre IDE integrieren

Das Qodana-Plugin kann von der Festplatte in die IDE installiert werden. Die Codeinspektion ist dann automatisch aktiviert und wird für jede Kotlin-Klasse im untersuchten Projekt aufgerufen. Um zu überprüfen, ob es funktioniert, kehren Sie zum Playground-Projekt zurück, öffnen Sie die Klasse org.intellij.sdk.qodana.service.SomeComponent und überprüfen Sie, ob der problematische Klassenname unterkringelt ist. Wenn Sie die Maus auf den Klassennamen bewegen, werden Ihnen das Ergebnis der Codeinspektion sowie eine Beschreibung des Problems angezeigt – sehr praktisch. Alternativ können Sie das Problems-Toolfenster öffnen – dort finden Sie das Problem in der Liste aller von Codeinspektionen gemeldeten Probleme.

Die Codeinspektion verhält sich nun wie jede andere von der IDE bereitgestellte Inspektion. Unter Settings | Editor | Inspections | Kotlin finden Sie die Inspektion SDK: Discouraged class name, zusammen mit der Beschreibung aus der zuvor erstellten HTML-Datei.

Qodana in der IDE ausführen

Nachdem Sie das Plugin installiert haben, können Sie Qodana auch in Ihrer IDE ausführen. Öffnen Sie im Problems-Toolfenster die Registerkarte Qodana und klicken Sie auf die Schaltfläche Try Locally. Qodana wird mit der Konfiguration aus der Datei qodana.yaml ausgeführt. Der Qodana-Bericht wird direkt im Toolfenster bereitgestellt.

Qodana-Plugins und der JetBrains Marketplace

Korrekt erstellte und getestete benutzerdefinierte Inspektions-Plugins können im JetBrains Marketplace veröffentlicht werden – dadurch entfällt die Notwendigkeit, das Plugin im .qodana-Verzeichnis bereitzustellen. Stattdessen müssen Sie lediglich sicherstellen, dass in der Qodana-Konfiguration ein öffentlicher Plugin-Bezeichner angegeben wird, der mit dem id-Element in der Plugin-Beschreibungsdatei übereinstimmt.

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

Der scan-Befehl von Qodana kann nun einfacher ausgeführt werden, da das .qodana-Verzeichnis nicht mehr gemountet werden muss.

qodana scan

Qodana lädt das Plugin vom JetBrains Marketplace herunter und führt alle darin enthaltenen Inspektionen durch. Die Ergebnisse werden sowohl in der Konsole als auch einem HTML-Bericht ausgegeben – letzteren können Sie in einem Webbrowser betrachten.

Zusammenfassung

Wir haben ein Qodana-Plugin mit einer Codeinspektion erstellt, welche die Einhaltung einer bestimmten Codekonvention prüft, und wir haben mehrere Möglichkeiten, um die Inspektion auszuführen:

  • Als JAR-Datei im Verzeichnis .qodana ablegen und in der Qodana-YAML-Datei darauf verweisen.
  • Auf ein öffentlich verfügbares Plugin im JetBrains Marketplace verweisen.
  • Als JAR-Datei in der IDE installieren, damit die Inspektion bei lokalen Qodana-Durchläufen ausgeführt wird.
  • Als JAR-Datei in der IDE installieren, damit die Inspektion zusammen mit den eingebauten Codeinspektionen ausgeführt wird.

Im IntelliJ-SDK-Beispielcode finden Sie kurze Beispiele sowohl für das Qodana-Plugin als auch für ein Playground-Projekt.

Autor des ursprünglichen Blogposts

Róbert Novotný

Róbert Novotný

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

image description