Przejdź do treści

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

Kod
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 (\b i \B jako 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 \G ró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 port vscode-textmate: Registry rozpoznaje gramatyki według nazwy zakresu (w tym odwołania include, osadzone języki i wstrzyknięcia), a Tokenizer zarządza stosami reguł i zakresów wiersz po wierszu.
  • Motyw (Shikiphp\Theme) przenosi mechanizm dopasowywania motywów z vscode-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.