Platform logo

JetBrains Platform

Plugin and extension development for JetBrains products.

IntelliJ Platform Plugins Qodana

Ampliar la funcionalidad de Qodana con inspecciones personalizadas de código

Read this post in other languages:

Qodana es una herramienta de análisis estático de código que lleva las inspecciones de código y los arreglos rápidos de los JetBrains IDEs al ámbito de la integración continua. Puede ejecutarse en la nube, ejecutarse desde un contenedor Docker, integrarse en procesos de CI/CD o invocarse a través de un JetBrains IDE.

Qodana ya ofrece un impresionante conjunto de inspecciones, pero no se limita a lo que lleva incorporado. Puede añadir inspecciones personalizadas para hacer cumplir las especificaciones y convenciones del proyecto.

Por ejemplo, imagine un proyecto con una convención de código específica:

Cada clase Kotlin de un paquete service debe tener un sufijo Service.

En este caso, com.jetbrains.service.JetComponent no se ajustaría a esta convención, mientras que com.jetbrains.service.BrainComponentService sería perfectamente adecuada. Para continuar, crearemos un complemento que implemente esta inspección, para que Qodana pueda hacer cumplir esta convención en futuros proyectos.

Podemos implementar esta convención de código creando una inspección de código personalizada empaquetada en un complemento. Los complementos de Qodana se desarrollan igual que los de los JetBrains IDEs, es decir, solo tenemos que crear un complemento de IntelliJ Platform que se pueda ejecutar en Qodana. He aquí un rápido resumen de los pasos que seguiremos:

  1. Inicialice el proyecto desde IntelliJ Platform Plugin Template.
  2. Ajuste las propiedades del proyecto y el descriptor del complemento junto con las dependencias necesarias.
  3. Declare la inspección local en el descriptor del complemento e impleméntelo en Kotlin.
  4. Compile y empaquete el complemento.
  5. En el proyecto de prueba de ejemplo, sitúe el artefacto del complemento en un directorio correcto.
  6. Ajuste el archivo de configuración de Qodana.
  7. Ejecute Qodana y mire el informe.

Preparación del proyecto del complemento

Para arrancar el proyecto, visite el repositorio IntelliJ Platform Plugin Template y haga clic en el botón Use this template para crear un repositorio de complementos. Llámelo classname-inspection-qodana-plugin, copie la URL del proyecto y ábralo en IntelliJ IDEA. Cuando el proyecto esté listo, personalice gradle.properties declarando pluginGroup, pluginName y pluginRepositoryUrl según sea necesario. No se olvide de pulsar el botón flotante Sync Gradle Changes para aplicar los cambios. Para modificar el identificador único del complemento, cambie el elemento id en el descriptor del complemento plugin.xml.

Declarar dependencias

Nuestra inspección de código tiene como objetivo las clases Kotlin, por lo que necesitamos añadir el complemento de Kotlin a las dependencias del complemento de Qodana. El archivo gradle.properties requiere que declare:

platformBundledPlugins = org.jetbrains.kotlin

Además, el descriptor del complemento plugin.xml debe contener el mismo complemento Kotlin empaquetado en sus dependencias:

org.jetbrains.kotlin

De nuevo, recuerde sincronizar los cambios de Gradle pulsando el botón flotante.

Además, la inspección de clases de Kotlin debe ser compatible con el compilador K2 de Kotlin, habilitado de forma predeterminada desde la versión 2025.1 de IntelliJ Platform. En el descriptor del complemento, declare la extensión org.jetbrains.kotlin.supportsKotlinPluginMode.

    

Creación de la inspección de código

El código real para cualquier inspección de código que se dirija a clases Kotlin requiere tres pasos:

  1. Declare una extensión com.intellij.localInspection en el descriptor del complemento junto con los atributos necesarios y una referencia totalmente cualificada a la clase de implementación.
  2. Cree una clase de implementación, preferiblemente en Kotlin.
  3. Proporcione un archivo HTML independiente con una descripción de la inspección, directrices de uso y un ejemplo.

Declarar la extensión

Añada la siguiente declaración al archivo descriptor del complemento plugin.xml:

    

El atributo language indica que la inspección se aplica a los archivos de código fuente en Kotlin. Es importante activar explícitamente la inspección de forma predeterminada; de lo contrario, Qodana no la ejecutará. A continuación, proporcione un displayName descriptivo, legible para el usuario, que se mostrará en el informe y en la configuración. El atributo groupName establece la categoría de inspección que se muestra tanto en el informe de Qodana como en la configuración del IDE. Por último, proporcione un nombre completamente cualificado para la clase de implementación.

Código fuente de inspección del código

El complemento de Kotlin proporciona una clase de inspección base muy útil para las inspecciones de Kotlin: AbstractKotlinInspection. Anule el método buildVisitor y proporcione una instancia de visitante PSI que recorra los elementos de clase de Kotlin de forma segura en cuanto a tipos. classVisitor es una práctica función similar a la de DSL que devuelve este tipo de visitante PSI y se invoca sobre cualquier clase Kotlin del proyecto que esté inspeccionando.

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

La subclase del visitante extrae el nombre de clase de Kotlin totalmente cualificado, inspecciona el elemento del paquete situado más a la derecha y comprueba el sufijo correspondiente. Cualquier nombre de clase incorrecto se notifica a la instancia ProblemsHolder con una clase envolvente como elemento PSI y una descripción del problema legible para el usuario.

Descripción de la inspección

Cada inspección local requiere un archivo de descripción complementario, representado como HTML. Si utiliza el arreglo rápido Create description file ServicePackageClassNameInspection.html, se creará un archivo llamado src/main/resources/inspectionDescriptions/ServicePackageClassName.html en la ubicación adecuada. También tendrá que proporcionar una descripción que se mostrará en el informe de Qodana y en los ajustes del IDE.

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

Example:

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

Compile el complemento

Ya está todo listo: ¡es hora de compilar! Ejecute la tarea buildPlugin de Gradle y busque el artefacto build/distributions/qodana-code-inspection-0.0.1.zip disponible en el directorio de salida de Gradle. El archivo JAR se utilizará como artefacto principal en el análisis de Qodana. 

Tenga en cuenta el tipo de artefacto del complemento

Qodana no admite directamente artefactos de complementos ZIP locales que incluyan archivos JAR adicionales o dependencias de terceros. Cualquier complemento se debe empaquetar como un único JAR o descomprimir en un directorio específico.

Ejecutar Qodana en un proyecto de prueba

Vamos a crear un proyecto de prueba, escrito en Kotlin, que podamos inspeccionar con Qodana ahora que lo hemos extendido con nuestro complemento. Para ejecutar el complemento de Qodana de forma local, asegúrese de que los siguientes dos componentes de software estén disponibles en su sistema:

Nuestro proyecto de prueba debería contener una clase llamada src/main/kotlin/org/intellij/sdk/qodana/service/SomeComponent.kt, que no sigue nuestra convención de código, ya que no tiene el sufijo Service. Hay dos formas de integrar el complemento de Qodana en Qodana:

  • Publíquelo en JetBrains Marketplace
  • Para una implementación más rápida, coloque el artefacto JAR del complemento en el directorio .qodana de su proyecto.

Para simplificar la compilación, copie el archivo build/distributions/qodana-code-inspection-0.0.1.zip del proyecto del complemento en el archivo .qodana/qodana-code-inspection-0.0.1.zip del proyecto de prueba. Cree el directorio .qodana, si aún no existe. A continuación, extraiga el archivo con el programa o herramienta que prefiera. Qodana puede acceder al complemento en el directorio build/distributions/qodana-code-inspection.

Además, es necesario configurar Qodana para incluir nuestra inspección de código personalizada. Modifique el archivo qodana.yaml en el directorio raíz del proyecto de prueba como se indica a continuación:

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

El bloque include debe hacer referencia al nombre de clase totalmente cualificado de la inspección de código disponible en el complemento. Ahora ejecute el siguiente comando para ejecutar Qodana desde el terminal:

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

Esto descargará la imagen de Docker de Qodana correspondiente. Se creará y ejecutará un contenedor de Docker basado en la configuración de Qodana. Para que el complemento personalizado sea accesible dentro de la ejecución de Qodana, monte el directorio del complemento de Qodana desde el sistema de archivos local al directorio apropiado dentro del contenedor de Docker de Qodana. Tras un par de minutos, Qodana elaborará un resumen del informe y lo imprimirá en el resultado estándar.

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

Abra el informe completo en su navegador:

  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 mostrará que SomeComponent no cumple la convención de código proporcionada por nuestra inspección local en el complemento de Qodana.

Consejos para futuras ejecuciones

Cuando el complemento de Qodana se modifica y se reconstruye, la caché de Qodana también debe volver a crearse. En tales casos, utilice el parámetro de CLI --clear-cache para recargar todas las dependencias de la ejecución de Qodana.

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

Integrar Qodana en su IDE

El complemento de Qodana puede instalarse desde el disco en el IDE. A continuación, su inspección de código se activa e invoca automáticamente para cualquier clase de Kotlin de un proyecto. Para comprobar que funciona, vuelva al proyecto de prueba, abra la clase org.intellij.sdk.qodana.service.SomeComponent y asegúrese de que el nombre de la clase con problemas aparece subrayado. Para mayor comodidad, al pasar el ratón sobre el nombre de la clase se mostrará el resultado de la inspección del código junto con la descripción del problema. Alternativamente, puede abrir la ventana de herramientas Problems y buscar el problema en la lista de todos los problemas notificados por las inspecciones de código. 

La inspección de código se comporta ahora como cualquier otra inspección proporcionada por el IDE. En Settings | Editor | Inspections | Kotlin, encontrará la inspección SDK: Discouraged class name, junto con la descripción obtenida del archivo HTML que le proporcionamos antes.

Ejecutar Qodana dentro del IDE

Con el complemento instalado, ahora también puede ejecutar Qodana desde su IDE. En la ventana de herramientas Problems, vaya a la pestaña Qodana y pulse el botón Try Locally. Qodana se configurará con el archivo qodana.yaml y se ejecutará. El informe de Qodana se encuentra directamente en la ventana de herramientas. 

Complementos de Qodana y JetBrains Marketplace

Los complementos de inspección personalizados correctamente compilados y probados pueden publicarse en JetBrains Marketplace, para que no sea necesario servirlos desde el directorio .qodana. En su lugar, solo tiene que asegurarse de que la configuración de Qodana especifique un identificador de complemento público que coincida con el elemento id del descriptor del complemento.

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

El comando scan de Qodana está simplificado, ya que el montaje del directorio .qodana ya no es necesario.

qodana scan

Qodana descarga este complemento desde JetBrains Marketplace y ejecuta todas sus inspecciones, produciendo tanto un resultado de consola como un informe HTML que puede mostrarse en un navegador web.

Resumen

Hemos creado un complemento de Qodana con una inspección de código que comprueba una convención de código específica, y tenemos varias formas de ejecutarlo:

  • Como un JAR colocado en el directorio .qodana e incluido en el archivo YAML de Qodana.
  • Como referencia al complemento disponible públicamente en JetBrains Marketplace.
  • Como un JAR instalado en el IDE, donde la inspección se aplica en una ejecución local de Qodana.
  • Como un JAR instalado en el IDE, donde la inspección se incluye entre las inspecciones de código integradas. 

Consulte el el ejemplo de código del IntelliJ SDK para ver demostraciones concisas tanto del complemento de Qodana como de un proyecto de prueba.

Artículo original en inglés de:

Róbert Novotný

Róbert Novotný

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

image description