Difftastic, una herramienta de análisis de diferencias estructurales que entiende la sintaxis
(difftastic.wilfred.me.uk)- Difftastic es una herramienta CLI de diff que compara archivos no por líneas, sino según la estructura sintáctica del código
- Internamente analiza el código con tree-sitter, por lo que incluso si el formateo cambia las líneas, distingue mejor qué expresiones realmente cambiaron
- Incluso cuando un formateador divide elementos en varias líneas o se agrega un wrapper, puede mostrar por separado la estructura modificada y el contenido interno que se mantiene
- Ofrece guía de configuración para Git y documentación de instalación, y soporta varios lenguajes de programación y formatos de texto estructurado como JSON y YAML
- Se publica bajo licencia MIT, por lo que puede descargarse, modificarse y compartirse, y es ideal para agregar comparación estructural a flujos de diff existentes
Compara la estructura sintáctica, no las líneas
- Difftastic es una herramienta CLI de diff que compara archivos con base en la sintaxis
- Se enfoca en mostrar resultados que entienden la estructura del código, en lugar de un diff tradicional por líneas
- El código se analiza con tree-sitter
- Incluso expresiones internas que en un diff centrado en líneas podrían parecer modificadas pueden distinguirse según si realmente cambiaron o no
- Incluso cuando un formateador de código divide el código en varias líneas, puede mostrar los cambios reales
- Si se agrega un wrapper, empareja los delimitadores con precisión
- Incluso si el contenido interno también cambia, puede señalar el wrapper agregado
- En lugar de usar la sintaxis de hunk de diff como
@@ -5,6 +5,7 @@, muestra los números de línea reales de los archivos antes y después del cambio
Lenguajes compatibles e integración con Git
- Los recursos del proyecto están disponibles en GitHub, Manual e Install
- Algunos ejemplos de lenguajes de programación compatibles son:
- C++, C#, Clojure, Dart, Erlang, Go, Haskell, Java, JavaScript, Kotlin, Lisp, Lua, OCaml, PHP, Python, R, Ruby, Rust, Scala, TypeScript
- La lista completa puede consultarse en la lista de lenguajes compatibles del manual
- Algunos ejemplos de formatos de archivo compatibles son:
- HCL, HTML, JSON, YAML
- La lista completa puede verse en la lista de formatos de texto estructurado del manual
- Puede usarse junto con Git, y el manual incluye una guía de configuración de Git
- Difftastic se publica bajo licencia MIT, por lo que puede descargarse, modificarse y compartirse
1 comentarios
Comentarios de Hacker News
Quizá algunos ya lo sepan, pero esta herramienta está construida sobre tree-sitter(https://tree-sitter.github.io/tree-sitter/), y hace para el parsing algo parecido a lo que LSP hizo por el análisis
Convierte el código en un árbol de sintaxis abstracta (AST) y proporciona una interfaz estándar para que clientes como editores o herramientas de diff puedan usar ese AST
Herramientas como esta no necesitan dar soporte directo a decenas de lenguajes; con soportar tree-sitter pueden funcionar automáticamente con todos los lenguajes que tree-sitter soporte
Si desarrollas un lenguaje nuevo, basta con crear un parser de tree-sitter, y entonces todas las herramientas que usan tree-sitter podrán soportar ese lenguaje
Gracias a estas dos grandes innovaciones, las mejoras en este tipo de herramientas se han disparado. Ahora editores, herramientas de diff y demás pueden soportar decenas o cientos de lenguajes sin repetir el mismo trabajo entre sí, lo cual es realmente impresionante
Como referencia, algunos compañeros de mi equipo mantienen los bindings de tree-sitter para OCaml y también contribuyen con frecuencia a las gramáticas como parte del trabajo en Semgrep. Semgrep usa tree-sitter para la búsqueda de código y para parsear consultas, que son fragmentos de código en sí mismos, como matchers de AST
Al crear un linter, muchas veces hay que llevarse también el runtime del lenguaje objetivo. Por ejemplo, si construyes un parser en Python con el módulo
astintegrado, tienes que hacer coincidir la versión y las características del lenguaje, así que Pylint ejecutándose en Python 2.7 no puede parsear código Python 3Cuando apuntas a varios lenguajes, eso se vuelve mucho más molesto de lo que uno imagina al principio
Antes de tree-sitter, a menudo lo mejor era usar las herramientas AST integradas del lenguaje, porque al menos había garantía de seguir la sintaxis más reciente
Personalmente, creo que la genialidad de tree-sitter está en que hizo mucho más fácil mantener actualizados los parsers de los lenguajes que con los enfoques gramaticales tradicionales
Si quieren conocer más sobre las decisiones de diseño de tree-sitter, recomiendo muchísimo la charla de Strange Loop de Max Brunsfield: https://www.youtube.com/watch?v=Jes3bD6P0To
Como resultado, además de difftastic, varias herramientas nuevas como neovim, Zed, Semgrep y la búsqueda de código de GitHub se han construido sobre tree-sitter
Intenté escribir una gramática de tree-sitter y un conjunto de consultas de resaltado para VHDL, y hubo muchas dificultades para expresar la gramática de VHDL en tree-sitter
LSP sí resuelve de verdad el problema de las M×N integraciones necesarias entre M editores y N lenguajes usando una interfaz estándar para compiladores orientados por consultas
tree-sitter, más que resolver ese problema, hace mucho más fácil escribir esas N integraciones desde el editor o la herramienta
Common Lisp no se puede parsear sin una implementación completa de Lisp, la gramática de Haskell es lo bastante compleja como para que la gramática quede incompleta, C/C++ ya es difícil de parsear correctamente solo por el preprocesador, y parsear Perl es turing-completo
En vez de hacer que en un ecosistema nuevo se reescriban parsers, como sugieren algunas propuestas por ahí, me parece más correcto definir un formato que los parsers existentes puedan generar como salida adicional
Así difftastic podría mostrar cambios por oración en lugar de por línea visual
En ensayos, hacer diff por líneas visuales se siente raro. Normalmente la oración cambiada empieza a mitad de una línea visual
Si lo instalaste con cargo, así puedes actualizar difftastic y herramientas relacionadas
cargo install cargo-updatecargo install-update --listcargo install-update --allTambién hay proyectos interesantes en Rust que se pueden instalar con cargo
https://mise.jdx.dev/ mise-en-place es una alternativa rápida y flexible a https://asdf-vm.com/
https://github.com/ajeetdsouza/zoxide es un excelente reemplazo de
cd: guarda los lugares a los que te moviste y luego puede llevarte a"~/projects/helloworld"con solo una coincidencia parcial como"z hel"https://github.com/bootandy/dust complementa a
dumostrando qué directorios están usando mucho espacio en discolsvisualmente más agradable, https://github.com/ClementTsang/bottom es unhtopcon gráficos, y https://github.com/sharkdp/bat es uncatcon resaltado de sintaxislsmás bonitodu, dua-cli es el que más me gusta. Tiene una UI interactiva de terminal estilo clon dencduLo encontré porque estaba buscando una opción con modo claro, ya que
ncduno lo tienencdues por mucho el mejor reemplazo deduCasi no entiendo por qué herramientas como git no ofrecen algo así por defecto
Llevo como un año usando difft, y mi mayor queja es que hace difícil volver a otras herramientas de diff cuando estás en un entorno donde difft no está disponible
Me pregunto si también se ha investigado alguna herramienta de diff semántico. O sea, una que maneje casos donde cambió la sintaxis pero el significado sigue siendo el mismo
Como problema general suena intratable, pero quizá podría ser posible o útil en DSL pequeños o en subconjuntos de algunos lenguajes
diffcomo valor predeterminadoYa está presente en casi cualquier sistema donde quieras ejecutar git, es rápido, pequeño y todo el mundo conoce su uso básico
Aun así, está bien que ese valor predeterminado se pueda cambiar fácilmente
No es una utilidad de línea de comandos, sino una extensión de VS Code y una GitHub App
Si te interesa cómo funciona y en qué se diferencia de difftastic, puedes ver https://semanticdiff.com/blog/semanticdiff-vs-difftastic/
Aun así, una de las razones por las que no creo que deba ser el valor predeterminado es que oculta las diferencias de espacios en blanco. Todavía no he revisado si eso se puede configurar
Me identifiqué totalmente con la pregunta de si sabes leer la sintaxis
@@ -5,6 +5,7 @@, y con la explicación de que Difftastic muestra los números de línea reales de los archivos antes y después del cambioLo instalé de inmediato e hice
git diff; funciona muy bienQue GitHub no haya sacado algo así parece un gran vacío de innovación de producto
Ni siquiera ayuda cuando solo cambia la indentación, y normalmente lo muestra como una eliminación enorme y una adición enorme
El visor de diff de GitHub podría ser más inteligente y debería serlo
De hecho, ni puede distinguir que
foo.cpp.inyfoo.mk.inson distintosCualquier
foo.tse declara como Perl y no hay forma de corregirlo. Hay un ticket de hace 10 añosDe hecho, los propios desarrolladores principales de TS recomiendan no preocuparse demasiado por la exactitud de la gramática[1]
Parece que esta limitación podría ser fatal para GitHub o servicios en una posición similar
[1] https://github.com/tree-sitter/tree-sitter/issues/130#issuec...
Intenté cambiarme a esta herramienta, pero usa un formato raro incluso en partes que no cambiaron, así que se sintió demasiado ruidosa
Al final regresé a icdiff[0]
[0]: https://github.com/jeffkaufman/icdiff
Hace tiempo encontré Semantic Merge[1], pero no era open source
Este no hace merge, solo diff, pero al menos es open source y el diff se ve mucho mejor, así que ya lo puse como valor predeterminado
Me pregunto si hay planes de extenderlo con una función de merge
[1] https://docs.plasticscm.com/semanticmerge
Dice que el merge de AST es un problema difícil y que difftastic no lo aborda
Además, el diff de AST es un proceso con pérdida desde la perspectiva del diff de texto. difftastic ignora espacios en blanco que no tienen significado sintáctico, pero para hacer merge sí necesitas rastrear esos espacios
Cuando trabajaba en refactorizaciones desordenadas de proyectos C# en un equipo grande, de verdad fue una herramienta salvadora
Debates anteriores:
https://news.ycombinator.com/item?id=27768861 (297 points | hace 3 años | 61 comments)
https://news.ycombinator.com/item?id=32746258 (698 points | hace 2 años | 90 comments)
https://news.ycombinator.com/item?id=30841244 (983 points | hace 2 años | 219 comments)
Parece una gran herramienta, pero al probarla con algunos archivos XML grandes, las líneas modificadas se mostraban en verde normal y los atributos modificados en verde en negritas, así que visualmente era difícil distinguirlos
No encontré en la documentación cómo cambiar el estilo del diff ni cómo asignar otro color a los casos en negritas
Me pregunto si alguien sabe cómo hacerlo
Dejé un comentario en este issue[1] diciendo que apoyo esa función
Si lo que quieres entra en la misma categoría, podrías sumar tu opinión ahí; y si crees que es una función suficientemente distinta, quizá valga la pena abrir un issue nuevo
[1]: https://github.com/Wilfred/difftastic/issues/611
Es interesante que el paquete de Arch Linux pesa 7 MB, pero después de instalarse se expande a 80 MB, y solo el binario
difftya ocupa 78 MBEn un dataset de ZFS con compresión LZ4 activada,
dudice que son 17 MBMe pregunto por qué algo que se comprime tan bien dentro del binario no se deja simplemente comprimido. Parece que si se descomprime en RAM hasta podría cargar más rápido
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 76MParece que Tree-sitter necesita una cantidad enorme de código, y una buena parte de eso se ve redundante o comprimible
Se mapean en memoria y solo se cargan las partes que realmente se usan cuando hacen falta
Por eso, si comprimes un binario grande, la paginación por demanda deja de funcionar y en realidad se vuelve más lento
Hace unos años hice una prueba con un EXE de Windows de 280 MB, y aunque sí se comprimía a unos 70 MB, tardaba varios segundos más en iniciar que el original
En algunos escenarios, como ejecutar binarios a través de la red, puede tener sentido, pero en la mayoría de los casos un binario sin comprimir arranca más rápido