본문으로 건너뛰기

아키텍처

shikiphp는 TextMate 문법과 VS Code 테마를 사용해 소스 코드를 테마가 적용된 하이라이트 HTML로 변환합니다. json/mbstring 외의 PHP 확장, Node 런타임, 네이티브 Oniguruma 바인딩이 필요 없습니다. 이 페이지는 파이프라인을 요약하며, 저장소의 ARCHITECTURE.md에 전체 규범 계약이 있습니다.

파이프라인

코드
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>

모든 작업은 UTF-16 코드 단위 오프셋 공간에서 이루어집니다. JS 정규식 엔진 및 Shiki의 토크나이저와 같은 공간입니다. ASCII에서는 바이트 오프셋과 같습니다.

정규식 런타임

가장 아래 계층에는 완전한 순수 PHP ECMAScript 정규식 엔진인 Shikiphp\Regex가 있습니다. inline0/phasis에서 벤더링됨(순수 PHP JavaScript 엔진)입니다. JS-RegExp 소스와 플래그를 파싱하고 Oniguruma onig_search 시맨틱으로 정방향 검색을 수행합니다.

Oniguruma → JS 변환

TextMate 문법은 Oniguruma로 작성됩니다. Shikiphp\Oniguruma\PatternConverter는 PHP 포트로, oniguruma-to-es입니다. Oniguruma 패턴을 정규식 엔진이 허용하는 JS 호환 소스와 플래그 문자열로 변환합니다. 이는 Shiki의 JavaScript 정규식 엔진이 사용하는 것과 동일한 Oniguruma → RegExp 경로이며, 덕분에 shikiphp는 해당 엔진의 출력과 오프셋 단위까지 일치시킬 수 있습니다. 변환기는 문법에서 실제로 사용하는 POSIX 클래스, 탐욕적 수량자와 원자 그룹, 인라인 플래그, 이름 지정 그룹과 역참조, Unicode 속성 등을 처리합니다.

스캐너와 PCRE 빠른 경로

Shikiphp\Oniguruma\OnigScanner는 vscode-oniguruma의 스캐너를 따릅니다. 패턴 목록에서 규칙마다 한 번 생성되며 시작 위치 이후 또는 그 위치에서 가장 왼쪽에 있는 일치 항목을 찾습니다.

트리 순회 매처는 정확하지만 느리므로, 스캐너는 동등성이 확인된 PCRE 빠른 경로를 두 단계로 제공합니다. PcreTranslator는 결과를 보장할 수 있는 경우에만 변환기의 JS 소스를 네이티브 PCRE 패턴으로 다시 작성합니다:

  • 전체 일치. 모든 캡처를 포함해 변환 결과가 매처와 동일하게 작동하면 패턴을 PCRE에서 바로 실행합니다. 이름 지정 그룹, 역참조, \p{}, \b, \G, 원자 그룹 에뮬레이션, 그리고 JavaScript 시맨틱이 PCRE와 다른 캡처(수량화 그룹이나 후방 탐색 내부)는 이 단계에서 제외됩니다.
  • 위치만 확인. 캡처만 달라질 수 있는 경우에는 두 번째 변환을 통해 해당 구문을 허용합니다(\b 및 \B는 정확한 ASCII 후방 탐색으로, 원자 그룹은 네이티브 (?>…)로, \p{…}는 PCRE가 컴파일할 수 있는 경우). 이때 PCRE는 다음 일치 위치만 찾고, 실제 캡처를 생성하기 위해 매처가 그 오프셋에서 다시 실행됩니다. 역참조와 \G는 이 단계에서도 제외됩니다.

나머지는 모두 매처만 사용합니다. 번들 문법의 Shiki 3.23.0에서 서로 다른 14,234개 패턴 중 약 39%는 PCRE 전체 일치 단계에서 실행되고, 약 57%는 위치 단계에서 실행되며, 약 4%는 매처에 남습니다. 저장소의 개발 도구(bin/.oracle-tools/pcre-equivalence.php)가 시나리오 입력과 합성 문자열을 대상으로 빠른 경로와 매처의 인덱스, 끝 위치, 그리고 첫 번째 단계의 모든 캡처 범위를 비교해 분류를 확인합니다. 차이는 허용되지 않습니다.

문법 및 테마

  • 문법(Shikiphp\Grammar)은 vscode-textmate의 충실한 포트입니다. Registry는 스코프 이름으로 문법을 확인하며(include 참조, 포함 언어, 인젝션 포함), Tokenizer는 줄마다 규칙 및 스코프 스택을 관리합니다.
  • 테마(Shikiphp\Theme)는 vscode-textmate의 테마 일치 방식을 포팅합니다. 전경색과 글꼴 스타일은 상위 스코프 일치를 포함해 스코프 선택자의 구체성에 따라 결정됩니다.

렌더링

Shikiphp\Render\HtmlRenderer는 Shiki가 만드는 것과 정확히 같은 pre.shiki > code > span.line > span HAST 트리를 만든 다음 직렬화합니다. 단일 테마 모드에서는 일반 색상을 기록하고, 이중 테마 모드에서는 --shiki-light / --shiki-dark CSS 변수를 기록합니다.

Shiki 기준 검증

Shiki 자체가 기준이며, JavaScript 정규식 엔진을 사용하는 shiki 3.23.0 버전을 고정해 검증합니다. scenarios/ 디렉터리에는 214개의 픽스처가 있으며, 각각 하나의 입력, 언어, 테마로 구성됩니다. 이 픽스처는 번들 문법 253개 중 167개, lang: 'ansi', 그리고 테마 65개 전체를 다룹니다. bin/test-regression는 각 항목을 두 엔진으로 하이라이트하고, 앞뒤 공백을 제거한 뒤 HTML가 동일한지 확인합니다. 옵션과 변환기는 동일한 Shiki 및 @shikijs/transformers 4.2.0 버전에서 캡처한 출력과 비교하는 패리티 테스트로 확인합니다. 하이라이팅 출력은 Shiki 기준으로 퇴행해서는 안 됩니다.