Tái cấu trúc mã theo cấu trúc
Hướng dẫn này chỉ cho bạn cách để một agent thực hiện một thay đổi cơ học trên diện rộng — một codemod, một đợt di trú API hàng loạt, một mẫu bạn muốn loại bỏ — bằng tìm kiếm và viết lại theo cấu trúc, với thay đổi được đưa vào vùng chờ để xem xét trước khi bất cứ thứ gì được ghi.
ast_grep khớp mã theo cấu trúc. ast_edit viết lại nó. resolve áp dụng hoặc hủy bỏ.
Trước khi bắt đầu
Phần tiêu đề “Trước khi bắt đầu”Các công cụ cấu trúc bao phủ những ngôn ngữ mà đồ thị mã phân tích cú pháp: Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB và Ada. Chúng cần các ngữ pháp tree-sitter được chuẩn bị sẵn, và trong một bản dựng phát hành thì chúng luôn có sẵn — một ngữ pháp thiếu sẽ chặn máy chủ chứ không làm suy giảm tìm kiếm.
Tìm cấu trúc trước
Phần tiêu đề “Tìm cấu trúc trước”Đừng bao giờ viết lại thứ bạn chưa xem qua. Hãy yêu cầu agent tìm kiếm:
Tìm mọi chỗ chúng ta gọi
dispose()trên một giá trị nullable mà chúng ta vừa kiểm tra null.
ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")Kết quả là file:line kèm mã nguồn khớp được ở mỗi vị trí.
Cú pháp mẫu mang lại cho bạn những gì
Phần tiêu đề “Cú pháp mẫu mang lại cho bạn những gì”| Token | Khớp |
|---|---|
$NAME |
Một nút bất kỳ, được bắt lại |
$$$NAME |
Một dãy nút anh em bất kỳ — đối số, câu lệnh |
$_ |
Bất cứ thứ gì, bắt lại nhưng không đặt tên |
Điều quan trọng nhất: một $NAME lặp lại phải bắt đúng cùng một đoạn văn bản.
if ($X != null) $X.dispose()sẽ tìm thấy một câu lệnh bảo vệ giải phóng đúng thứ nó vừa kiểm tra. Nó không tìm thấy if (a != null) b.dispose() — mà đó thường chính là lỗi bạn đang tìm. Không tìm kiếm văn bản nào diễn đạt được điều đó.
Khi mẫu không tìm thấy gì
Phần tiêu đề “Khi mẫu không tìm thấy gì”Hai điều cần thử, theo thứ tự:
- Thu nhỏ nó lại. Dùng
foo($X)trướcif (a) { foo($X); }. Một mẫu không phải là mã hợp lệ khi đứng một mình thường cũng không phải là cấu trúc bạn muốn. - Nêu rõ ngôn ngữ.
language:chỉ được suy ra khipathtrỏ tới một tệp duy nhất.
Một mẫu mà không ngữ cảnh phân tích nào chấp nhận sẽ được báo là không thể phân tích cú pháp — công cụ nói rõ điều đó thay vì báo “không có kết quả khớp” cho một thứ chưa từng được đọc theo cách nó trông như vậy.
Viết lại, và xem xét trước khi áp dụng
Phần tiêu đề “Viết lại, và xem xét trước khi áp dụng”ast_edit( pattern: "if ($X != null) $X.dispose()", rewrite: "$X?.dispose()", language: "dart",)Kết quả là một đề xuất:
(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.Chưa có gì được ghi lên đĩa. Hãy đọc danh sách, kiểm tra một vài vị trí mà tìm kiếm đã báo, rồi:
resolve(edit_id: "edit_1", action: "accept")hoặc
resolve(edit_id: "edit_1", action: "discard")Kiểm tra kết quả bằng trình biên dịch
Phần tiêu đề “Kiểm tra kết quả bằng trình biên dịch”Lý do làm việc này theo cấu trúc là vì nó có phạm vi rộng. Những thay đổi rộng chính là nơi một bản viết lại trông có vẻ hợp lý sẽ làm vỡ một kiểu dữ liệu. Hãy yêu cầu chẩn đoán sau khi resolve:
lsp(action: "diagnostics", file: "*")file: "*" chạy trình kiểm tra của chính dự án (dart analyze, tsc --noEmit, cargo check, go build) thay vì truy vấn từng tệp — đúng kiểu câu hỏi cần đặt ra sau một thay đổi đụng tới bốn mươi tệp.
Ví dụ minh họa: di trú một lời gọi
Phần tiêu đề “Ví dụ minh họa: di trú một lời gọi”Giả sử log(msg, level) đang chuyển thành log(level: level, message: msg).
ast_grep(pattern: "log($MSG, $LEVEL)", language: "dart")Hãy xem các kết quả khớp. Nếu một số trong đó là một log hai đối số khác, hãy thu hẹp mẫu bằng cách giới hạn path vào thư mục chứa hàm bạn muốn, thay vì cố diễn đạt sự khác biệt đó trong mẫu.
ast_edit( pattern: "log($MSG, $LEVEL)", rewrite: "log(level: $LEVEL, message: $MSG)", path: "lib/features/telemetry", language: "dart",)Sau đó resolve, rồi lsp(action: "diagnostics", file: "*").
Khi nào nên dùng thứ khác
Phần tiêu đề “Khi nào nên dùng thứ khác”| Tình huống | Công cụ phù hợp hơn |
|---|---|
| Đổi tên một ký hiệu | lsp_rename — nó đi theo tham chiếu, không theo cấu trúc |
| Tìm một chuỗi, một chú thích hay một tên tệp | search (nội dung) / search_files (tên) |
| “Thay đổi này sẽ làm hỏng những gì?” | Đồ thị mã |
| Một ngôn ngữ mà các ngữ pháp không bao phủ | search kèm edit |
Liên quan
Phần tiêu đề “Liên quan”- Trí tuệ mã nguồn — vì sao khớp theo cấu trúc không phải là regex thêm vài bước
- Công cụ agent tích hợp — danh sách đầy đủ các đối số
- Tìm mã bằng đồ thị mã — phần ký hiệu và tác động