انتقل إلى المحتوى

المحوّلات

تتيح لك المحوّلات إعادة تشكيل المخرجات المظللة في كل مرحلة — من السلسلة المصدرية الخام، مرورًا بشبكة الرموز، وصولًا إلى شجرة HAST النهائية وHTML. ينقل shikiphp نموذج المحوّلات في Shiki بخطافاته كما هي، لذا تعمل المحوّلات بالطريقة نفسها التي تعمل بها نظيراتها في JavaScript.

مرّرها عبر الخيار transformers:

PHP
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\Notation\NotationHighlight;
use Shikiphp\Transformer\Notation\NotationDiff;

$code = <<<'JS'
console.log('this line is highlighted') // [!code highlight]
console.log('old line') // [!code --]
console.log('new line') // [!code ++]
JS;

echo Shikiphp::codeToHtml($code, [
    'lang'         => 'js',
    'theme'        => 'vitesse-dark',
    'transformers' => [
        new NotationHighlight(),
        new NotationDiff(),
    ],
]);

مسار المعالجة

كل محوّل كائن يطبّق Shikiphp\Transformer\Transformer. ينفّذ المسار الخطافات بالترتيب أثناء بناء كتلة واحدة:

الخطاف التوقيع التوقيت
preprocess (string $code, array &$options, $context): ?string قبل تقسيم النص إلى رموز؛ وقد يعيد كتابة المصدر ويعدّل الخيارات
tokens (array $tokens, $context): ?array بعد تقسيم النص إلى رموز، على شبكة ThemedToken ثنائية الأبعاد
root (Element $el, $context): ?Element على جذر HAST
pre (Element $el, $context): ?Element على العنصر <pre>
code (Element $el, $context): ?Element على العنصر <code>
line (Element $el, int $line, $context): ?Element لكل عنصر سطر (يبدأ الترقيم من 1)
span (Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element لكل نطاق رمز
postprocess (string $html, array $options, $context): ?string على سلسلة HTML النهائية المتسلسلة

كل الخطافات اختيارية. استبدال القيمة بالنتيجة يحدث عند إرجاع قيمة؛ أما إرجاع null فيُبقيها دون تغيير.

يتحكم enforce() في ترتيب المحوّلات: يُنفّذ 'pre' أولًا، و'post' أخيرًا، بينما يُنفّذ null (الافتراضي) بينهما حسب ترتيب التسجيل.

كتابة محوّل

وسّع Shikiphp\Transformer\AbstractTransformer لتجاوز الخطافات التي تحتاج إليها فقط؛ أما البقية فتبقى دون عمليات. يتيح سياق المحوّل addClassToHast() لإضافة أصناف إلى العناصر.

PHP
use Shikiphp\Hast\Element;
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\AbstractTransformer;
use Shikiphp\Transformer\TransformerContext;

final class AddLanguageClass extends AbstractTransformer
{
    public function pre(Element $element, TransformerContext $context): ?Element
    {
        if ($context->lang !== null) {
            $context->addClassToHast($element, 'language-' . $context->lang);
        }

        return $element;
    }
}

echo Shikiphp::codeToHtml('const answer = 42', [
    'lang'         => 'js',
    'theme'        => 'nord',
    'transformers' => [new AddLanguageClass()],
]);
// <pre class="shiki nord language-js" …>

المحوّلات المضمّنة

محوّلات الترميز

تقرأ هذه المحوّلات تعليقات // [!code …] خاصة في المصدر (ويُكتشف تركيب التعليقات الخاص باللغة تلقائيًا) وتحولها إلى أصناف، مع إزالة التعليق نفسه. وهي نُسخ مباشرة من مجموعة ترميز @shikijs/transformers.

NotationHighlight

Shikiphp\Transformer\Notation\NotationHighlight — تضيف // [!code highlight] (أو // [!code hl]) الصنف highlighted إلى السطر.

JavaScript
console.log('kept plain')
console.log('this line is highlighted') // [!code highlight]

NotationDiff

Shikiphp\Transformer\Notation\NotationDiff — يحدد // [!code ++] السطر بوصفه مضافًا (diff add)، ويحدده // [!code --] بوصفه محذوفًا (diff remove).

JavaScript
console.log('old line') // [!code --]
console.log('new line') // [!code ++]

NotationFocus

Shikiphp\Transformer\Notation\NotationFocus — يضيف // [!code focus] الصنف focused.

JavaScript
console.log('focus on me') // [!code focus]

NotationErrorLevel

Shikiphp\Transformer\Notation\NotationErrorLevel — تضيف // [!code error] و// [!code warning] و// [!code info] الصنف highlighted بالإضافة إلى error / warning / info.

JavaScript
throwSomething() // [!code error]
maybeRisky()     // [!code warning]

NotationWordHighlight

Shikiphp\Transformer\Notation\NotationWordHighlight — // [!code word:foo] (اختياريًا // [!code word:foo:2] للحد من ذلك إلى الأسطر N التالية) يغلّف كل ظهور لـ foo في عنصر span يحمل highlighted-word.

JavaScript
// [!code word:config]
const config = loadConfig()

محوّلات البيانات الوصفية

تقرأ هذه المحوّلات سلسلة البيانات الوصفية الخاصة بسياج الشيفرة، والممررة عبر الخيار meta (meta['__raw'])، كما يقرأ Shiki النص الذي يلي لغة كتلة شيفرة محاطة بسياج.

MetaHighlight

Shikiphp\Transformer\MetaHighlight — يبرز الأسطر المذكورة في نطاق بيانات وصفية مثل {1,3-5}، ويضيف الصنف highlighted.

PHP
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\MetaHighlight;

$code = "const a = 1\nconst b = 2\nconst c = 3\nconst d = 4\nconst e = 5";

echo Shikiphp::codeToHtml($code, [
    'lang'         => 'js',
    'theme'        => 'nord',
    'meta'         => ['__raw' => '{1,3-5}'],
    'transformers' => [new MetaHighlight()],
]);

MetaWordHighlight

Shikiphp\Transformer\MetaWordHighlight — يبرز الكلمات المذكورة في سلسلة البيانات الوصفية بصيغة /word/، ويضيف highlighted-word.

المسافات البيضاء والبنية

RenderWhitespace

Shikiphp\Transformer\RenderWhitespace — يغلّف محارف المسافة وعلامة الجدولة في عناصر span (الصنفان space وtab افتراضيًا) لعرضها بوضوح. يقبل المُنشئ (classSpace, classTab, position) حيث تكون القيمة position هي 'all' (افتراضيًا) أو 'boundary' أو 'trailing' أو 'leading'.

CompactLineOptions

Shikiphp\Transformer\CompactLineOptions — يطبّق أصنافًا على أسطر محددة من قائمة تضم عناصر {line, classes}.

PHP
use Shikiphp\Transformer\CompactLineOptions;

$transformer = new CompactLineOptions([
    ['line' => 1, 'classes' => 'highlighted'],
    ['line' => 3, 'classes' => ['highlighted', 'error']],
]);

تبدأ أرقام الأسطر من 1. مرّر الكائن في الخيار transformers مثل أي محوّل آخر.

RemoveNotationEscape

Shikiphp\Transformer\RemoveNotationEscape — يفكّ تهريب صيغة [\!code …] المستخدمة لكتابة تعليق ترميز حرفي من دون تشغيل محوّل الترميز.

RemoveLineBreak

Shikiphp\Transformer\RemoveLineBreak — يزيل عقد النص التي تحتوي على فواصل الأسطر بين الأسطر (transformerRemoveLineBreak في Shiki).

StyleToClass

Shikiphp\Transformer\StyleToClass — ينقل الأنماط المضمّنة إلى أصناف منشأة ويجمع ورقة أنماط تسترجعها باستخدام getCSS(). تستند أسماء الأصناف إلى تجزئة النمط باستخدام cyrb53 في Shiki، لذا تكون المخرجات مطابقة بايتًا ببايت لمخرجات @shikijs/transformers 4.2.0، بما في ذلك النطاق:

  • يصبح نمط <pre> دائمًا صنفًا.
  • لا تُحوّل نطاقات الرموز إلا إذا حملت نمطًا مُسبق الإنشاء، وهو ما يحدث مع خريطة themes. أما مع theme مفرد، فتحتفظ نطاقات الرموز بنمطها المضمّن style="color:…"، كما في Shiki.
PHP
use Shikiphp\Shikiphp;
use Shikiphp\Transformer\StyleToClass;

$code = 'const answer = 42';
$styleToClass = new StyleToClass();

$html = Shikiphp::codeToHtml($code, [
    'lang'         => 'js',
    'themes'       => ['light' => 'github-light', 'dark' => 'github-dark'],
    'transformers' => [$styleToClass],
]);

$css = $styleToClass->getCSS(); // collected stylesheet for the classes above

يقبل المُنشئ (classPrefix, classSuffix, classReplacer)؛ وتكون البادئة الافتراضية __shiki_.