3 points par GN⁺ 2024-03-22 | 1 commentaires | Partager sur WhatsApp
  • Difftastic est un outil CLI de diff qui compare les fichiers non pas ligne par ligne, mais selon la structure syntaxique du code
  • En interne, il analyse le code avec tree-sitter, ce qui permet de mieux distinguer les expressions réellement modifiées même lorsque des lignes changent à cause du formatage
  • Même lorsqu’un formateur de code répartit des éléments sur plusieurs lignes ou qu’un wrapper est ajouté, il peut séparer les changements de structure du contenu interne inchangé
  • Une documentation d’installation et de configuration Git est fournie, et l’outil prend en charge de nombreux langages de programmation ainsi que des formats de texte structurés comme JSON et YAML
  • Publié sous licence MIT, il peut être téléchargé, modifié et partagé, et convient bien pour ajouter une comparaison structurelle à un flux de travail diff existant

Comparer la structure syntaxique plutôt que les lignes

  • Difftastic est un outil CLI de diff qui compare les fichiers selon leur syntaxe
  • Il vise à montrer des résultats qui comprennent la structure du code, plutôt qu’un diff classique ligne par ligne
  • Le code est analysé avec tree-sitter
    • Même des expressions internes qui peuvent sembler modifiées dans un diff centré sur les lignes peuvent être distinguées selon qu’elles ont réellement changé ou non
  • Même lorsqu’un formateur de code répartit le code sur plusieurs lignes, il peut montrer les changements réels
  • Lorsqu’un wrapper est ajouté, il fait correspondre les délimiteurs avec précision
    • Même si le contenu interne change aussi, il peut indiquer le wrapper ajouté
  • Au lieu d’une syntaxe de hunk diff comme @@ -5,6 +5,7 @@, il affiche les vrais numéros de ligne des fichiers avant et après modification

Langages pris en charge et intégration avec Git

  • Les ressources du projet sont disponibles sur GitHub, Manual et Install
  • Exemples de langages de programmation pris en charge :
    • C++, C#, Clojure, Dart, Erlang, Go, Haskell, Java, JavaScript, Kotlin, Lisp, Lua, OCaml, PHP, Python, R, Ruby, Rust, Scala, TypeScript
    • La liste complète est disponible dans le manuel, à la page Supported Languages
  • Exemples de formats de fichiers pris en charge :
  • Il peut être utilisé avec Git, et le manuel propose un guide de configuration Git
  • Difftastic est publié sous licence MIT, ce qui permet son téléchargement, sa modification et son partage

1 commentaires

 
GN⁺ 2024-03-22
Avis Hacker News
  • Certains le savent peut-être déjà, mais cet outil est construit sur tree-sitter(https://tree-sitter.github.io/tree-sitter/) ; en quelque sorte, il fait pour le parsing ce que LSP a fait pour l’analyse
    Il transforme le code en arbre de syntaxe abstraite (AST) et fournit une interface standard permettant à des clients comme des éditeurs ou des outils de diff d’utiliser cet AST
    Sans avoir à prendre directement en charge des dizaines de langages, ce type d’outil peut simplement prendre en charge tree-sitter, et fonctionner alors automatiquement avec tous les langages pris en charge par tree-sitter
    Si vous développez un nouveau langage, il suffit de créer un parseur tree-sitter, et tous les outils qui savent manipuler tree-sitter pourront alors prendre en charge ce langage
    Grâce à ces deux grandes innovations, les améliorations de ce type d’outil explosent. Désormais, éditeurs, outils de diff, etc. peuvent prendre en charge des dizaines, voire des centaines de langages sans répéter le même travail chacun de leur côté, et c’est vraiment impressionnant

    • Je suis entièrement d’accord et, pour reprendre un commentaire que j’avais écrit l’an dernier, il faut aussi souligner que tree-sitter a une architecture sans dépendances
      Pour contexte, plusieurs membres de notre équipe maintiennent les bindings OCaml de tree-sitter et contribuent souvent aux grammaires dans le cadre de notre travail sur Semgrep. Semgrep utilise tree-sitter pour la recherche de code et pour parser en matchers AST des requêtes qui sont elles-mêmes des fragments de code
      Quand on écrit un linter, il faut souvent embarquer aussi le runtime du langage ciblé. Par exemple, si l’on construit un parseur avec le module intégré ast de Python, il faut faire correspondre la version du langage et ses fonctionnalités ; ainsi, Pylint exécuté sous Python 2.7 ne peut pas parser du code Python 3
      Dès qu’on cible plusieurs langages, cela devient beaucoup plus pénible qu’on ne l’imagine au départ
      Avant tree-sitter, le mieux était souvent d’utiliser les outils AST intégrés au langage, parce qu’ils garantissaient de suivre la syntaxe la plus récente
      À titre personnel, le génie de tree-sitter tient au fait qu’il a rendu beaucoup plus facile le maintien à jour des parseurs de langage que les approches grammaticales traditionnelles
      Si vous voulez en savoir plus sur les choix de conception de tree-sitter, je recommande vivement la présentation Strange Loop de Max Brunsfield : https://www.youtube.com/watch?v=Jes3bD6P0To
      Résultat : au-delà de difftastic, de nombreux nouveaux outils comme neovim, Zed, Semgrep ou la recherche de code GitHub ont été construits sur tree-sitter
    • Je suis d’accord pour dire que tree-sitter est un excellent outil, mais écrire une grammaire soi-même peut être extrêmement difficile
      J’ai écrit une grammaire tree-sitter et un ensemble de requêtes de coloration pour VHDL, et représenter la grammaire VHDL avec tree-sitter m’a posé beaucoup de difficultés
    • Je pense que cette explication n’est pas exacte. Il n’existe pas d’interface commune du type parler tree-sitter, et chaque parseur tree-sitter produit un arbre de syntaxe concret différent, pas un arbre de syntaxe abstraite standard
      LSP résout réellement le problème des M×N intégrations nécessaires entre M éditeurs et N langages en utilisant une interface standard pour des compilateurs orientés requêtes
      tree-sitter ne résout pas vraiment ce problème ; il rend plutôt beaucoup plus facile l’écriture de N intégrations dans un éditeur ou un outil
    • Ce qui me gêne le plus avec tree-sitter, c’est que cette approche ne fonctionne pas pour plusieurs langages qui m’intéressent
      Common Lisp ne peut pas être parsé sans une implémentation Lisp complète, la syntaxe de Haskell est suffisamment complexe pour que la grammaire soit incomplète, C/C++ est difficile à parser correctement ne serait-ce qu’à cause du préprocesseur, et le parsing de Perl est Turing-complet
      Comme cela a été suggéré ailleurs, plutôt que de forcer la réécriture des parseurs dans un nouvel écosystème, il me semblerait plus juste de définir un format que les parseurs existants pourraient produire comme sortie auxiliaire
    • Serait-il possible de créer une grammaire tree-sitter pour le langage naturel, comme l’anglais, afin d’étiqueter chaque phrase comme une unité de phrase ?
      Difftastic pourrait alors afficher les changements par phrase plutôt que par ligne visuelle
      Dans un essai, un diff basé sur les lignes visuelles est étrange. En général, la phrase modifiée commence au milieu d’une ligne visuelle
  • Si vous l’avez installé avec cargo, voici comment mettre à jour difftastic et les outils associés
    cargo install cargo-update
    cargo install-update --list
    cargo install-update --all
    Il existe aussi des projets Rust intéressants installables avec cargo
    https://mise.jdx.dev/ mise-en-place est une alternative rapide et flexible à https://asdf-vm.com/
    https://github.com/ajeetdsouza/zoxide est un excellent remplaçant de cd, qui mémorise les emplacements visités et permet d’aller vers "~/projects/helloworld" avec une correspondance partielle comme "z hel"
    https://github.com/bootandy/dust est un outil qui complète du et montre quels répertoires consomment beaucoup d’espace disque

    • Pour ajouter trois outils propres : https://github.com/eza-community/eza est un ls visuellement plus agréable, https://github.com/ClementTsang/bottom est un htop avec des graphes, et https://github.com/sharkdp/bat est un cat avec coloration syntaxique
    • J’aime beaucoup zoxide. lsd mérite aussi d’être ajouté à la liste : c’est un ls plus joli
    • J’ai essayé d’installer mise-en-place, et c’est exactement ce que j’aurais voulu qu’asdf devienne
    • Parmi les nouveaux outils du genre du, dua-cli est celui que je préfère. C’est une interface terminal interactive qui clone ncdu
      Je l’ai découvert en cherchant une alternative parce que ncdu n’a pas de mode clair
    • ncdu reste de loin le meilleur remplaçant de du
  • J’ai du mal à comprendre pourquoi des outils comme git ne proposent pas ça par défaut
    J’utilise difft depuis environ un an, et mon principal reproche est qu’il rend difficile le retour à d’autres outils de diff dans les environnements où difft n’est pas disponible
    Je me demande s’il existe aussi des recherches sur des outils de diff sémantique. Par exemple des outils qui gèrent les cas où la syntaxe a changé mais où le sens reste le même
    Comme problème général, ça paraît ardu, mais pour de petits DSL ou des sous-ensembles de certains langages, ça pourrait être possible ou utile

    • Je pense que l’ancien diff est le bon choix par défaut
      Il est déjà présent sur presque tous les systèmes où l’on voudra exécuter git, il est rapide, petit, et tout le monde connaît son usage de base
      Cela dit, c’est bien de pouvoir changer facilement cette valeur par défaut
    • Je travaille sur https://semanticdiff.com/, qui détecte des changements sémantiques de base, comme remplacer des littéraux décimaux par de l’hexadécimal ou changer l’ordre des clés dans un objet JSON
      Ce n’est pas un utilitaire en ligne de commande, mais une extension VS Code et une GitHub App
      Si vous voulez savoir comment ça fonctionne et en quoi ça diffère de difftastic, voyez https://semanticdiff.com/blog/semanticdiff-vs-difftastic/
    • Difftastic est un outil utile, mais d’après mon expérience il est trop lent pour devenir le choix par défaut d’un outil aussi largement utilisé que git
    • Pour faire ce genre de chose, l’outil de diff devient en fait un compilateur
    • Je suis en train d’essayer difft avec git, et ça me plaît pas mal
      Mais l’une des raisons pour lesquelles je pense que ça ne devrait pas devenir la valeur par défaut, c’est que ça masque les différences d’espaces. Je n’ai pas encore vérifié si c’est configurable
  • Je suis tout à fait d’accord avec l’explication qui demande si l’on sait lire la syntaxe @@ -5,6 +5,7 @@, et qui dit que Difftastic affiche les vrais numéros de ligne des fichiers avant et après modification
    Je viens de l’installer et de lancer git diff, et ça marche très bien

    • On ne sait pas lire cette syntaxe ?
  • Le fait que GitHub n’ait pas sorti quelque chose comme ça ressemble à un énorme manque d’innovation produit
    Il n’y a même pas de fonctionnalité pour aider quand seule l’indentation change, et c’est généralement affiché comme d’énormes suppressions et ajouts
    Le visualiseur de diff de GitHub pourrait, et devrait, être plus intelligent

    • GitHub ne gère même pas correctement la sensibilité à la syntaxe, alors un diff sémantique…
      En pratique, il ne sait même pas distinguer foo.cpp.in de foo.mk.in
      N’importe quel foo.t est déclaré comme du Perl, sans moyen de le corriger. Il existe même un ticket vieux de 10 ans
    • tree-sitter est optimisé non pas pour l’exactitude, mais pour la performance dans les éditeurs
      En fait, même les développeurs principaux de TS recommandent de ne pas trop se soucier de l’exactitude des grammaires[1]
      Pour GitHub ou des services dans une position similaire, cette contrainte peut sembler rédhibitoire
      [1] https://github.com/tree-sitter/tree-sitter/issues/130#issuec...
    • GitHub propose une option pour ignorer les espaces dans les diffs
  • J’ai essayé de passer à cet outil, mais il appliquait une mise en forme étrange même aux parties inchangées, ce qui m’a paru beaucoup trop bruyant
    Je suis finalement revenu à icdiff[0]
    [0]: https://github.com/jeffkaufman/icdiff

  • J’avais découvert Semantic Merge[1] il y a quelque temps, mais ce n’était pas open source
    Celui-ci ne fait pas de fusion, seulement du diff, mais au moins il est open source et le diff est bien plus lisible, donc je l’ai déjà mis par défaut
    Je me demande s’il est prévu de l’étendre avec des fonctionnalités de fusion
    [1] https://docs.plasticscm.com/semanticmerge

    • D’après le README GitHub, difftastic ne fait pas de fusion
      La fusion d’AST est un problème difficile, et difftastic ne le traite pas
      De plus, le diff d’AST est un processus avec perte du point de vue du diff textuel. Difftastic ignore les espaces qui n’ont pas de signification syntaxique, mais la fusion nécessite de suivre les espaces
    • J’allais moi aussi recommander Semantic Merge
      À l’époque où je faisais du refactoring désordonné sur un projet C# dans une grande équipe, c’était vraiment un outil salvateur
  • Discussions précédentes :
    https://news.ycombinator.com/item?id=27768861 (297 points | il y a 3 ans | 61 commentaires)
    https://news.ycombinator.com/item?id=32746258 (698 points | il y a 2 ans | 90 commentaires)
    https://news.ycombinator.com/item?id=30841244 (983 points | il y a 2 ans | 219 commentaires)

  • Ça a l’air d’être un excellent outil, mais après l’avoir testé sur quelques gros fichiers XML, les lignes modifiées s’affichaient en vert normal et les attributs modifiés en vert gras, ce qui était difficile à distinguer visuellement
    Je n’ai pas trouvé dans la documentation comment changer le style du diff ou attribuer une autre couleur aux éléments en gras
    Je me demande si quelqu’un sait comment faire

    • Malheureusement, il semble que la personnalisation des couleurs ne soit pas encore prise en charge
      J’ai commenté cette issue[1] pour dire que je soutenais cette fonctionnalité
      Si la fonctionnalité que vous voulez relève de la même catégorie, vous pouvez y ajouter votre avis ; si vous pensez qu’elle est suffisamment différente, vous pouvez aussi ouvrir une nouvelle issue
      [1]: https://github.com/Wilfred/difftastic/issues/611
  • Il est intéressant de voir que le paquet Arch Linux fait 7 Mo, mais se décompresse à 80 Mo après installation, et que le binaire difft à lui seul fait 78 Mo
    Sur un dataset ZFS avec la compression LZ4 activée, du indique 17 Mo
    Je me demande pourquoi quelque chose qui se compresse aussi bien à l’intérieur du binaire n’est pas simplement gardé compressé. Décompressé en RAM, il pourrait même sembler se charger plus vite

    • Comme la question de la compression a été évoquée, j’ai vérifié par curiosité : avec btrfs zstd:1, on est autour de 10 Mo
      Processed 1 file, 614 regular extents (614 refs), 0 inline.
      Type Perc Disk Usage Uncompressed Referenced
      TOTAL 14% 10M 77M 77M
      none 100% 1.1M 1.1M 1.1M
      zstd 12% 9.8M 76M 76M
    • C’est probablement lié au commentaire disant que le parseur Nim est un fichier C de 60 Mo impossible à fusionner
      Tree-sitter semble nécessiter énormément de code, dont une bonne partie paraît redondante ou compressible
    • Un exécutable n’est pas chargé entièrement en RAM avant de démarrer
      Il est mappé en mémoire, et seules les parties réellement utilisées sont chargées à la demande
      Donc, si l’on compresse un gros binaire, la pagination à la demande ne fonctionne plus, ce qui le rend au contraire plus lent
      J’avais testé il y a quelques années avec un EXE Windows de 280 Mo : il se compressait bien à environ 70 Mo, mais mettait plusieurs secondes de plus à démarrer que l’original
      Cela peut avoir du sens dans certains scénarios, par exemple lorsqu’on exécute un binaire à travers le réseau, mais dans la plupart des cas un binaire non compressé démarre plus vite