Langkau ke kandungan

Refaktor kod secara struktur

Panduan ini menunjukkan cara meminta ejen membuat perubahan mekanikal yang luas — codemod, penghijrahan API secara pukal, corak yang anda mahu hapuskan — menggunakan carian dan penulisan semula struktur, dengan perubahan itu diperingkatkan untuk semakan sebelum apa-apa ditulis.

ast_grep memadankan kod mengikut bentuk. ast_edit menulis semulanya. resolve mengkomit atau membuangnya.

Alat struktur merangkumi bahasa yang dihuraikan oleh graf kod: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB dan Ada. Ia memerlukan tatabahasa tree-sitter disediakan, dan ia sentiasa begitu dalam binaan yang dikeluarkan — tatabahasa yang hilang menghentikan pelayan dan bukannya merendahkan carian.

Jangan sekali-kali menulis semula sesuatu yang belum anda lihat. Minta ejen mencari:

Cari setiap tempat kita memanggil dispose() pada nilai nullable yang baru sahaja kita semak null.

ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")

Outputnya ialah file:line serta sumber yang dipadankan bagi setiap tapak.

Token Padanan
$NAME Sebarang nod tunggal, ditangkap
$$$NAME Sebarang jujukan nod bersaudara — argumen, penyataan
$_ Apa-apa sahaja, ditangkap tanpa nama

Yang paling penting: $NAME yang berulang mesti menangkap teks yang sama.

if ($X != null) $X.dispose()

menemui pengawal yang melupuskan apa yang diujinya. Ia tidak menemui if (a != null) b.dispose() — yang selalunya menjadi pepijat yang anda cari. Tiada carian teks dapat menyatakan perkara itu.

Dua perkara untuk dicuba, mengikut urutan:

  1. Kecilkan ia. foo($X) sebelum if (a) { foo($X); }. Corak yang bukan kod sah dengan sendirinya selalunya bukan konstruk yang anda maksudkan.
  2. Namakan bahasa. language: hanya diinferensikan apabila path menamakan satu fail tunggal.

Corak yang tidak diterima oleh mana-mana konteks penghuraian dilaporkan sebagai tidak boleh dihuraikan — alat itu menyatakannya dan bukannya melaporkan “tiada padanan” bagi sesuatu yang tidak pernah dibaca seperti rupanya.

ast_edit(
pattern: "if ($X != null) $X.dispose()",
rewrite: "$X?.dispose()",
language: "dart",
)

Hasilnya ialah satu cadangan:

(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.

Belum ada apa-apa di cakera. Baca senarai itu, semak beberapa tapak yang dilaporkan oleh carian, kemudian:

resolve(edit_id: "edit_1", action: "accept")

atau

resolve(edit_id: "edit_1", action: "discard")

Tujuan melakukannya secara struktur ialah kerana ia luas. Perubahan luas ialah tempat yang tepat apabila penulisan semula yang kelihatan munasabah memecahkan sesuatu jenis. Minta diagnostik selepas melakukan resolve:

lsp(action: "diagnostics", file: "*")

file: "*" menjalankan pemeriksa projek itu sendiri (dart analyze, tsc --noEmit, cargo check, go build) dan bukannya pertanyaan bagi setiap fail — bentuk soalan yang betul selepas perubahan yang menyentuh empat puluh fail.

Katakan log(msg, level) sedang bertukar menjadi log(level: level, message: msg).

ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")

Lihat padanan yang ditemui. Jika sebahagian daripadanya ialah log dua argumen yang berbeza, sempitkan corak itu dengan menskopkan path kepada direktori yang memiliki log yang anda maksudkan, dan bukannya cuba menyatakan perbezaan itu dalam corak.

ast_edit(
pattern: "log($MSG, $LEVEL)",
rewrite: "log(level: $LEVEL, message: $MSG)",
path: "lib/features/telemetry",
language: "dart",
)

Kemudian resolve, kemudian lsp(action: "diagnostics", file: "*").

Keadaan Alat yang lebih baik
Menamakan semula simbol lsp_rename — ia mengikut rujukan, bukan bentuk
Mencari rentetan, komen atau nama fail search (kandungan) / search_files (nama)
“Apa yang pecah jika saya ubah ini?” Graf kod
Bahasa yang tidak diliputi oleh tatabahasa search bersama edit