Δομική αναδιάρθρωση κώδικα
Αυτός ο οδηγός δείχνει πώς να κάνετε έναν πράκτορα να πραγματοποιήσει μια ευρεία μηχανική αλλαγή — ένα codemod, μια μαζική μετεγκατάσταση 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()σε μια nullable μεταβλητή που μόλις ελέγξαμε για 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 |
Σχετικά
Ενότητα με τίτλο «Σχετικά»- Νοημοσύνη κώδικα — γιατί η δομική αντιστοίχιση δεν είναι regex με περισσότερα βήματα
- Ενσωματωμένα εργαλεία πράκτορα — η πλήρης λίστα ορισμάτων
- Αναζήτηση κώδικα με το γράφημα κώδικα — η πλευρά των συμβόλων και της επιρροής