Refactorează codul structural
Acest ghid îți arată cum să pui un agent să facă o modificare mecanică de mare amploare — un codemod, o migrare în bloc a unui API, un șablon de care vrei să scapi — folosind căutarea și rescrierea structurală, cu modificarea pregătită pentru revizuire înainte de a se scrie ceva.
ast_grep potrivește codul după formă. ast_edit îl rescrie. resolve comite sau renunță.
Înainte de a începe
Secțiune intitulată „Înainte de a începe”Instrumentele structurale acoperă limbajele pe care le analizează sintactic graful de cod: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB și Ada. Au nevoie de gramaticile tree-sitter pregătite, ceea ce se întâmplă întotdeauna într-o versiune livrată — o gramatică lipsă oprește serverul, în loc să degradeze căutarea.
Găsește mai întâi forma
Secțiune intitulată „Găsește mai întâi forma”Nu rescrie niciodată ceva ce nu ai privit. Cere-i agentului să caute:
Găsește fiecare loc în care apelăm
dispose()pe un nullable pentru care tocmai am verificat că nu este null.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")Rezultatul este file:line plus sursa potrivită pentru fiecare loc.
Ce îți oferă sintaxa șablonului
Secțiune intitulată „Ce îți oferă sintaxa șablonului”| Token | Potrivește |
|---|---|
$NAME |
Orice nod individual, capturat |
$$$NAME |
Orice serie de noduri surori — argumente, instrucțiuni |
$_ |
Orice, capturat fără nume |
Cel care contează: un $NAME repetat trebuie să capteze același text.
if ($X != null) $X.dispose()găsește o gardă care eliberează exact ce a testat. Nu găsește if (a != null) b.dispose() — care este de obicei chiar bug-ul pe care îl căutai. Nicio căutare textuală nu poate exprima asta.
Când șablonul nu găsește nimic
Secțiune intitulată „Când șablonul nu găsește nimic”Două lucruri de încercat, în ordine:
- Fă-l mai mic.
foo($X)înainte deif (a) { foo($X); }. Un șablon care nu este cod valid de unul singur nu este adesea construcția pe care o aveai în minte. - Numește limbajul.
language:este dedus numai cândpathindică un singur fișier.
Un șablon pe care nu îl acceptă niciun context de analiză este raportat ca neanalizabil — instrumentul spune asta, în loc să raporteze „nicio potrivire” pentru ceva ce nu era citit niciodată așa cum arată.
Rescrie și verifică înainte de aplicare
Secțiune intitulată „Rescrie și verifică înainte de aplicare”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Rezultatul este o propunere:
(proposed) ast_edit: if ($X != null) $X.dispose() → $X?.dispose()12 replacements in 4 files: lib/features/a/x.dart (3) lib/features/b/y.dart (5) …Nothing has been written. Call resolve with edit_id "edit_1" and action"accept" to apply it, or "discard" to drop it.Încă nu este nimic pe disc. Citește lista, verifică câteva dintre locurile raportate de căutare, apoi:
resolve(edit_id: "edit_1", action: "accept")sau
resolve(edit_id: "edit_1", action: "discard")Verifică-ți munca cu compilatorul
Secțiune intitulată „Verifică-ți munca cu compilatorul”Rostul de a face asta structural este că este larg. Modificările largi sunt exact locul unde o rescriere care pare plauzibilă strică un tip. Cere diagnostice după ce rezolvi:
lsp(action: "diagnostics", file: "*")file: "*" rulează verificatorul propriu al proiectului (dart analyze, tsc --noEmit, cargo check, go build) în locul unei interogări per fișier — forma corectă de întrebare după o modificare care a atins patruzeci de fișiere.
Exemplu lucrat: migrarea unui apel
Secțiune intitulată „Exemplu lucrat: migrarea unui apel”Să presupunem că log(msg, level) devine log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Privește potrivirile. Dacă unele dintre ele sunt un log diferit cu două argumente, restrânge șablonul limitând path la directorul care îl conține pe cel vizat, în loc să încerci să exprimi diferența în șablon.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Apoi resolve, apoi lsp(action: "diagnostics", file: "*").
Când să folosești altceva
Secțiune intitulată „Când să folosești altceva”| Situație | Instrument mai bun |
|---|---|
| Redenumirea unui simbol | lsp_rename — urmărește referințe, nu forme |
| Găsirea unui șir, a unui comentariu sau a unui nume de fișier | search (conținut) / search_files (nume) |
| „Ce se strică dacă modific asta?” | Graful de cod |
| Un limbaj pe care gramaticile nu îl acoperă | search plus edit |
- Intelligence pentru cod — de ce potrivirea structurală nu este o expresie regulată cu pași în plus
- Instrumente agent integrate — lista completă de argumente
- Caută cod cu graful de cod — partea de simboluri și de impact