Lewati ke konten

Arsitektur

shikiphp mengubah kode sumber menjadi HTML yang disorot dengan tema menggunakan grammar TextMate dan tema VS Code, tanpa ekstensi PHP di luar json/mbstring, tanpa runtime Node, dan tanpa binding native Oniguruma. Halaman ini merangkum pipeline; kontrak normatif lengkapnya ada di ARCHITECTURE.md dalam repositori.

Pipeline

Kode
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>

Semua beroperasi dalam ruang offset unit kode UTF-16 — ruang yang sama dengan mesin regex JS dan tokenizer milik Shiki. Untuk ASCII, nilainya sama dengan offset byte.

Runtime regex

Pada lapisan terbawah terdapat Shikiphp\Regex, mesin regex ECMAScript murni PHP lengkap yang disertakan dari vendor inline0/phasis (mesin PHP JavaScript murni). Mesin ini mengurai sumber RegExp JS beserta flag, lalu menjalankan pencarian maju dengan semantik Oniguruma onig_search.

Konversi Oniguruma → JS

Grammar TextMate ditulis dalam Oniguruma. Shikiphp\Oniguruma\PatternConverter adalah port PHP dari oniguruma-to-es: ini mengubah pola Oniguruma menjadi string sumber dan flag yang kompatibel dengan RegExp JS dan diterima mesin regex. Ini adalah jalur Oniguruma → RegExp yang sama yang digunakan mesin regex JavaScript milik Shiki, sehingga shikiphp dapat mencocokkan output mesin tersebut dengan tepat pada setiap offset. Converter menangani konstruksi nyata yang digunakan grammar: kelas POSIX, kuantifier posesif dan grup atomik, flag inline, grup bernama dan backreference, properti Unicode, dan lainnya.

Pemindai dan jalur cepat PCRE

Shikiphp\Oniguruma\OnigScanner meniru pemindai milik vscode-oniguruma: pemindai ini dibuat sekali untuk setiap aturan dari daftar pola dan menemukan kecocokan paling kiri pada atau setelah posisi awal.

Pencocok yang menelusuri pohon sudah benar, tetapi tidak cepat. Karena itu, pemindai memiliki jalur cepat PCRE yang hanya digunakan jika ekuivalen dengan dua tingkat. PcreTranslator mengubah sumber JS dari converter menjadi pola PCRE native hanya jika hasilnya dapat dibuktikan:

  • Cocok penuh. Jika terjemahan berperilaku identik dengan pencocok, termasuk setiap tangkapan, pola dijalankan langsung oleh PCRE. Grup bernama, referensi balik, \p{}, \b, \G, emulasi atomik, dan tangkapan dengan semantik JavaScript yang berbeda dari PCRE (di dalam grup terkuantifikasi atau lookbehind) ditolak dari tingkat ini.
  • Posisi saja. Jika hanya tangkapan yang mungkin berbeda, terjemahan kedua menerima konstruksi tersebut (\b dan \B sebagai lookaround ASCII yang persis, grup atomik sebagai (?>…) asli, \p{…} jika PCRE dapat mengompilasinya). PCRE kemudian hanya mencari posisi kecocokan berikutnya, lalu pencocok dijalankan ulang pada offset tersebut untuk menghasilkan tangkapan yang sebenarnya. Referensi balik dan \G tetap ditolak di sini.

Semua yang lain hanya dijalankan oleh pencocok. Untuk grammar bawaan pada Shiki 3.23.0, sekitar 39% dari 14,234 pola berbeda dijalankan sepenuhnya dengan PCRE, sekitar 57% menggunakan tingkat posisi, dan sekitar 4% tetap menggunakan pencocok. Klasifikasi ini diperiksa oleh alat pengembangan di repositori (bin/.oracle-tools/pcre-equivalence.php) yang membandingkan jalur cepat dengan pencocok — indeks, akhir, serta setiap rentang hasil tangkapan untuk tingkat pertama — pada input skenario dan serangkaian string sintetis, tanpa toleransi terhadap perbedaan.

Grammar dan tema

  • Grammar (Shikiphp\Grammar) adalah port setia dari vscode-textmate: Registry menyelesaikan grammar berdasarkan nama scope (termasuk referensi include, bahasa tertanam, dan injeksi), sedangkan Tokenizer mengelola tumpukan aturan dan scope baris demi baris.
  • Tema (Shikiphp\Theme) mem-port pencocokan tema vscode-textmate: latar depan dan gaya font ditentukan berdasarkan kekhususan pemilih scope dengan pencocokan scope induk.

Render

Shikiphp\Render\HtmlRenderer membangun pohon HAST pre.shiki > code > span.line > span yang persis seperti yang dibangun Shiki, lalu menserialisasikannya. Mode tema tunggal menulis warna polos; mode tema ganda menulis variabel --shiki-light / --shiki-dark CSS.

Divalidasi terhadap Shiki

Shiki sendiri adalah oracle, dipatok ke shiki 3.23.0 dengan mesin regex JavaScript-nya. Direktori scenarios/ berisi 214 fixture, masing-masing berupa satu input dalam satu bahasa dan satu tema; secara keseluruhan, fixture ini mencakup 167 dari 253 grammar bawaan, lang: 'ansi', dan seluruh 65 tema. bin/test-regression menyorot masing-masing dengan kedua mesin dan memastikan HTML identik setelah spasi awal dan akhir dihapus. Opsi dan transformer diperiksa melalui pengujian kesetaraan terhadap output yang diambil dari Shiki dan @shikijs/transformers 4.2.0 yang sama. Aturan yang tidak dapat ditawar adalah output penyorotan tidak boleh mengalami regresi terhadap Shiki.