Architektura
shikiphp zamienia kod źródłowy na podświetlony kod HTML przy użyciu gramatyk TextMate
i motywów VS Code, bez rozszerzeń PHP poza json/mbstring, bez środowiska uruchomieniowego Node
i natywnego powiązania Oniguruma. Ta strona podsumowuje potok; pełna specyfikacja znajduje się w pliku ARCHITECTURE.md w repozytorium.
Potok przetwarzania
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>
Wszystkie operacje używają offsetów w jednostkach kodowych UTF-16 — tak samo jak silnik regex JS i tokenizator Shiki. Dla ASCII odpowiadają one offsetom bajtów.
Środowisko uruchomieniowe regex
U podstaw znajduje się Shikiphp\Regex — kompletny silnik regex ECMAScript napisany wyłącznie w PHP,
dołączony do pakietu z inline0/phasis (silnik JavaScript napisany wyłącznie w PHP). Analizuje źródło RegExp JS wraz z flagami i wykonuje wyszukiwanie w przód z semantyką Oniguruma onig_search.
Konwersja Oniguruma → JS
Gramatyki TextMate są zapisane w Oniguruma. Shikiphp\Oniguruma\PatternConverter
to PHP portu
oniguruma-to-es: konwertuje wzorzec
Oniguruma na źródło i ciąg flag zgodne z RegExp JS, akceptowane przez silnik regex. Tę samą ścieżkę Oniguruma → RegExp wykorzystuje silnik regex JavaScriptShiki, co pozwala shikiphp dopasować jego wynik z dokładnością do offsetu. Konwerter obsługuje konstrukcje używane w rzeczywistych gramatykach: klasy POSIX, kwantyfikatory zachłanne i grupy atomowe, flagi inline, nazwane grupy i odwołania wsteczne, właściwości Unicode i inne.
Skaner i szybka ścieżka PCRE
Shikiphp\Oniguruma\OnigScanner działa tak jak skaner vscode-oniguruma: jest budowany
raz dla każdej reguły z listy wzorców i znajduje najwcześniejsze dopasowanie na pozycji początkowej lub dalej.
Dopasowywanie przez przechodzenie drzewa jest poprawne, ale wolne, dlatego skaner ma
szybką ścieżkę PCRE z gwarancją równoważności, działającą na dwóch poziomach. PcreTranslator przekształca
źródło JS konwertera na natywny wzorzec PCRE tylko wtedy, gdy może dowieść poprawności wyniku:
- Pełne dopasowanie. Gdy tłumaczenie zachowuje się identycznie jak dopasowywanie,
łącznie ze wszystkimi przechwyceniami, wzorzec jest wykonywany bezpośrednio przez PCRE. Nazwane grupy,
odwołania wsteczne,
\p{},\b,\G, emulacja grup atomowych oraz przechwycenia, których semantyka JavaScript różni się od PCRE (wewnątrz grupy z kwantyfikatorem lub wyrażenia sprawdzającego od tyłu), nie są dopuszczane na tym poziomie. - Tylko pozycja. Gdy mogą różnić się tylko przechwycenia, drugie tłumaczenie
dopuszcza te konstrukcje (
\bi\Bjako dokładne asercje wyprzedzające i wsteczne ASCII, grupy atomowe jako natywne(?>…),\p{…}, jeśli PCRE je kompiluje). PCRE znajduje wtedy tylko pozycję następnego dopasowania, a mechanizm dopasowujący ponownie uruchamia się od tego offsetu, aby wygenerować właściwe przechwycenia. Odwołania wsteczne i\Grównież są tu odrzucane.
Pozostałe przypadki obsługuje wyłącznie mechanizm dopasowujący. Dla gramatyk dołączonych w Shiki
3.23.0 około 39% spośród 14,234 różnych wzorców jest w całości wykonywanych przez PCRE, około 57%
korzysta z poziomu pozycjonowania, a około 4% pozostaje przy mechanizmie dopasowującym. Klasyfikację
sprawdza narzędzie deweloperskie w repozytorium
(bin/.oracle-tools/pcre-equivalence.php), które porównuje szybką ścieżkę z mechanizmem dopasowującym — indeks, koniec i, dla pierwszego poziomu, każdy zakres przechwycenia — dla zestawu scenariuszy i syntetycznych ciągów; rozbieżności nie są dopuszczalne.
Gramatyka i motyw
- Gramatyka (
Shikiphp\Grammar) to wierny portvscode-textmate:Registryrozpoznaje gramatyki według nazwy zakresu (w tym odwołaniainclude, osadzone języki i wstrzyknięcia), aTokenizerzarządza stosami reguł i zakresów wiersz po wierszu. - Motyw (
Shikiphp\Theme) przenosi mechanizm dopasowywania motywów zvscode-textmate: kolor pierwszego planu i styl czcionki są dobierane według szczegółowości selektora zakresu z uwzględnieniem dopasowania zakresu nadrzędnego.
Renderowanie
Shikiphp\Render\HtmlRenderer buduje dokładnie takie samo drzewo HAST pre.shiki > code > span.line > span jak Shiki, a następnie je serializuje. Tryb jednego motywu zapisuje zwykłe kolory; tryb dwóch motywów zapisuje zmienne --shiki-light / --shiki-dark CSS.
Walidacja względem Shiki
Samo Shiki jest wzorcem odniesienia, przypiętym do shiki 3.23.0 z silnikiem regex JavaScript. Katalog scenarios/ zawiera 214 zestawów testowych; każdy obejmuje jedno wejście w jednym języku i jednym motywie. Łącznie pokrywają 167 z 253 dołączonych gramatyk, lang: 'ansi' oraz wszystkie 65 motywów. bin/test-regression podświetla każde wejście za pomocą obu silników i sprawdza, czy wynik HTML jest identyczny po usunięciu białych znaków z początku i końca. Opcje i transformatory są sprawdzane testami zgodności względem wyników zapisanych z tych samych wersji Shiki i @shikijs/transformers 4.2.0. Wynik podświetlania nie może być gorszy niż w Shiki.