Refaktorer kode strukturelt
Denne veiledningen viser deg hvordan du får en agent til å gjøre en bred, mekanisk endring — en codemod, en massemigrering av et API, et mønster du vil bli kvitt — ved hjelp av strukturelt søk og strukturell omskriving, med endringen mellomlagret for gjennomgang før noe som helst skrives.
ast_grep matcher kode etter form. ast_edit skriver den om. resolve sender inn eller forkaster.
Før du begynner
Seksjon med tittelen «Før du begynner»Strukturelle verktøy dekker språkene kodegrafen parser: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB og Ada. De trenger tree-sitter-grammatikkene på plass, og det er de alltid i et ferdiglevert bygg — en manglende grammatikk stopper serveren i stedet for å svekke søket.
Finn formen først
Seksjon med tittelen «Finn formen først»Skriv aldri om noe du ikke har sett på. Be agenten søke:
Finn hvert sted der vi kaller
dispose()på en nullable som vi nettopp har null-sjekket.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")Utdataene er file:line pluss kildekoden som ble matchet, for hvert sted.
Hva mønstersyntaksen gir deg
Seksjon med tittelen «Hva mønstersyntaksen gir deg»| Token | Matcher |
|---|---|
$NAME |
Enhver enkelt node, fanget |
$$$NAME |
Enhver sekvens av søsken — argumenter, setninger |
$_ |
Hva som helst, fanget uten navn |
Det som betyr noe: et gjentatt $NAME må fange den samme teksten.
if ($X != null) $X.dispose()finner en vakt som frigjør det den testet. Den finner ikke if (a != null) b.dispose() — som vanligvis er feilen du lette etter. Det kan ikke noe tekstbasert søk uttrykke.
Når mønsteret ikke finner noe
Seksjon med tittelen «Når mønsteret ikke finner noe»To ting du kan prøve, i denne rekkefølgen:
- Gjør det mindre.
foo($X)førif (a) { foo($X); }. Et mønster som ikke er gyldig kode alene, er ofte ikke det konstruktet du mente. - Oppgi språket.
language:utledes bare nårpathpeker på én enkelt fil.
Et mønster som ingen parsekontekst godtar, rapporteres som uparsbart — verktøyet sier det i stedet for å rapportere «ingen treff» for noe som aldri ble lest slik det ser ut.
Skriv om, og se på det før det lander
Seksjon med tittelen «Skriv om, og se på det før det lander»ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Resultatet er et forslag:
(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.Ingenting er på disken ennå. Les lista, sjekk et par av stedene søket rapporterte, og kjør deretter:
resolve(edit_id: "edit_1", action: "accept")eller
resolve(edit_id: "edit_1", action: "discard")Sjekk arbeidet ditt med kompilatoren
Seksjon med tittelen «Sjekk arbeidet ditt med kompilatoren»Poenget med å gjøre dette strukturelt, er at det er bredt. Brede endringer er nettopp der en omskriving som ser troverdig ut, ødelegger en type. Be om diagnostikk etter at du har sendt inn:
lsp(action: "diagnostics", file: "*")file: "*" kjører prosjektets egen sjekker (dart analyze, tsc --noEmit, cargo check, go build) i stedet for en spørring per fil — den riktige formen på spørsmålet etter en endring som berørte førti filer.
Fullstendig eksempel: migrere et kall
Seksjon med tittelen «Fullstendig eksempel: migrere et kall»Si at log(msg, level) er i ferd med å bli log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Se på treffene. Hvis noen av dem er en annen log med to argumenter, avgrens mønsteret ved å sette path til mappa som eier den du mener, i stedet for å prøve å uttrykke forskjellen i mønsteret.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Deretter resolve, så lsp(action: "diagnostics", file: "*").
Når du bør bruke noe annet
Seksjon med tittelen «Når du bør bruke noe annet»| Situasjon | Bedre verktøy |
|---|---|
| Å gi et symbol nytt navn | lsp_rename — den følger referanser, ikke former |
| Å finne en streng, en kommentar eller et filnavn | search (innhold) / search_files (navn) |
| «Hva går i stykker hvis jeg endrer dette?» | Kodegrafen |
| Et språk grammatikkene ikke dekker | search pluss edit |
Relatert
Seksjon med tittelen «Relatert»- Kodeintelligens — hvorfor strukturell matching ikke er et regex med ekstra trinn
- Innebygde agentverktøy — hele argumentlista
- Søk i kode med kodegrafen — siden med symboler og påvirkning