Code structureel refactoren
Deze gids laat zien hoe je een agent een brede, mechanische wijziging laat doorvoeren — een codemod, een bulk-API-migratie, een patroon dat je weg wilt — met structureel zoeken en herschrijven, waarbij de wijziging klaarstaat voor review voordat er iets wordt geschreven.
ast_grep matcht code op vorm. ast_edit herschrijft die. resolve voert de wijziging door of gooit haar weg.
Voordat je begint
Sectie getiteld “Voordat je begint”Structurele tools dekken de talen die de codegraph parseert: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB en Ada. Ze hebben de tree-sitter-grammatica’s klaargezet nodig, wat in een uitgebrachte build altijd zo is — een ontbrekende grammatica stopt de server in plaats van het zoeken te verslechteren.
Vind eerst de vorm
Sectie getiteld “Vind eerst de vorm”Herschrijf nooit iets waar je niet naar hebt gekeken. Vraag de agent om te zoeken:
Vind elke plek waar we
dispose()aanroepen op een nullable die we net op null hebben gecontroleerd.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")De uitvoer is file:line plus de gematchte broncode voor elke plek.
Wat de patroonsyntaxis je oplevert
Sectie getiteld “Wat de patroonsyntaxis je oplevert”| Token | Matcht |
|---|---|
$NAME |
Elk afzonderlijk knooppunt, vastgelegd |
$$$NAME |
Elke reeks broers en zussen — argumenten, statements |
$_ |
Alles, zonder naam vastgelegd |
Degene die ertoe doet: een herhaalde $NAME moet dezelfde tekst vastleggen.
if ($X != null) $X.dispose()vindt een guard die opruimt wat hij heeft getest. Hij vindt niet if (a != null) b.dispose() — en dat is meestal precies de bug die je zocht. Geen enkele tekstuele zoekopdracht kan dat uitdrukken.
Als het patroon niets vindt
Sectie getiteld “Als het patroon niets vindt”Twee dingen om te proberen, in deze volgorde:
- Maak het kleiner.
foo($X)vóórif (a) { foo($X); }. Een patroon dat op zichzelf geen geldige code is, is vaak niet het construct dat je bedoelde. - Noem de taal.
language:wordt alleen afgeleid alspathnaar één enkel bestand wijst.
Een patroon dat geen enkele parsecontext accepteert, wordt als onparseerbaar gemeld — de tool zegt dat, in plaats van “geen overeenkomsten” te melden voor iets dat nooit werd gelezen zoals het eruitziet.
Herschrijven, en kijken voordat het landt
Sectie getiteld “Herschrijven, en kijken voordat het landt”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Het resultaat is een voorstel:
(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.Er staat nog niets op schijf. Lees de lijst, controleer een paar van de plekken die de zoekopdracht meldde, en dan:
resolve(edit_id: "edit_1", action: "accept")of
resolve(edit_id: "edit_1", action: "discard")Controleer je werk met de compiler
Sectie getiteld “Controleer je werk met de compiler”Het punt van dit structureel aanpakken is dat het breed is. Brede wijzigingen zijn precies waar een ogenschijnlijk plausibele herschrijving een type breekt. Vraag na het doorvoeren om diagnostiek:
lsp(action: "diagnostics", file: "*")file: "*" draait de eigen checker van het project (dart analyze, tsc --noEmit, cargo check, go build) in plaats van een query per bestand — de juiste vorm van vraag na een wijziging die veertig bestanden raakte.
Uitgewerkt voorbeeld: een aanroep migreren
Sectie getiteld “Uitgewerkt voorbeeld: een aanroep migreren”Stel dat log(msg, level) verandert in log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Bekijk de treffers. Als sommige ervan een andere log met twee argumenten zijn, beperk het patroon dan door path te richten op de map die de bedoelde bevat, in plaats van het verschil in het patroon te willen uitdrukken.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Dan resolve, dan lsp(action: "diagnostics", file: "*").
Wanneer je iets anders gebruikt
Sectie getiteld “Wanneer je iets anders gebruikt”| Situatie | Betere tool |
|---|---|
| Een symbool hernoemen | lsp_rename — die volgt referenties, geen vormen |
| Een string, een commentaar of een bestandsnaam vinden | search (inhoud) / search_files (namen) |
| “Wat breekt er als ik dit wijzig?” | De codegraph |
| Een taal die de grammatica’s niet dekken | search plus edit |
Gerelateerd
Sectie getiteld “Gerelateerd”- Code-intelligentie — waarom structureel matchen geen regex met extra stappen is
- Ingebouwde agenttools — de volledige argumentenlijst
- Code zoeken met de codegraph — de symbolen- en impactkant