Přejít k obsahu

Architektura

shikiphp převádí zdrojový kód na zvýrazněné HTML podle motivu pomocí gramatik TextMate a motivů VS Code, bez rozšíření PHP mimo json/mbstring, bez běhového prostředí Node a bez nativní vazby Oniguruma. Tato stránka shrnuje postup; úplná závazná specifikace je v souboru ARCHITECTURE.md v repozitáři.

Postup zpracování

Kód
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>

Vše probíhá v prostoru offsetů UTF-16 kódových jednotek — stejném jako v regulárním výrazovém modulu JS a tokenizeru Shiki. U znaků ASCII se shoduje s bajtovými offsety.

Běhové prostředí regulárních výrazů

Na nejnižší vrstvě je Shikiphp\Regex, kompletní čistě PHP engine regulárních výrazů ECMAScriptu, převzatý z inline0/phasis (engine čistě PHP JavaScript). Zpracovává zdrojový kód JS-RegExp spolu s příznaky a provádí vyhledávání dopředu se sémantikou Oniguruma onig_search.

Převod Oniguruma → JS

Gramatiky TextMate jsou napsány v Oniguruma. Shikiphp\Oniguruma\PatternConverter je port PHP nástroje oniguruma-to-es: převádí vzor Oniguruma na zdrojový kód a řetězec příznaků kompatibilní s JS-RegExp, které engine regulárních výrazů přijímá. Jde o stejnou cestu Oniguruma → RegExp, kterou používá engine regulárních výrazů ShikiJavaScript, a díky tomu může shikiphp porovnávat svůj výstup s výstupem tohoto enginu offset po offsetu. Převodník podporuje konstrukce používané v reálných gramatikách: třídy POSIX, hladové kvantifikátory a atomické skupiny, vložené příznaky, pojmenované skupiny a zpětné odkazy, vlastnosti Unicode a další.

Skener a jeho rychlá cesta PCRE

Shikiphp\Oniguruma\OnigScanner kopíruje skener vscode-oniguruma: pro každé pravidlo se jednou sestaví ze seznamu vzorů a hledá nejlevější shodu na zadané pozici nebo za ní.

Matcher procházející strom je správný, ale pomalý, proto má skener rychlou cestu PCRE ověřenou ekvivalencí ve dvou úrovních. PcreTranslator přepisuje zdrojový kód JS z převodníku na nativní vzor PCRE jen tehdy, když může dokázat výsledek:

  • Úplná shoda. Pokud se překlad chová stejně jako porovnávač, včetně všech zachycených skupin, vzor se spustí přímo v PCRE. Pojmenované skupiny, zpětné odkazy, \p{}, \b, \G, emulace atomických skupin a zachycené skupiny, jejichž sémantika JavaScript se liší od PCRE (uvnitř kvantifikované skupiny nebo v lookbehind), jsou z této úrovně vyloučeny.
  • Pouze pozice. Pokud se mohou lišit jen zachycené skupiny, druhý překlad připouští tyto konstrukce (\b a \B jako přesné ASCII lookaroundy, atomické skupiny jako nativní (?>…), \p{…}, pokud je PCRE zkompiluje). PCRE pak pouze najde pozici další shody a porovnávač spustí znovu na tomto offsetu, aby vytvořil skutečné zachycené skupiny. Zpětné odkazy a \G jsou i zde vyloučeny.

Vše ostatní zpracovává pouze matcher. U sdružených gramatik v Shiki 3.23.0 běží přibližně 39 % z 14,234 jedinečných vzorů plně v PCRE, přibližně 57 % používá úroveň určující pouze pozici a přibližně 4 % zůstávají u matcheru. Klasifikaci ověřuje vývojářský nástroj v repozitáři (bin/.oracle-tools/pcre-equivalence.php), který porovnává rychlou cestu s matcherem — index, konec a u první úrovně i rozsah každé zachycené skupiny — na scénářových vstupech a sadě syntetických řetězců; povoleny nejsou žádné rozdíly.

Gramatika a motiv

  • Gramatika (Shikiphp\Grammar) je přesný port vscode-textmate: Registry vyhledává gramatiky podle názvu oboru (včetně odkazů include, vložených jazyků a injekcí) a Tokenizer udržuje zásobník pravidel a oborů po jednotlivých řádcích.
  • Motiv (Shikiphp\Theme) přenáší způsob přiřazování motivů z vscode-textmate: barva popředí a styl písma se určují podle konkrétnosti selektoru oboru a shody s nadřazenými obory.

Vykreslování

Shikiphp\Render\HtmlRenderer vytvoří přesně takový strom HAST pre.shiki > code > span.line > span, jaký vytváří Shiki, a poté jej serializuje. Režim jednoho motivu zapisuje běžné barvy; režim dvou motivů zapisuje proměnné --shiki-light / --shiki-dark CSS.

Ověřeno vůči Shiki

Shiki samo slouží jako referenční zdroj a je připnuté k verzi shiki 3.23.0 s jeho modulem regulárních výrazů JavaScript. Adresář scenarios/ obsahuje 214 testovacích podkladů, přičemž každý představuje jeden vstup v jednom jazyce a jednom motivu; dohromady pokrývají 167 z 253 přibalených gramatik, lang: 'ansi' a všech 65 motivů. bin/test-regression zvýrazní každý podklad oběma enginy a ověří, že HTML jsou po oříznutí počátečních a koncových mezer totožné. Možnosti a transformátory se kontrolují testy shody s výstupem zachyceným ze stejného Shiki a @shikijs/transformers 4.2.0. Nepřekročitelným pravidlem je, že se výstup zvýrazňování nesmí oproti Shiki zhoršit.