Difftastic, uma ferramenta de diff estrutural que entende a sintaxe
(difftastic.wilfred.me.uk)- Difftastic é uma ferramenta CLI de diff que compara arquivos com base na estrutura sintática do código, não linha por linha
- Internamente, usa tree-sitter para fazer o parsing do código, distinguindo melhor as expressões que realmente mudaram mesmo quando a formatação altera as linhas
- Mesmo quando um formatador de código divide itens em várias linhas ou adiciona um wrapper, consegue separar e mostrar a estrutura alterada e o conteúdo interno preservado
- Há documentação de instalação e instruções de configuração para Git, com suporte a várias linguagens de programação e formatos de texto estruturado como JSON e YAML
- Disponível sob a licença MIT, pode ser baixado, modificado e compartilhado, sendo adequado para adicionar comparação estrutural a fluxos de diff existentes
Compara a estrutura sintática, não as linhas
- Difftastic é uma ferramenta CLI de diff que compara arquivos com base na sintaxe
- Seu foco é mostrar resultados que entendem a estrutura do código melhor do que um diff tradicional linha por linha
- O código é analisado com tree-sitter
- Isso permite distinguir se expressões internas que poderiam parecer alteradas em um diff centrado em linhas realmente mudaram ou não
- Mesmo quando um formatador de código divide o código em várias linhas, consegue mostrar as mudanças reais
- Quando um wrapper é adicionado, ele faz a correspondência correta dos delimitadores
- Mesmo que o conteúdo interno também mude, consegue indicar o wrapper adicionado
- Em vez da sintaxe de hunk de diff como
@@ -5,6 +5,7 @@, mostra os números de linha reais dos arquivos antes e depois da mudança
Linguagens compatíveis e integração com Git
- Os materiais do projeto estão disponíveis no GitHub, no Manual e em Install
- Exemplos de linguagens de programação compatíveis incluem:
- C++, C#, Clojure, Dart, Erlang, Go, Haskell, Java, JavaScript, Kotlin, Lisp, Lua, OCaml, PHP, Python, R, Ruby, Rust, Scala, TypeScript
- A lista completa pode ser consultada na lista de linguagens compatíveis do manual
- Exemplos de formatos de arquivo compatíveis incluem:
- HCL, HTML, JSON, YAML
- A lista completa está disponível na lista de formatos de texto estruturado do manual
- Pode ser usado com Git, e o manual traz instruções de configuração do Git
- O Difftastic é disponibilizado sob a licença MIT, permitindo download, modificação e compartilhamento
1 comentários
Comentários do Hacker News
Talvez alguns já saibam, mas esta ferramenta foi construída sobre o tree-sitter(https://tree-sitter.github.io/tree-sitter/) e, de certa forma, faz para parsing o que o LSP fez para análise
Ela converte código em uma árvore de sintaxe abstrata (AST) e fornece uma interface padrão para que clientes como editores ou ferramentas de diff possam usar essa AST
Assim, essas ferramentas não precisam oferecer suporte direto a dezenas de linguagens; basta suportar o tree-sitter, e elas passam a funcionar automaticamente com todas as linguagens que o tree-sitter suporta
Se você estiver desenvolvendo uma nova linguagem, basta criar um parser para o tree-sitter, e então todas as ferramentas que trabalham com tree-sitter poderão suportar essa linguagem
Graças a essas duas grandes inovações, as melhorias nesse tipo de ferramenta estão crescendo de forma explosiva. Agora editores, ferramentas de diff etc. conseguem suportar dezenas ou centenas de linguagens sem repetir o mesmo trabalho entre si, o que é realmente incrível
Para contextualizar, alguns membros da nossa equipe mantêm os bindings de tree-sitter para OCaml e também contribuem com frequência para gramáticas como parte do trabalho no Semgrep. O Semgrep usa tree-sitter para busca de código e para fazer parsing de consultas, que são em si trechos de código, como matchers de AST
Ao criar um linter, muitas vezes é preciso trazer junto o runtime da linguagem alvo. Por exemplo, se você construir um parser em Python usando o módulo embutido
ast, precisa alinhar versão e recursos da linguagem, então um Pylint executando em Python 2.7 não consegue fazer parsing de código Python 3Quando você lida com várias linguagens, isso fica muito mais incômodo do que parece à primeira vista
Antes do tree-sitter, muitas vezes a melhor opção era usar ferramentas de AST embutidas na própria linguagem, porque havia a garantia de acompanhar a sintaxe mais recente
Na minha opinião, a genialidade do tree-sitter está em ter tornado muito mais fácil manter parsers de linguagens atualizados do que com abordagens gramaticais tradicionais
Se quiser entender melhor as escolhas de projeto do tree-sitter, recomendo fortemente a palestra do Max Brunsfield na Strange Loop: https://www.youtube.com/watch?v=Jes3bD6P0To
Como resultado, além do difftastic, várias ferramentas novas foram construídas sobre o tree-sitter, como neovim, Zed, Semgrep e a busca de código do GitHub
Já tentei escrever uma gramática de tree-sitter para VHDL e um conjunto de queries de highlighting, e houve muitas dificuldades para expressar a gramática de VHDL no tree-sitter
O LSP realmente resolve o problema das integrações M×N necessárias entre M editores e N linguagens, usando uma interface padrão para compiladores orientados a consultas
O tree-sitter, em vez de resolver esse problema, torna muito mais fácil escrever as N integrações no editor ou na ferramenta
Common Lisp não pode ser parseado sem uma implementação completa de Lisp, a gramática de Haskell é complexa o bastante para a gramática ficar incompleta, C/C++ já são difíceis de parsear corretamente só por causa do pré-processador, e o parsing de Perl é Turing-completo
Como foi sugerido em outro lugar, em vez de fazer cada novo ecossistema reescrever parsers, talvez fosse melhor definir um formato que parsers existentes possam gerar como saída adicional
Assim talvez o difftastic pudesse mostrar mudanças por frase em vez de por linha visual
Em ensaios, diff por linha visual é estranho. Normalmente a frase alterada começa no meio de uma linha visual
Se você instalou via cargo, aqui vai uma forma de atualizar o difftastic e ferramentas relacionadas
cargo install cargo-updatecargo install-update --listcargo install-update --allTambém existem alguns projetos interessantes em Rust que podem ser instalados via cargo
https://mise.jdx.dev/ mise-en-place é uma alternativa rápida e flexível ao https://asdf-vm.com/
https://github.com/ajeetdsouza/zoxide é um excelente substituto para
cd: ele guarda os lugares por onde você passou e depois permite ir para"~/projects/helloworld"usando apenas uma correspondência parcial como"z hel"https://github.com/bootandy/dust complementa o
du, mostrando quais diretórios estão consumindo mais espaço em discolsvisualmente melhor, https://github.com/ClementTsang/bottom é umhtopcom gráficos, e https://github.com/sharkdp/bat é umcatcom destaque de sintaxelsmais bonitodu, a de que mais gosto é o dua-cli. É uma interface interativa de terminal no estilo clone doncduDescobri isso enquanto procurava uma alternativa porque o
ncdunão tem modo claroncducontinua sendo, de longe, o melhor substituto paraduQuase não consigo entender por que ferramentas como o git não oferecem isso por padrão
Uso o difft há cerca de um ano, e minha maior reclamação é que ele dificulta voltar para outras ferramentas de diff em ambientes onde o difft não existe
Fico curioso se já existe alguma pesquisa sobre ferramentas de diff semântico. Quero dizer, ferramentas que lidem com casos em que a sintaxe mudou, mas o significado continua o mesmo
Parece um problema geral difícil, mas talvez seja possível ou útil em DSLs pequenas ou em subconjuntos de algumas linguagens
diffcomo padrãoEle já está presente em praticamente qualquer sistema onde alguém vai querer rodar git, é rápido, pequeno, e todo mundo conhece o uso básico
Ainda assim, é bom que esse padrão possa ser trocado com facilidade
Não é um utilitário de linha de comando, e sim uma extensão do VS Code e um GitHub App
Se você quiser saber como funciona e qual é a diferença em relação ao difftastic, veja https://semanticdiff.com/blog/semanticdiff-vs-difftastic/
Mesmo assim, uma das razões pelas quais acho que ele não deveria ser o padrão é que ele esconde diferenças de espaço em branco. Ainda não verifiquei se isso é configurável
Concordo totalmente com a pergunta sobre saber ler a sintaxe
@@ -5,6 +5,7 @@e com a explicação de que o Difftastic mostra os números reais das linhas dos arquivos antes e depois da mudançaInstalei na hora e rodei
git diff, e funcionou muito bemO fato de o GitHub não ter lançado algo assim parece uma grande lacuna de inovação de produto
Ele não ajuda nem quando só a indentação muda, e normalmente mostra isso como uma exclusão enorme seguida de uma adição enorme
O visualizador de diff do GitHub poderia e deveria ser mais inteligente
Na prática, ele nem consegue distinguir
foo.cpp.indefoo.mk.inQualquer
foo.té classificado como Perl, e não há como corrigir isso. Existe até um ticket com 10 anosNa verdade, os próprios desenvolvedores principais do TS recomendam não se preocupar demais com a precisão da gramática[1]
Para serviços como o GitHub, ou em posição semelhante, essa limitação pode ser fatal
[1] https://github.com/tree-sitter/tree-sitter/issues/130#issuec...
Tentei mudar para essa ferramenta, mas achei ela barulhenta demais porque aplica uma formatação estranha até nas partes que não mudaram
No fim, voltei para o icdiff[0]
[0]: https://github.com/jeffkaufman/icdiff
Encontrei o Semantic Merge[1] há algum tempo, mas ele não era open source
Este aqui não faz merge, só diff, mas pelo menos é open source e o diff é muito mais agradável de ver, então já troquei meu padrão por ele
Fico curioso se há planos de expandi-lo com uma função de merge
[1] https://docs.plasticscm.com/semanticmerge
Merge de AST é um problema difícil, e o difftastic não lida com isso
Além disso, diff de AST é um processo com perdas do ponto de vista de diff textual. O difftastic ignora espaços em branco que não têm significado sintático, mas merge precisa rastrear espaços em branco
Quando eu fazia refatorações bagunçadas em projetos C# numa equipe grande, ele era realmente uma ferramenta salvadora
Discussões anteriores:
https://news.ycombinator.com/item?id=27768861 (297 points | 3 years ago | 61 comments)
https://news.ycombinator.com/item?id=32746258 (698 points | 2 years ago | 90 comments)
https://news.ycombinator.com/item?id=30841244 (983 points | 2 years ago | 219 comments)
Parece uma ótima ferramenta, mas testei com alguns arquivos XML grandes e as linhas modificadas apareciam em verde normal, enquanto os atributos modificados apareciam em verde negrito, então ficou visualmente difícil distinguir
Não consegui encontrar na documentação uma forma de mudar o estilo do diff ou definir outra cor para os casos em negrito
Queria saber se alguém conhece um jeito
Comentei nessa issue[1] dizendo que apoio esse recurso
Se o que você quer estiver na mesma categoria, pode acrescentar sua opinião lá; se achar que é algo suficientemente diferente, talvez valha abrir uma nova issue
[1]: https://github.com/Wilfred/difftastic/issues/611
É interessante que o pacote do Arch Linux tem 7MB, mas depois da instalação se expande para 80MB, e só o binário
difftjá tem 78MBEm um dataset ZFS com compressão LZ4 ativada, dizem que o
dumostra 17MBFico curioso por que algo que comprime tão bem dentro do binário não é simplesmente mantido comprimido. Parece até que, ao descomprimir na RAM, poderia carregar mais 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 76MO Tree-sitter parece precisar de uma quantidade enorme de código, e boa parte disso parece redundante ou compressível
Ele é mapeado em memória e só as partes realmente usadas são carregadas quando necessário
Por isso, comprimir um binário grande acaba desativando a paginação sob demanda e pode até deixá-lo mais lento
Alguns anos atrás, testei isso com um EXE de 280MB no Windows; ele realmente comprimia para algo em torno de 70MB, mas demorava alguns segundos a mais para iniciar do que o original
Em alguns cenários, como executar o binário pela rede, isso pode fazer sentido, mas na maioria dos casos um binário sem compressão inicia mais rápido