Αρχιτεκτονική
Το shikiphp μετατρέπει τον πηγαίο κώδικα σε επισημασμένο με θέμα HTML, χρησιμοποιώντας γραμματικές TextMate και θέματα VS Code, χωρίς επεκτάσεις PHP πέρα από τα json/mbstring, χωρίς runtime Node και χωρίς εγγενή σύνδεση Oniguruma. Αυτή η σελίδα συνοψίζει τη ροή επεξεργασίας· η πλήρης κανονιστική σύμβαση βρίσκεται στο ARCHITECTURE.md του αποθετηρίου.
Η ροή επεξεργασίας
source code
└─ Highlighter::codeToTokens()
├─ Grammar\Registry loads the grammar (+ its includes / embedded langs)
├─ Grammar\Tokenizer line by line, driving an OnigScanner per rule state
│ └─ Oniguruma\OnigScanner.findNextMatch()
│ └─ Oniguruma\PatternConverter Oniguruma pattern → JS regex
│ └─ Shikiphp\Regex (Parser + Matcher) JS RegExp runtime
├─ Theme\Theme.match(scopeStack) → StyleAttributes per token
└─ Render\ThemedToken[] (text + style) per line
└─ Render\HtmlRenderer → <pre class="shiki">…</pre>
Όλες οι λειτουργίες χρησιμοποιούν μετατοπίσεις σε μονάδες κώδικα UTF-16 — το ίδιο σύστημα με τη μηχανή regex JS και τον tokenizer του Shiki. Για ASCII αυτό αντιστοιχεί σε μετατοπίσεις byte.
Το runtime regex
Στη βάση βρίσκεται το Shikiphp\Regex, μια πλήρης μηχανή regex ECMAScript pure-PHP, ενσωματωμένη από το inline0/phasis (μηχανή JavaScript pure-PHP). Αναλύει μια πηγή JS-RegExp μαζί με flags και εκτελεί αναζητήσεις προς τα εμπρός με σημασιολογία Oniguruma onig_search.
Μετατροπή Oniguruma → JS
Οι γραμματικές TextMate είναι γραμμένες σε Oniguruma. Το Shikiphp\Oniguruma\PatternConverter είναι μια υλοποίηση PHP του oniguruma-to-es: μετατρέπει ένα μοτίβο Oniguruma σε πηγή και συμβολοσειρά flags συμβατά με JS-RegExp, τα οποία δέχεται η μηχανή regex. Αυτή είναι η ίδια διαδρομή Oniguruma → RegExp που ακολουθεί η μηχανή regex του Shiki JavaScript και επιτρέπει στο shikiphp να αντιστοιχίζει την έξοδο αυτής της μηχανής με ακρίβεια σε κάθε μετατόπιση. Ο μετατροπέας υποστηρίζει τις δομές που χρησιμοποιούνται στην πράξη από τις γραμματικές: κλάσεις POSIX, possessive quantifiers και atomic groups, inline flags, named groups και backreferences, ιδιότητες Unicode και άλλα.
Ο σαρωτής και η γρήγορη διαδρομή PCRE
Το Shikiphp\Oniguruma\OnigScanner αντιστοιχεί στον σαρωτή του vscode-oniguruma: δημιουργείται μία φορά ανά κανόνα από μια λίστα μοτίβων και βρίσκει την αριστερότερη αντιστοιχία στη θέση έναρξης ή μετά από αυτήν.
Η αντιστοίχιση με διάσχιση του δέντρου είναι σωστή αλλά αργή, γι' αυτό ο σαρωτής διαθέτει γρήγορη διαδρομή PCRE με έλεγχο ισοδυναμίας σε δύο επίπεδα. Ένα PcreTranslator ξαναγράφει την πηγή JS του μετατροπέα ως εγγενές μοτίβο PCRE μόνο όταν μπορεί να αποδείξει το αποτέλεσμα:
- Πλήρης αντιστοιχία. Όταν η μετάφραση συμπεριφέρεται πανομοιότυπα με τον matcher, συμπεριλαμβανομένων όλων των captures, το μοτίβο εκτελείται απευθείας σε PCRE. Τα named groups, τα backreferences, τα
\p{},\b,\G, η προσομοίωση atomic groups και τα captures των οποίων η σημασιολογία JavaScript διαφέρει από την PCRE (μέσα σε ποσοτικοποιημένο group ή lookbehind) αποκλείονται από αυτό το επίπεδο. - Μόνο θέση. Όταν ενδέχεται να διαφέρουν μόνο τα captures, μια δεύτερη μετάφραση επιτρέπει αυτές τις δομές (τα
\bκαι\Bως ακριβή ASCII lookarounds, τα atomic groups ως εγγενή(?>…), το\p{…}όταν το μεταγλωττίζει η PCRE). Έτσι, η PCRE βρίσκει μόνο τη θέση της επόμενης αντιστοιχίας και ο matcher εκτελεί ξανά την αντιστοίχιση σε αυτή τη μετατόπιση για να παράγει τα πραγματικά captures. Τα backreferences και το\Gπαραμένουν επίσης αποκλεισμένα.
Όλα τα υπόλοιπα εκτελούνται μόνο από τον matcher. Για τις ενσωματωμένες γραμματικές στο Shiki 3.23.0, περίπου το 39% των 14,234 διαφορετικών μοτίβων εκτελείται πλήρως σε PCRE, περίπου το 57% χρησιμοποιεί το επίπεδο θέσης και περίπου το 4% παραμένει στον matcher. Η ταξινόμηση ελέγχεται από ένα εργαλείο ανάπτυξης στο αποθετήριο (bin/.oracle-tools/pcre-equivalence.php), το οποίο συγκρίνει τη γρήγορη διαδρομή με τον matcher — δείκτη, τέλος και, για το πρώτο επίπεδο, κάθε εύρος capture — στα σενάρια δοκιμής και σε συνθετικές συμβολοσειρές, χωρίς να επιτρέπονται αποκλίσεις.
Γραμματική και θέμα
- Γραμματική (
Shikiphp\Grammar) είναι πιστή μεταφορά τουvscode-textmate: έναRegistryεπιλύει γραμματικές με βάση το όνομα scope (συμπεριλαμβανομένων των αναφορώνinclude, των ενσωματωμένων γλωσσών και των injections) και έναTokenizerδιατηρεί τις στοίβες κανόνων και scope ανά γραμμή. - Θέμα (
Shikiphp\Theme) μεταφέρει την αντιστοίχιση θεμάτων τουvscode-textmate: το χρώμα προσκηνίου και το στυλ γραμματοσειράς επιλύονται βάσει της ειδικότητας του scope selector και της αντιστοίχισης γονικών scope.
Απόδοση
Το Shikiphp\Render\HtmlRenderer δημιουργεί ακριβώς το ίδιο δέντρο HAST pre.shiki > code > span.line > span που δημιουργεί το Shiki και, στη συνέχεια, το σειριοποιεί. Η λειτουργία ενός θέματος γράφει απλά χρώματα· η λειτουργία δύο θεμάτων γράφει μεταβλητές --shiki-light / --shiki-dark CSS.
Επικυρωμένο σε σύγκριση με το Shiki
Το ίδιο το Shiki αποτελεί το σημείο αναφοράς, με σταθερή έκδοση shiki 3.23.0 και τη μηχανή regex JavaScript. Ο κατάλογος scenarios/ περιλαμβάνει 214 fixtures, καθένα με μία είσοδο, μία γλώσσα και ένα θέμα· συνολικά καλύπτουν 167 από τις 253 ενσωματωμένες γραμματικές, lang: 'ansi' και τα 65 θέματα. Το bin/test-regression επισημαίνει καθεμία με τις δύο μηχανές και επιβεβαιώνει ότι η συμβολοσειρά HTML είναι πανομοιότυπη αφού αφαιρεθούν τα κενά στην αρχή και στο τέλος. Οι επιλογές και οι transformers ελέγχονται με δοκιμές ισοδυναμίας έναντι εξόδου που καταγράφηκε από τις ίδιες εκδόσεις Shiki και @shikijs/transformers 4.2.0. Ο απαράβατος κανόνας είναι να μην υποβαθμίζεται η έξοδος επισήμανσης σε σχέση με το Shiki.