Kodu yapısal olarak yeniden düzenleme
Bu kılavuz, bir ajanın yapısal arama ve yeniden yazmayı kullanarak geniş ve mekanik bir değişikliği — bir codemod, toplu bir API geçişi, kurtulmak istediğiniz bir deseni — nasıl yapacağını gösterir; değişiklik, herhangi bir şey yazılmadan önce incelenmek üzere sahnelenir.
ast_grep kodu şekle göre eşleştirir. ast_edit onu yeniden yazar. resolve ise işler ya da atar.
Başlamadan önce
Bölüm başlığı “Başlamadan önce”Yapısal araçlar, kod grafiğinin ayrıştırdığı dilleri kapsar: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB ve Ada. Bunların tree-sitter gramerlerinin hazırlanmış olması gerekir; gönderilen bir derlemede bu her zaman böyledir — eksik bir gramer, aramayı bozmak yerine sunucuyu durdurur.
Önce şekli bulun
Bölüm başlığı “Önce şekli bulun”Bakmadığınız bir şeyi asla yeniden yazmayın. Ajandan arama yapmasını isteyin:
Az önce null denetimi yaptığımız bir nullable üzerinde
dispose()çağırdığımız her yeri bul.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")Çıktı, her yer için file:line bilgisi ve eşleşen kaynak metnidir.
Desen söz dizimi size ne kazandırır
Bölüm başlığı “Desen söz dizimi size ne kazandırır”| Belirteç | Eşleştirdiği |
|---|---|
$NAME |
Yakalanan herhangi bir tek düğüm |
$$$NAME |
Herhangi bir kardeş dizisi — argümanlar, deyimler |
$_ |
Her şey, adsız yakalanır |
Önemli olan şu: yinelenen bir $NAME aynı metni yakalamak zorundadır.
if ($X != null) $X.dispose()test ettiği şeyi dispose eden bir koruma koşuluyla eşleşir. if (a != null) b.dispose() ile eşleşmez — ki bu genellikle aradığınız hatanın ta kendisidir. Hiçbir metinsel arama bunu ifade edemez.
Desen hiçbir şey bulamadığında
Bölüm başlığı “Desen hiçbir şey bulamadığında”Sırayla denenecek iki şey:
- Daha küçük yapın.
if (a) { foo($X); }yerine öncefoo($X)deneyin. Tek başına geçerli kod olmayan bir desen, çoğu zaman kastettiğiniz yapı değildir. - Dili belirtin.
language:yalnızcapathtek bir dosyayı gösterdiğinde çıkarımlanır.
Hiçbir ayrıştırma bağlamının kabul etmediği bir desen, ayrıştırılamaz olarak bildirilir — araç, hiçbir zaman göründüğü gibi okunmayan bir şey için “eşleşme yok” demek yerine bunu söyler.
Yeniden yazın ve yere inmeden önce bakın
Bölüm başlığı “Yeniden yazın ve yere inmeden önce bakın”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Sonuç bir öneridir:
(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.Henüz diskte hiçbir şey yok. Listeyi okuyun, aramanın bildirdiği birkaç yeri denetleyin, ardından:
resolve(edit_id: "edit_1", action: "accept")ya da
resolve(edit_id: "edit_1", action: "discard")Çalışmanızı derleyiciyle denetleyin
Bölüm başlığı “Çalışmanızı derleyiciyle denetleyin”Bunu yapısal olarak yapmanın amacı, geniş olmasıdır. Akla yatkın görünen bir yeniden yazmanın bir türü bozduğu yer tam da geniş değişikliklerdir. İşledikten sonra tanılama isteyin:
lsp(action: "diagnostics", file: "*")file: "*", dosya başına bir sorgu yerine projenin kendi denetleyicisini (dart analyze, tsc --noEmit, cargo check, go build) çalıştırır — kırk dosyaya dokunan bir değişiklikten sonra sorulacak doğru soru biçimi budur.
Uygulamalı örnek: bir çağrıyı yeni biçimine geçirme
Bölüm başlığı “Uygulamalı örnek: bir çağrıyı yeni biçimine geçirme”Diyelim ki log(msg, level), log(level: level, message: msg) biçimine geçiyor.
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Eşleşmelere bakın. Bazıları başka bir iki argümanlı log ise, farkı desende ifade etmeye çalışmak yerine path değerini kastettiğiniz log’un bulunduğu dizinle sınırlayarak deseni daraltın.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Ardından resolve, sonra lsp(action: "diagnostics", file: "*").
Ne zaman başka bir şey kullanmalı
Bölüm başlığı “Ne zaman başka bir şey kullanmalı”| Durum | Daha iyi araç |
|---|---|
| Bir sembolü yeniden adlandırma | lsp_rename — şekilleri değil, referansları izler |
| Bir dizeyi, bir yorumu ya da bir dosya adını bulma | search (içerik) / search_files (adlar) |
| “Bunu değiştirirsem ne bozulur?” | Kod grafiği |
| Gramerlerin kapsamadığı bir dil | search artı edit |
İlgili
Bölüm başlığı “İlgili”- Kod zekâsı — yapısal eşleştirme neden fazladan adımları olan bir regex değildir
- Yerleşik ajan araçları — tam argüman listesi
- Kod grafiğiyle kod arama — sembol ve etki tarafı