Tìm kiếm trang web

Công cụ này cho phép bạn thực hiện các thay đổi mã ma thuật—mà không cần AI


Trong thế giới lập trình, văn bản là vua, vì vậy các công cụ như grep, sed và awk là những người bạn tuyệt vời cho trình biên dịch hoặc thông dịch của bạn. Hộp công cụ của mọi lập trình viên cũng có thể được lợi từ một hệ thống kiểm soát phiên bản dựa trên văn bản, như Git.

Nhưng tất cả các công cụ này đều hoạt động ở mức văn bản chung. Trong khi các biểu thức chính quy có thể vô cùng mạnh mẽ, chúng vẫn chưa hoàn toàn phù hợp với ngữ pháp của một ngôn ngữ lập trình. Để thực hiện tìm kiếm có ý nghĩa và mang tính ngữ nghĩa, bạn cần một loại công cụ khác. Giới thiệu ast-grep.

Ast-grep là gì, và tại sao bạn lại cần nó?

Tìm kiếm siêu mạnh mẽ hiểu mã của bạn

Giả sử bạn muốn có một danh sách tất cả các tên hàm trong cơ sở mã của bạn. Một thử nghiệm đầu tiên có thể trông giống như thế này:

grep -r 'function' ...

Và sau đó bạn có thể nhận ra rằng bạn đang khớp quá nhiều trường hợp không liên quan, vì vậy bạn tinh chỉnh biểu thức chính quy của mình:

egrep -r '^function\W' ...

Bạn tiếp tục theo cách này cho đến khi, cuối cùng, bạn nhận ra rằng, bất kể bạn làm gì, tìm kiếm của bạn sẽ khớp với một thứ gì đó như:

/*
The following should be:
function foo(a, b, c)
but that’s not going to work because...
*/

Các công cụ như grep đơn giản là không hiểu ngữ cảnh của một thứ trông giống như khai báo hàm bên trong nhận xét. Chúng hoạt động chỉ bằng cách so khớp các ký tự riêng lẻ, chứ không cân nhắc bất kỳ ý nghĩa rộng hơn nào mà sự kết hợp của những ký tự đó có thể thể hiện.

ast-grep thì khác. Thay vì đọc văn bản từng ký tự một, từng dòng một, nó phân tích cú pháp văn bản (giống như một trình biên dịch) và xây dựng một cây cú pháp trừu tượng (AST) đại diện cho ý nghĩa thực sự của mã của bạn. Dựa trên đó, nó có thể thực hiện các tìm kiếm ngữ nghĩa cho các phần tử như khai báo biến, gọi hàm, v.v.

Bên trong, ast-grep sử dụng thư viện Tree-sitter phổ biến, cho phép nó hỗ trợ một loạt các ngôn ngữ đa dạng như Python, Java và Go. ast-grep được viết bằng Rust, vì vậy nó hoạt động tốt, ngay cả khi tìm kiếm trên các cơ sở mã lớn. Điều này quan trọng vì, do nó phải phân tích đầy đủ các tệp trước khi tìm kiếm, nên có nhiều công việc phải làm hơn so với tìm kiếm dựa trên văn bản thông thường.

Cách tìm kiếm gần như bất cứ thứ gì bằng ast-grep

Sử dụng dòng lệnh hoặc một ứng dụng web hoàn hảo cho người mới bắt đầu

Cách sử dụng ast-grep đơn giản nhất là sử dụng tùy chọn -p để chỉ định một mẫu đơn giản duy nhất.

ast-grep -p 'console.log'

Không có bất kỳ đối số đường dẫn nào, ast-grep sẽ tìm kiếm các tệp trong thư mục hiện tại, theo đệ qui. Trừ khi bạn chỉ định một ngôn ngữ, ast-grep sẽ suy ra ngôn ngữ dựa trên phần mở rộng của tệp. Chương trình sẽ nhóm kết quả theo tệp, in chi tiết của từng khớp và làm nổi bật các phần liên quan:

Lưu ý rằng tìm kiếm này trả về các lời gọi hàm không có đối số và một tham chiếu đến thuộc tính phương thức mà thậm chí không phải là một lời gọi. Tuy nhiên, nó tìm kiếm cụ thể một đối tượng có tên là console, vì vậy nó sẽ không tìm thấy những điều sau:

with (console) {
    log("Hello, world.");
}

Nhưng nó sẽ, một cách chính xác, bỏ qua "'console.log'" như một chuỗi và "/* console.log */" như một chú thích. Tại thời điểm này, tôi khuyên bạn nên khám phá sân chơi ast-grep, một ứng dụng web chạy ast-grep ở nền và trình bày kết quả của nó:

Như bạn có thể thấy, sân chơi rõ ràng chỉ ra chính xác những gì khớp và những gì không khớp. Điều này trái ngược với chương trình dòng lệnh, chương trình lọc đầu vào và chỉ trả về các kết quả khớp. Tôi thấy sân chơi rất hữu ích khi mới bắt đầu tìm hiểu về ast-grep và cố gắng xây dựng các mẫu riêng của mình.

Bạn có thể sử dụng các biến meta để khớp với nội dung động hơn. Một biến meta bắt đầu bằng $ và chỉ sử dụng chữ cái in hoa, cùng với gạch dưới và chữ số. Để tìm tất cả các lần gọi console.log với một đối số, hãy sử dụng mẫu này:

console.log($SINGLE_ARG)

Lưu ý rằng mẫu khớp với các cuộc gọi có một đối số, không phải những cuộc gọi không có đối số hoặc có nhiều hơn một:

Nếu bạn muốn khớp nhiều đối số, bạn có thể sử dụng một biến meta đa năng, bắt đầu bằng ba dấu đô la:

console.log($$$MULTIPLE_ARGS)

Mẫu này sẽ khớp với tất cả các lệnh gọi console.log, bao gồm cả những lệnh không có hoặc có nhiều đối số:

Đối với các tìm kiếm phức tạp hơn, ast-grep hỗ trợ cú pháp quy tắc trong YAML. Bạn có thể sử dụng điều này để định nghĩa các tìm kiếm có ngữ cảnh cao, tận dụng đầy đủ cấu trúc AST.

Ví dụ, đây là một quy tắc khớp với tất cả các lệnh gọi đến console.log, console.debug và console.warn, cộng với các lệnh gọi đến console.error nếu chúng không nằm trong một khối catch:

id: no-console-except-error
language: typescript
rule:
  any:
    - pattern: console.error($$$)
      not:
        inside:
          kind: catch_clause
          stopBy: end
    - pattern: console.$METHOD($$$)
constraints:
  METHOD:
    regex: 'log|debug|warn'

Thay đổi mã của bạn mà không chỉnh sửa thủ công

Với việc chuẩn bị và thử nghiệm cẩn thận, ast-grep có thể trở thành một trình chỉnh sửa tự động mạnh mẽ

ast-grep đã là một công cụ mạnh mẽ, nhưng nó đi xa hơn việc tìm kiếm. Với tính năng thay thế của nó, ast-grep còn cho phép bạn chỉnh sửa mã của mình nữa. Điều này có nghĩa là bạn có thể thực hiện những thay đổi vô cùng tốn thời gian chỉ trong vài giây, dù là tự động hay tương tác.

Hãy xem xét ví dụ console.log trước đó. Hãy tưởng tượng một trong các cơ sở mã của bạn đầy rẫy mã gỡ lỗi gọi console.log. Bạn quyết định muốn một thứ gì đó tinh vi hơn một chút, vì vậy bạn bắt đầu với một hàm tùy chỉnh, my_logger:

function my_logger() {
    if (GLOBAL_DEBUG) {
        console.log.apply(null, arguments)
    }
}

Nó chỉ là một lớp bao đơn giản cho console.log, với một cờ toàn cục, tạm thời. Nhưng ý là bạn sẽ cần phải chuyển đổi mỗi lần gọi console.log() sang my_logger. Sử dụng ast-grep, đây là một quá trình đơn giản. Bắt đầu với một tệp quy tắc, ví dụ: fix-logging.yml:

rule:
  pattern: console.log($$$MULTIPLE_ARGS)
fix: my_logger($$$MULTIPLE_ARGS)

Sau đó chạy nó như thế này để xem những thay đổi sẽ được thực hiện:

ast-grep scan --rule fix-logging.yml

Nếu bạn hài lòng với kết quả, bạn có thể để ast-grep thực hiện những thay đổi này cho bạn, cập nhật các tệp tại chỗ với tùy chọn --update-all:

ast-grep scan --rule fix-logging.yml --update-all

Có nhiều chế độ bạn có thể sử dụng để thực hiện thay đổi, bao gồm một chế độ tương tác, có lẽ là cách an toàn nhất để chạy ast-grep trong khi bạn đang làm quen với nó.







Mặc dù đường cong học tập cao, việc thành thạo ast-grep sẽ mang lại phần thưởng

Không còn nghi ngờ gì nữa: ast-grep là một chương trình phức tạp. Các ví dụ mà tôi đã đưa vào đây nằm ở mức dễ hơn, và nếu bạn tìm hiểu kỹ hơn vào tài liệu, bạn sẽ thấy công cụ này còn có nhiều tính năng hơn nữa.

Mặc dù nó có thể đáng sợ, sân chơi giúp rất nhiều, và việc sử dụng chế độ tương tác để dọn dẹp cơ sở mã của bạn là một cách tuyệt vời để bắt đầu học ast-grep.

Bài viết liên quan