Treci la conținut

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

Cod
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 \B ca 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 \G ră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 al vscode-textmate: un Registry rezolvă gramaticile după numele domeniului de aplicare (inclusiv referințe include, limbi încorporate și injecții), iar un Tokenizer menține stivele de reguli și domenii de aplicare, linie cu linie.
  • Temă (Shikiphp\Theme) reproduce potrivirea temelor din vscode-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.