Difftastic, ein strukturelles Diff-Tool, das Syntax versteht
(difftastic.wilfred.me.uk)- Difftastic ist ein CLI-Diff-Tool, das Dateien nicht zeilenweise, sondern anhand der Syntaxstruktur von Code vergleicht
- Intern wird Code mit tree-sitter geparst, sodass tatsächlich geänderte Ausdrücke besser erkannt werden, auch wenn sich Zeilen nur durch Formatierung verschoben haben
- Auch wenn ein Code-Formatter Elemente auf mehrere Zeilen aufteilt oder ein Wrapper hinzugefügt wurde, kann es geänderte Strukturen und unveränderte innere Inhalte getrennt darstellen
- Anleitungen zur Git-Konfiguration und Installationsdokumentation sind verfügbar; unterstützt werden viele Programmiersprachen sowie strukturierte Textformate wie JSON und YAML
- Unter der MIT-Lizenz veröffentlicht, kann es heruntergeladen, verändert und geteilt werden und eignet sich dazu, bestehende Diff-Workflows um strukturelle Vergleiche zu ergänzen
Vergleicht Syntaxstrukturen statt Zeilen
- Difftastic ist ein CLI-Diff-Tool, das Dateien anhand der Syntax vergleicht
- Der Fokus liegt darauf, Ergebnisse zu zeigen, die die Codestruktur besser verstehen als ein gewöhnlicher zeilenbasierter Diff
- Code wird mit tree-sitter geparst
- Auch innere Ausdrücke, die in einem zeilenorientierten Diff wie geändert aussehen könnten, lassen sich daraufhin unterscheiden, ob sie tatsächlich geändert wurden
- Selbst wenn ein Code-Formatter Code auf mehrere Zeilen aufteilt, kann es die tatsächlichen Änderungen anzeigen
- Wird ein Wrapper hinzugefügt, ordnet es die Trennzeichen korrekt zu
- Selbst wenn sich zugleich der innere Inhalt ändert, kann der hinzugefügte Wrapper angezeigt werden
- Statt der Diff-Hunk-Syntax wie
@@ -5,6 +5,7 @@zeigt es die tatsächlichen Zeilennummern der Dateien vor und nach der Änderung
Unterstützte Sprachen und Git-Integration
- Projektmaterialien sind auf GitHub, im Manual und unter Install verfügbar
- Beispiele für unterstützte Programmiersprachen sind:
- C++, C#, Clojure, Dart, Erlang, Go, Haskell, Java, JavaScript, Kotlin, Lisp, Lua, OCaml, PHP, Python, R, Ruby, Rust, Scala, TypeScript
- Die vollständige Liste findet sich in der Liste der unterstützten Sprachen im Manual
- Beispiele für unterstützte Dateiformate sind:
- HCL, HTML, JSON, YAML
- Die vollständige Liste ist in der Liste strukturierter Textformate im Manual zu finden
- Es kann zusammen mit Git verwendet werden; das Manual enthält eine Anleitung zur Git-Konfiguration
- Difftastic ist unter der MIT-Lizenz veröffentlicht und kann heruntergeladen, verändert und geteilt werden
1 Kommentare
Meinungen auf Hacker News
Manche wissen es vielleicht schon, aber dieses Tool ist auf tree-sitter(https://tree-sitter.github.io/tree-sitter/) aufgebaut und leistet fürs Parsing gewissermaßen das, was LSP für die Analyse geleistet hat.
Es wandelt Code in einen abstrakten Syntaxbaum (AST) um und stellt eine Standardschnittstelle bereit, über die Clients wie Editoren oder Diff-Tools diesen AST nutzen können.
Solche Tools müssen nicht Dutzende Sprachen direkt unterstützen; es reicht, tree-sitter zu unterstützen, und sie funktionieren automatisch mit allen Sprachen, die tree-sitter unterstützt.
Wenn man eine neue Sprache entwickelt, erstellt man einen tree-sitter-Parser, und damit können alle Tools, die mit tree-sitter umgehen, diese Sprache unterstützen.
Dank dieser beiden großen Innovationen explodieren die Verbesserungen solcher Tools geradezu. Editoren, Diff-Tools usw. können nun Dutzende bis Hunderte Sprachen unterstützen, ohne dass alle dieselbe Arbeit doppelt machen müssen – wirklich großartig.
Zur Einordnung: Einige Leute aus unserem Team pflegen die OCaml-Bindings für tree-sitter und tragen im Rahmen der Arbeit an Semgrep auch häufig zu Grammatiken bei. Semgrep verwendet tree-sitter für Code-Suche und um Queries, die selbst Codefragmente sind, in AST-Matcher zu parsen.
Wenn man einen Linter baut, muss man oft auch die Runtime der Zielsprache mitbringen. Wenn man zum Beispiel in Python mit dem eingebauten Modul
asteinen Parser baut, muss man Sprachversion und Features aufeinander abstimmen; daher kann ein unter Python 2.7 laufendes Pylint keinen Python-3-Code parsen.Wenn man mehrere Sprachen unterstützen will, wird das viel lästiger, als man zunächst denkt.
Vor tree-sitter war es oft am besten, die in die Sprache eingebauten AST-Tools zu verwenden, weil damit am ehesten gewährleistet war, dass sie mit der neuesten Syntax Schritt halten.
Für mich liegt die Genialität von tree-sitter darin, dass es viel einfacher geworden ist, Sprachparser aktuell zu halten als mit traditionellen Grammatikansätzen.
Wer mehr über die Designentscheidungen von tree-sitter wissen möchte, dem empfehle ich den Strange-Loop-Vortrag von Max Brunsfield sehr: https://www.youtube.com/watch?v=Jes3bD6P0To
In der Folge wurden neben difftastic auch mehrere neue Tools wie neovim, Zed, Semgrep und die Code-Suche von GitHub auf tree-sitter aufgebaut.
Ich habe eine tree-sitter-Grammatik und ein Set von Highlighting-Queries für VHDL geschrieben, und es gab viele Schwierigkeiten, die VHDL-Grammatik in tree-sitter auszudrücken.
LSP löst mit einer Standardschnittstelle für query-orientierte Compiler tatsächlich das M×N-Integrationsproblem zwischen M Editoren und N Sprachen.
tree-sitter löst dieses Problem nicht wirklich, sondern macht es für Editoren oder Tools deutlich einfacher, N Integrationen zu schreiben.
Common Lisp lässt sich ohne vollständige Lisp-Implementierung nicht parsen, die Haskell-Grammatik ist so komplex, dass die Grammatik unvollständig ist, C/C++ ist schon wegen des Präprozessors schwer korrekt zu parsen, und Perl-Parsing ist turingvollständig.
Wie andernorts vorgeschlagen wurde, scheint es sinnvoller, ein Format zu definieren, das bestehende Parser als zusätzliche Ausgabe erzeugen können, statt Parser in einem neuen Ökosystem neu schreiben zu lassen.
Dann könnte difftastic Änderungen nach Sätzen statt nach visuellen Zeilen anzeigen.
Bei Essays sind Diffs nach visuellen Zeilen seltsam, weil geänderte Sätze meist mitten in einer visuellen Zeile beginnen.
Wenn man es über cargo installiert hat, aktualisiert man difftastic und verwandte Tools so:
cargo install cargo-updatecargo install-update --listcargo install-update --allEs gibt auch interessante Rust-Projekte, die sich über cargo installieren lassen.
https://mise.jdx.dev/ mise-en-place ist eine schnelle und flexible Alternative zu https://asdf-vm.com/.
https://github.com/ajeetdsouza/zoxide ist ein großartiger Ersatz für
cd: Es speichert Orte, zu denen man gewechselt ist, und kann dann mit einer Teilübereinstimmung wie"z hel"nach"~/projects/helloworld"wechseln.https://github.com/bootandy/dust ist ein ergänzendes Tool zu
duund zeigt, welche Verzeichnisse viel Speicherplatz belegen.ls, https://github.com/ClementTsang/bottom ist einhtopmit Diagrammen, und https://github.com/sharkdp/bat ist eincatmit Syntaxhervorhebung.ls.du-artigen Tools gefällt mir dua-cli am besten. Es ist eine interaktive Terminal-UI, ein Klon vonncdu.Ich habe es gefunden, als ich nach etwas gesucht habe, weil
ncdukeinen hellen Modus hat.ncduist mit Abstand der beste Ersatz fürdu.Ich verstehe kaum, warum Tools wie git so etwas nicht standardmäßig anbieten
Ich nutze difft seit etwa einem Jahr, und mein größter Kritikpunkt ist, dass es schwerfällt, in Umgebungen ohne difft wieder zu anderen diff-Tools zurückzukehren
Ich frage mich, ob es auch Forschung zu semantikbasierten diff-Tools gibt. Also etwa zu Tools, die Fälle behandeln, in denen sich die Syntax geändert hat, die Semantik aber gleich geblieben ist
Als allgemeines Problem klingt das schwierig, aber bei kleinen DSLs oder Teilmengen mancher Sprachen könnte es möglich oder nützlich sein
diffzu verwendenEs ist auf fast jedem System, auf dem man git ausführen will, bereits vorhanden, schnell, klein, und jeder kennt die grundlegende Bedienung
Trotzdem ist es gut, dass man diesen Standard leicht ändern kann
Es ist kein Kommandozeilen-Utility, sondern eine VS-Code-Erweiterung und eine GitHub App
Wenn dich interessiert, wie es funktioniert und worin es sich von difftastic unterscheidet, siehe https://semanticdiff.com/blog/semanticdiff-vs-difftastic/
Einer der Gründe, warum es meiner Meinung nach trotzdem nicht der Standard werden sollte, ist, dass es Unterschiede bei Whitespace verbirgt. Ob das konfigurierbar ist, habe ich noch nicht geprüft
Ich stimme der Erklärung völlig zu, dass gefragt wird, ob man die Syntax
@@ -5,6 +5,7 @@lesen kann, und dass Difftastic die tatsächlichen Zeilennummern der Dateien vor und nach der Änderung zeigtIch habe es direkt eingebunden und
git diffausprobiert; es funktioniert sehr gutDass GitHub so etwas nicht anbietet, wirkt wie eine große Lücke bei der Produktinnovation
Selbst wenn sich nur die Einrückung ändert, gibt es keine hilfreiche Funktion, und meist wird es als riesiges Löschen und Hinzufügen angezeigt
GitHubs diff-Viewer kann und sollte intelligenter sein
Es kann tatsächlich nicht einmal unterscheiden, dass
foo.cpp.inundfoo.mk.inverschieden sindJedes
foo.twird als Perl deklariert, ohne Möglichkeit, das zu korrigieren. Dazu gibt es sogar ein 10 Jahre altes TicketTatsächlich empfehlen selbst die TS-Kernentwickler, sich nicht zu sehr um die Genauigkeit der Grammatik zu kümmern[1]
Für GitHub oder Dienste in ähnlicher Position könnte diese Einschränkung fatal sein
[1] https://github.com/tree-sitter/tree-sitter/issues/130#issuec...
Ich wollte auf dieses Tool umsteigen, aber es verwendete auch für unveränderte Teile eine merkwürdige Formatierung, sodass es sich viel zu unruhig anfühlte
Am Ende bin ich wieder zu icdiff[0] zurückgekehrt
[0]: https://github.com/jeffkaufman/icdiff
Ich hatte früher Semantic Merge[1] entdeckt, aber es war nicht Open Source
Dieses Tool macht zwar kein Merging, sondern nur diffs, ist aber immerhin Open Source, und die diffs sind viel besser lesbar, daher habe ich es bereits als Standard gesetzt
Ich frage mich, ob es Pläne gibt, es um Merge-Funktionalität zu erweitern
[1] https://docs.plasticscm.com/semanticmerge
AST-Merging ist ein schwieriges Problem, und difftastic behandelt es nicht
Außerdem ist ein AST-diff aus Sicht eines Text-diffs ein verlustbehafteter Prozess. Difftastic ignoriert syntaktisch bedeutungslosen Whitespace, aber für Merging muss Whitespace nachverfolgt werden
Als ich in einem großen Team chaotische Refactorings an einem C#-Projekt gemacht habe, war es wirklich ein Retter in der Not
Frühere Diskussionen:
https://news.ycombinator.com/item?id=27768861 (297 points | vor 3 Jahren | 61 Kommentare)
https://news.ycombinator.com/item?id=32746258 (698 points | vor 2 Jahren | 90 Kommentare)
https://news.ycombinator.com/item?id=30841244 (983 points | vor 2 Jahren | 219 Kommentare)
Es wirkt wie ein hervorragendes Tool, aber als ich es mit ein paar großen XML-Dateien getestet habe, wurden geänderte Zeilen in normalem Grün und geänderte Attribute in fettem Grün angezeigt, was visuell schwer zu unterscheiden war
In der Dokumentation habe ich keine Möglichkeit gefunden, den diff-Stil zu ändern oder für fette Hervorhebungen eine andere Farbe festzulegen
Ich frage mich, ob jemand weiß, wie das geht
Ich habe in diesem Issue[1] kommentiert, dass ich diese Funktion unterstütze
Wenn das, was du möchtest, in dieselbe Kategorie fällt, kannst du dort deine Meinung ergänzen; wenn du es für ausreichend anders hältst, könntest du auch ein neues Issue eröffnen
[1]: https://github.com/Wilfred/difftastic/issues/611
Interessant ist, dass das Arch-Linux-Paket 7 MB groß ist, nach der Installation aber auf 80 MB entpackt wird, und allein das
difft-Binary 78 MB ausmacht.Auf einem ZFS-Dataset mit aktivierter LZ4-Kompression soll
du17 MB anzeigen.Ich frage mich, warum man etwas, das sich im Binary so gut komprimieren lässt, nicht einfach komprimiert vorhält. Wenn es im RAM entpackt wird, könnte es scheinbar sogar schneller laden.
Processed 1 file, 614 regular extents (614 refs), 0 inline.Type Perc Disk Usage Uncompressed ReferencedTOTAL 14% 10M 77M 77Mnone 100% 1.1M 1.1M 1.1Mzstd 12% 9.8M 76M 76MTree-sitter scheint enorm viel Code zu benötigen, und ein beträchtlicher Teil davon wirkt redundant oder komprimierbar.
Sie wird speicherabgebildet und nur die tatsächlich verwendeten Teile werden bei Bedarf geladen.
Wenn man ein großes Binary komprimiert, funktioniert Demand Paging deshalb nicht, was es eher langsamer macht.
Ich habe das vor ein paar Jahren mit einer 280-MB-Windows-EXE getestet: Sie ließ sich zwar auf etwa 70 MB komprimieren, brauchte beim Start aber einige Sekunden länger als das Original.
In manchen Szenarien, etwa wenn man ein Binary über das Netzwerk ausführt, kann das sinnvoll sein, aber meistens starten unkomprimierte Binaries schneller.