Formattage du code en Scala

Il est important dans un projet de conserver une bonne maîtrise de votre base de code. C'est en tout cas un prérequis pour faciliter la mise en place de nouvelles fonctionnalités dans une application, mais aussi la correction de bugs, le refactoring et enfin de faciliter la transmission de connaissance dans un projet. Un code bien maîtrisé nécessite un code lisible au moins par l'équipe de développement. Outre le fait que les classes, les fonctions et les variables soient bien nommées, qu'il y ait une séparation nette entre aspects techniques et aspects métiers, que le code soit correctement documenté, la façon de formater ce code participe à sa propre lisibilité.

Avec Scala, il existe différents outils permettant de formater automatiquement votre code source. Les IDE et la plupart des éditeurs de code proposent généralement une fonctionnalité de formatage. Le problème dans ce cas est de partager la configuration avec l'équipe. Ce qui vous force à vous restreindre qu'à un seul type d'éditeur et même à avoir les fichiers de configuration de l'éditeur intégré dans votre gestionnaire de version. Ainsi, votre projet se retrouve en dépendance avec l'éditeur de code ! Il vaut mieux éviter cette perspective 🧐. Sinon, il existe dans l'écosystème Scala deux outils reconnus. Il y a Scalariform et son plugin SBT, ainsi que Scalafmt qui a aussi un plugin SBT.

J'ai personnellement une préférence pour Scalafmt. Cet outil propose de décrire la configuration du formatage dans un fichier séparé, nommé .scalafmt.conf. Il est compatible avec Dotty, représentant le futur de Scala. Il possède une documentation assez bien fournie (l'équipe Scalafmt indique toutefois qu'elle n'est pas exhaustive et qu'il faut parfois fouiller dans le code de l'outil 🤓 !) Il y a aussi un plugin disponible pour IntelliJ IDEA, qui se base sur le fichier .scalafmt.conf. Enfin, Scalafmt est aussi intégré dans Metals : un "serveur de langage" assurant une intégration avancées de Scala avec éditeurs de texte sophistiqués comme Atom, VSC, Vim et Emacs.

L'intégration de Scalafmt dans les projet basé sur SBT nécessite de déclarer le plugin associé dans le fichier plugins.sbt :

addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1")

Pour que le formatage soit fait avant chaque compilation, il faut ajouter la ligne suivante dans build.sbt :

scalafmtOnCompile in ThisBuild := true

L'équipe Scalafmt déconseille cette fonctionnalité, sans forcément donner plus d'explications. Néanmoins, le formatage du code est une étape qui est coûteuse en temps sur les gros projets. De plus, lors de cette étape, vous pouvez toujours éditer le code source. Vous vous retrouvez alors avec des cas de conflits entre le reformatage fait par Scalafmt et vos modifications. Enfin, vous pouvez avoir des conflits en cas d'import de code par Git (pull, rebase, pull). Ceci dit, avec un IDE comme IntelliJ IDEA et cette option activée, vous pouvez avoir un code régulièrement reformaté sans trop de désagrément — à ce propos, il est recommandé dans sa configuration d’utiliser le formateur d'IntelliJ.

Pour avoir un formatage intéressant, vous pouvez commencer par deux lignes de configuration dans .scalafmt.conf :

align = most
maxColumn = 120

À noter que s'il est parfois considéré comme mal venu de limiter la taille des lignes dans le code, il reste néanmoins plus confortable d'avoir un code pas trop large en règle générale.

La partie alignement est l'une des fonctionnalités sur laquelle il peut y avoir le plus de discussions. La configuration align peut avoir comme valeur none, some ou default, more, most, selon le degré d'alignement que vous souhaitez dans votre code. Les valeurs none, default et some sont celles qui limiteront les surprises lors du reformatage du code et aussi les conflits dans Git. Dans la mesure où le reformatage du code est automatisé au sein de l'équipe de développement et exécuté très régulièrement (avant chaque compilation, avant chaque sauvegarde du fichier, avant chaque push sur un repo central...), vous pouvez envisager les valeurs more et most.

Scalafmt propose plusieurs options pour paramétrer l'alignement des différentes parties de votre code, l'ajout d'espaces, de sauts de ligne et d'autres règles de réécriture. Il permet de mettre en place des règles spécifiques par rapport à des symboles et même par rapport à des symboles dans des contextes particuliers au niveau de l'AST. L'exemple ci-dessous permet de réaliser un alignement par rapport au symbole => uniquement dans le cadre d'un pattern matching avec case et non dans le cadre d'une fonction.

align.tokens = [{code = "=>", owner = "Case"}]

Il est possible aussi d'activer ou de désactiver Scalafmt selon certains critères : soit pour certaines catégories de fichiers (uniquement ceux gérés par Git et/ou correspondant à un certain motif), soit sur des blocs de code en utilisant le commentaire spécial // format:.

// format: off
// this part won't modified by scalafmt
val identity = Array(1, 0, 0,
                     0, 1, 0,
                     0, 0, 1)
// format: on

Scalafmt est un outil pratique pour conserver un code Scala homogène au niveau d'un projet. Aussi, nous vous recommandons de l'intégrer (celui-ci ou tout autre outil équivalent) dans vos projet et de discuter en équipe de la configuration de cet outil.