Uso de un formateador de código

Los formateadores de código existen para asegurar que el estilo del código escrito sea consistente en toda la base de código. Esto se utiliza en muchos proyectos importantes, desde Android hasta OpenCV. Los equipos pueden querer añadir un formateador en todo el código de su robot para asegurarse de que el código base mantiene la legibilidad y la coherencia en todo momento.

Para este artículo, destacaremos el uso de Spotless para equipos Java y wpiformat para equipos C++.

Spotless

Configuración

Es necesario realizar los cambios necesarios en build.gradle para que Spotless sea funcional. En el bloque plugins {} de tu build.gradle, añade el plugin Spotless para que aparezca de forma similar a la siguiente.

plugins {
   id "java"
   id "edu.wpi.first.GradleRIO" version "2024.1.1"
   id 'com.diffplug.spotless' version '6.20.0'
}

A continuación, asegúrese de añadir un bloque requerido spotless {} para configurar correctamente spotless. Esto puede colocarse al final de tu build.gradle.

spotless {
   java {
      target fileTree('.') {
            include '**/*.java'
            exclude '**/build/**', '**/build-*/**'
      }
      toggleOffOn()
      googleJavaFormat()
      removeUnusedImports()
      trimTrailingWhitespace()
      endWithNewline()
   }
   groovyGradle {
      target fileTree('.') {
            include '**/*.gradle'
            exclude '**/build/**', '**/build-*/**'
      }
      greclipse()
      indentWithSpaces(4)
      trimTrailingWhitespace()
      endWithNewline()
   }
   format 'xml', {
      target fileTree('.') {
            include '**/*.xml'
            exclude '**/build/**', '**/build-*/**'
      }
      eclipseWtp('xml')
      trimTrailingWhitespace()
      indentWithSpaces(2)
      endWithNewline()
   }
   format 'misc', {
      target fileTree('.') {
            include '**/*.md', '**/.gitignore'
            exclude '**/build/**', '**/build-*/**'
      }
      trimTrailingWhitespace()
      indentWithSpaces(2)
      endWithNewline()
   }
}

Ejecutar Spotless

Spotless puede ser ejecutado usando ./gradlew spotlessApply que aplicará todas las opciones de formato. También se puede especificar una tarea concreta añadiendo simplemente el nombre del formateador. Un ejemplo es ./gradlew spotlessmiscApply.

In addition to formatting code, Spotless can also ensure the code is correctly formatted; this can be used by running ./gradlew spotlessCheck. Thus, Spotless can be used as a CI check, as shown in the following GitHub Actions workflow:

on: [push]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  spotless:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 17
      - run: ./gradlew spotlessCheck

Explicación de las opciones

Cada sección format destaca el formato de los archivos personalizados en el proyecto. Las secciones java y groovyGradle son soportadas nativamente por spotless, por lo que se definen de forma diferente.

Desglosando esto, podemos dividirlo en varias partes.

  • Formateo de Java

  • Formateo de archivos Gradle

  • Formateo de archivos XML

  • Formateo de archivos varios

Todos son similares, excepto por algunas pequeñas diferencias que se explicarán. El siguiente ejemplo destacará el bloque java {}.

java {
   target fileTree('.') {
      include '**/*.java'
      exclude '**/build/**', '**/build-*/**'
   }
   toggleOffOn()
   googleJavaFormat()
   removeUnusedImports()
   trimTrailingWhitespace()
   endWithNewline()
}

Vamos a explicar qué significa cada una de las opciones.

target fileTree('.') {
   include '**/*.java'
   exclude '**/build/**', '**/build-*/**'
}

El ejemplo anterior le dice a spotless dónde están nuestras clases Java y que excluya el directorio build. El resto de las opciones se explican por sí mismas.

  • toggleOffOn() añade la posibilidad de que spotless ignore partes específicas de un proyecto. El uso es como el siguiente

// format:off

public void myWeirdFunction() {

}

// format:on
  • googleJavaFormat() le dice a spotless que formatee según la Guía de Estilo de Google

  • removeUnusedImports() will remove any unused imports from any of your Java classes

  • trimTrailingWhitespace() eliminará cualquier espacio en blanco extra al final de las líneas

  • endWithNewline() añadirá un carácter de nueva línea al final de tus clases

En el bloque groovyGradle, hay una opción greclipse. Este es el formateador que spotless utiliza para formatear los archivos gradle.

Además, hay una opción eclipseWtp en el bloque xml. Esto significa «Gradle Web Tools Platform» y es el formateador para formatear archivos xml. Los equipos que no utilicen ningún archivo XML pueden desear no incluir esta configuración.

Nota

Una lista completa de configuraciones está disponible en el Spotless README

Problemas con los finales de línea

Spotless intentará aplicar finales de línea por SO, lo que significa que los diffs de Git cambiarán constantemente si dos usuarios están en diferentes SOs (Unix vs Windows). Se recomienda que los equipos que contribuyen al mismo repositorio desde múltiples sistemas operativos utilicen un archivo .gitattributes. Lo siguiente debería ser suficiente para manejar los finales de línea.

*.gradle text eol=lf
*.java text eol=lf
*.md text eol=lf
*.xml text eol=lf

wpiformat

Requerimientos:

Puede instalar wpiformat escribiendo pip3 install wpiformat en un terminal o en la línea de comandos.

Uso

wpiformat se puede ejecutar escribiendo wpiformat en una consola. Esto formateará con clang-format. Se necesitan tres archivos de configuración (.clang-format, .styleguide, .styleguide-license). Estos deben existir en la raíz del proyecto.

A continuación se muestra un ejemplo de guía de estilo:

cppHeaderFileInclude {
   \.h$
   \.hpp$
   \.inc$
   \.inl$
}

cppSrcFileInclude {
   \.cpp$
}

modifiableFileExclude {
   gradle/
}

Nota

Los equipos pueden adaptar .styleguide y .styleguide-license como quieran. ¡Es importante que no se eliminen, ya que son necesarios para ejecutar wpiformat!

You can turn this into a CI check by running git --no-pager diff --exit-code HEAD, as shown in the example GitHub Actions workflow below:

name: Lint and Format

on:
  pull_request:
  push:
jobs:
  wpiformat:
    name: "wpiformat"
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Fetch all history and metadata
        run: |
          git checkout -b pr
          git branch -f main origin/main
      - name: Set up Python 3.8
        uses: actions/setup-python@v5
        with:
          python-version: 3.8
      - name: Install wpiformat
        run: pip3 install wpiformat==2024.32
      - name: Run
        run: wpiformat
      - name: Check output
        run: git --no-pager diff --exit-code HEAD