Arhitectură
shikiphp transformă codul sursă în HTML evidențiat cu teme, folosind gramatici TextMate și teme VS Code, fără extensii PHP în afara json/mbstring, fără runtime Node și fără legături native Oniguruma. Această pagină rezumă procesul; contractul normativ complet se găsește în ARCHITECTURE.md din depozit.
Procesul de lucru
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>
Totul funcționează în spațiul de decalaje în unități de cod UTF-16 — același ca pentru motorul regex JS și tokenizerul Shiki. Pentru ASCII, acesta coincide cu decalajele în octeți.
Runtime-ul regex
La bază se află Shikiphp\Regex, un motor regex ECMAScript complet, pur-PHP, inclus din inline0/phasis (un motor PHP JavaScript pur). Analizează o sursă JS-RegExp și flagurile, apoi execută căutări înainte cu semantica Oniguruma onig_search.
Conversia Oniguruma → JS
Gramaticile TextMate sunt scrise în Oniguruma. Shikiphp\Oniguruma\PatternConverter este un port PHP al oniguruma-to-es: transformă un model Oniguruma într-o sursă și un șir de flaguri compatibile cu JS-RegExp, acceptate de motorul regex. Acesta este același traseu Oniguruma → RegExp pe care îl folosește motorul regex Shiki JavaScript, ceea ce permite lui shikiphp să se potrivească cu rezultatul motorului, decalaj cu decalaj. Convertorul gestionează construcțiile întâlnite în gramatici reale: clase POSIX, cuantificatori posesivi și grupuri atomice, flaguri inline, grupuri cu nume și referințe inverse, proprietăți Unicode și altele.
Scannerul și calea rapidă PCRE
Shikiphp\Oniguruma\OnigScanner reproduce scannerul vscode-oniguruma: este construit o dată pentru fiecare regulă dintr-o listă de modele și găsește potrivirea cea mai din stânga, la sau după o poziție de pornire.
Potrivitorul care parcurge arborele este corect, dar lent, așa că scannerul are o cale rapidă PCRE condiționată de echivalență, cu două niveluri. Un PcreTranslator rescrie sursa JS a convertorului într-un model PCRE nativ doar atunci când poate demonstra rezultatul:
- Potrivire completă. Când traducerea se comportă identic cu potrivitorul, inclusiv pentru fiecare captură, modelul rulează direct în PCRE. Grupurile cu nume, referințele inverse,
\p{},\b,\G, emularea atomică și capturile a căror semantică JavaScript diferă de PCRE (într-un grup cuantificat sau într-un lookbehind) sunt excluse din acest nivel. - Doar poziția. Când ar putea diferi doar capturile, o a doua traducere permite aceste construcții (
\bși\Bca lookaround-uri ASCII exacte, grupurile atomice ca(?>…)native,\p{…}dacă PCRE le compilează). PCRE găsește doar poziția următoarei potriviri, iar potrivitorul rulează din nou la acel decalaj pentru a produce capturile reale. Referințele inverse și\Grămân excluse și aici.
În toate celelalte cazuri se folosește doar potrivitorul. Pentru gramaticile incluse la Shiki 3.23.0, aproximativ 39% dintre cele 14,234 de modele distincte rulează complet în PCRE, aproximativ 57% folosesc nivelul bazat pe poziție, iar aproximativ 4% rămân la potrivitor. Un instrument de dezvoltare din depozit verifică clasificarea (bin/.oracle-tools/pcre-equivalence.php), comparând calea rapidă cu potrivitorul — indexul, sfârșitul și, pentru primul nivel, intervalul fiecărei capturi — pe intrările scenariilor și pe un set de șiruri sintetice, fără a permite nicio divergență.
Gramatică și temă
- Gramatică (
Shikiphp\Grammar) este un port fidel alvscode-textmate: unRegistryrezolvă gramaticile după numele domeniului de aplicare (inclusiv referințeinclude, limbi încorporate și injecții), iar unTokenizermenține stivele de reguli și domenii de aplicare, linie cu linie. - Temă (
Shikiphp\Theme) reproduce potrivirea temelor dinvscode-textmate: culoarea prim-planului și stilul fontului se rezolvă după specificitatea selectorului de domeniu de aplicare, cu potrivirea domeniilor părinte.
Randare
Shikiphp\Render\HtmlRenderer construiește arborele HAST exact pe care îl construiește pre.shiki > code > span.line > span Shiki, apoi îl serializează. Modul cu o singură temă scrie culori simple; modul cu două teme scrie variabile --shiki-light / --shiki-dark CSS.
Validat față de Shiki
Shiki este etalonul, fixat la shiki 3.23.0 împreună cu motorul regex JavaScript. Directorul scenarios/ conține 214 fixture-uri, fiecare reprezentând o intrare pentru o limbă și o temă; împreună acoperă 167 dintre cele 253 de gramatici incluse, lang: 'ansi' și toate cele 65 de teme. bin/test-regression le evidențiază pe fiecare cu ambele motoare și verifică dacă HTML este identic după eliminarea spațiilor albe de la început și sfârșit. Opțiunile și transformatoarele sunt verificate prin teste de paritate față de rezultate capturate din aceleași versiuni Shiki și @shikijs/transformers 4.2.0. Regula de bază este ca rezultatul evidențierii să nu regreseze față de Shiki.