Refaktorera kod strukturellt
Den här guiden visar hur du får en agent att göra en bred mekanisk ändring – en codemod, en massmigrering av ett API, ett mönster du vill bli av med – med strukturell sökning och omskrivning, där ändringen förbereds för granskning innan något skrivs.
ast_grep matchar kod efter form. ast_edit skriver om den. resolve verkställer eller kastar.
Innan du börjar
Avsnitt med rubriken “Innan du börjar”Strukturella verktyg täcker de språk som kodgrafen parsar: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB och Ada. De behöver tree-sitter-grammatikerna på plats, vilket de alltid är i en levererad build – en saknad grammatik stoppar servern i stället för att försämra sökningen.
Hitta formen först
Avsnitt med rubriken “Hitta formen först”Skriv aldrig om något du inte har tittat på. Be agenten att söka:
Hitta varje ställe där vi anropar
dispose()på en nullable som vi just har null-kontrollerat.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")Utdata är file:line plus den matchade källkoden för varje ställe.
Vad mönstersyntaxen ger dig
Avsnitt med rubriken “Vad mönstersyntaxen ger dig”| Token | Matchar |
|---|---|
$NAME |
Vilken enskild nod som helst, fångad |
$$$NAME |
Vilken följd av syskon som helst – argument, satser |
$_ |
Vad som helst, fångat utan namn |
Det som spelar roll: ett upprepat $NAME måste fånga samma text.
if ($X != null) $X.dispose()hittar ett skyddsvillkor som gör sig av med det det testade. Det hittar inte if (a != null) b.dispose() – vilket oftast är buggen du letade efter. Ingen textsökning kan uttrycka det.
När mönstret inte hittar något
Avsnitt med rubriken “När mönstret inte hittar något”Två saker att prova, i tur och ordning:
- Gör det mindre.
foo($X)föreif (a) { foo($X); }. Ett mönster som inte är giltig kod i sig är ofta inte den konstruktion du menade. - Ange språket.
language:härleds bara närpathanger en enda fil.
Ett mönster som ingen parsningskontext accepterar rapporteras som oparsbart – verktyget säger det i stället för att rapportera ”inga träffar” för något som aldrig lästes så som det ser ut.
Skriv om, och titta innan det landar
Avsnitt med rubriken “Skriv om, och titta innan det landar”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Resultatet är ett förslag:
(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.Inget har skrivits till disk än. Läs listan, kontrollera ett par av ställena som sökningen rapporterade och kör sedan:
resolve(edit_id: "edit_1", action: "accept")eller
resolve(edit_id: "edit_1", action: "discard")Kontrollera ditt arbete med kompilatorn
Avsnitt med rubriken “Kontrollera ditt arbete med kompilatorn”Poängen med att göra det här strukturellt är att det är brett. Breda ändringar är precis där en omskrivning som ser rimlig ut bryter en typ. Be om diagnostik efter att du har verkställt:
lsp(action: "diagnostics", file: "*")file: "*" kör projektets egen kontroll (dart analyze, tsc --noEmit, cargo check, go build) i stället för en fråga per fil – rätt form av fråga efter en ändring som rörde fyrtio filer.
Exempel: migrera ett anrop
Avsnitt med rubriken “Exempel: migrera ett anrop”Säg att log(msg, level) blir log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Titta på träffarna. Om några av dem är en annan tvåarguments-log, snäva in mönstret genom att begränsa path till katalogen som äger den du menar, i stället för att försöka uttrycka skillnaden i mönstret.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Kör sedan resolve och därefter lsp(action: "diagnostics", file: "*").
När du bör använda något annat
Avsnitt med rubriken “När du bör använda något annat”| Situation | Bättre verktyg |
|---|---|
| Byta namn på en symbol | lsp_rename – följer referenser, inte former |
| Hitta en sträng, en kommentar eller ett filnamn | search (innehåll) / search_files (namn) |
| ”Vad går sönder om jag ändrar det här?” | Kodgrafen |
| Ett språk som grammatikerna inte täcker | search plus edit |
Relaterat
Avsnitt med rubriken “Relaterat”- Kodintelligens – varför strukturell matchning inte är ett regex med extra steg
- Inbyggda agentverktyg – hela argumentlistan
- Sök kod med kodgrafen – symbol- och påverkanssidan