รีแฟกเตอร์โค้ดเชิงโครงสร้าง
คู่มือนี้แสดงวิธีทำให้เอเจนต์ลงมือเปลี่ยนแปลงแบบกลไกในวงกว้าง — โค้ดมอด การโยกย้าย API แบบยกชุด แพตเทิร์นที่คุณอยากให้หายไป — ด้วยการค้นหาและเขียนใหม่เชิงโครงสร้าง โดยพักการเปลี่ยนแปลงไว้ให้รีวิวก่อนที่อะไรจะถูกเขียนลงไป
ast_grep จับคู่โค้ดตามรูปร่าง ast_edit เขียนมันใหม่ resolve คอมมิตหรือทิ้ง
ก่อนเริ่ม
หัวข้อที่มีชื่อว่า “ก่อนเริ่ม”เครื่องมือเชิงโครงสร้างครอบคลุมภาษาที่กราฟโค้ดแยกวิเคราะห์: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB และ Ada เครื่องมือเหล่านี้ต้องมีไวยากรณ์ tree-sitter เตรียมไว้พร้อม ซึ่งในบิลด์ที่จัดส่งมาก็มีเสมอ — ไวยากรณ์ที่หายไปจะทำให้เซิร์ฟเวอร์หยุด ไม่ใช่ทำให้การค้นหาด้อยลง
หารูปร่างก่อน
หัวข้อที่มีชื่อว่า “หารูปร่างก่อน”อย่าเขียนอะไรที่คุณยังไม่ได้ดูมันก่อน ขอให้เอเจนต์ค้นหา:
หาทุกจุดที่เราเรียก
dispose()บนค่าที่เป็น null ได้ ซึ่งเราเพิ่งตรวจว่าไม่เป็น null
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")ผลลัพธ์คือ file:line พร้อมซอร์สที่จับคู่ได้ของแต่ละจุด
สิ่งที่ไวยากรณ์แพตเทิร์นให้คุณ
หัวข้อที่มีชื่อว่า “สิ่งที่ไวยากรณ์แพตเทิร์นให้คุณ”| โทเคน | จับคู่กับ |
|---|---|
$NAME |
โหนดเดียวใด ๆ และถูกจับเก็บไว้ |
$$$NAME |
โหนดพี่น้องที่ต่อเนื่องกันชุดใดก็ได้ — อาร์กิวเมนต์ คำสั่ง |
$_ |
อะไรก็ได้ และถูกจับเก็บโดยไม่มีชื่อ |
ข้อที่สำคัญคือ $NAME ที่ซ้ำกันต้องจับข้อความเดียวกัน
if ($X != null) $X.dispose()หาโค้ดป้องกันที่ dispose สิ่งที่มันเพิ่งตรวจสอบเจอ แต่จะไม่หา if (a != null) b.dispose() เจอ — ซึ่งปกติแล้วคือบั๊กที่คุณกำลังตามหาอยู่พอดี ไม่มีการค้นหาเชิงข้อความใดที่แสดงออกมาแบบนั้นได้
เมื่อแพตเทิร์นไม่เจออะไรเลย
หัวข้อที่มีชื่อว่า “เมื่อแพตเทิร์นไม่เจออะไรเลย”สองอย่างที่ลองได้ เรียงตามลำดับ:
- ทำมันให้เล็กลง ใช้
foo($X)ก่อนif (a) { foo($X); }แพตเทิร์นที่ไม่ใช่โค้ดที่ถูกต้องในตัวเองมักไม่ใช่โครงสร้างที่คุณตั้งใจ - ระบุชื่อภาษา
language:จะถูกอนุมานก็ต่อเมื่อpathชี้ไปที่ไฟล์เดียว
แพตเทิร์นที่ไม่มีบริบทการแยกวิเคราะห์ใดยอมรับ จะถูกรายงานว่าแยกวิเคราะห์ไม่ได้ — เครื่องมือจะบอกเช่นนั้น แทนที่จะรายงานว่า “ไม่พบรายการที่ตรงกัน” สำหรับสิ่งที่ไม่เคยถูกอ่านในแบบที่มันดูเหมือนเลย
เขียนใหม่ แล้วดูให้ดีก่อนมันลงจริง
หัวข้อที่มีชื่อว่า “เขียนใหม่ แล้วดูให้ดีก่อนมันลงจริง”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)ผลลัพธ์คือข้อเสนอ:
(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.ยังไม่มีอะไรอยู่บนดิสก์ อ่านรายการ ตรวจดูสักสองสามจุดที่การค้นหารายงาน แล้ว:
resolve(edit_id: "edit_1", action: "accept")หรือ
resolve(edit_id: "edit_1", action: "discard")ตรวจงานของคุณด้วยคอมไพเลอร์
หัวข้อที่มีชื่อว่า “ตรวจงานของคุณด้วยคอมไพเลอร์”ประเด็นของการทำแบบเชิงโครงสร้างคือมันกว้าง และการเปลี่ยนแปลงที่กว้างก็คือจุดที่การเขียนใหม่ที่ดูสมเหตุสมผลทำไทป์พังพอดี ขอผลวินิจฉัยหลัง resolve:
lsp(action: "diagnostics", file: "*")file: "*" รันตัวตรวจสอบของโปรเจกต์เอง (dart analyze, tsc --noEmit, cargo check, go build) แทนที่จะเป็นการค้นต่อไฟล์ — เป็นรูปคำถามที่ถูกต้องหลังการเปลี่ยนแปลงที่แตะไปสี่สิบไฟล์
ตัวอย่างลงมือทำ: การย้ายการเรียก
หัวข้อที่มีชื่อว่า “ตัวอย่างลงมือทำ: การย้ายการเรียก”สมมติว่า log(msg, level) กำลังกลายเป็น log(level: level, message: msg)
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")ดูผลที่เจอ ถ้าบางอันเป็น log แบบสองอาร์กิวเมนต์ที่ต่างกัน ให้ทำให้แพตเทิร์นแคบลงโดยจำกัด path ไว้ที่ไดเรกทอรีที่เป็นเจ้าของตัวที่คุณหมายถึง แทนที่จะพยายามอธิบายความต่างนั้นในแพตเทิร์น
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)จากนั้น resolve แล้วต่อด้วย lsp(action: "diagnostics", file: "*")
เมื่อใดควรใช้เครื่องมืออื่น
หัวข้อที่มีชื่อว่า “เมื่อใดควรใช้เครื่องมืออื่น”| สถานการณ์ | เครื่องมือที่เหมาะกว่า |
|---|---|
| การเปลี่ยนชื่อสัญลักษณ์ | lsp_rename — มันตามการอ้างอิง ไม่ใช่รูปร่าง |
| การหาสตริง คอมเมนต์ หรือชื่อไฟล์ | search (เนื้อหา) / search_files (ชื่อ) |
| “ถ้าฉันเปลี่ยนตรงนี้ อะไรจะพัง” | กราฟโค้ด |
| ภาษาที่ไวยากรณ์ยังไม่ครอบคลุม | search บวกกับ edit |
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”- ความฉลาดของโค้ด — ทำไมการจับคู่เชิงโครงสร้างจึงไม่ใช่รีจิกซ์ที่เพิ่มขั้นตอนเข้ามา
- เครื่องมือเอเจนต์ในตัว — รายการอาร์กิวเมนต์ทั้งหมด
- ค้นหาโค้ดด้วยกราฟโค้ด — ด้านสัญลักษณ์และผลกระทบ