
# المحوّلات

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

مرّرها عبر الخيار [`transformers`](https://shikiphp.inline0.dev/ar/docs/options#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` إلى السطر.

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

#### `NotationDiff`

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

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

#### `NotationFocus`

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

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

#### `NotationErrorLevel`

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

```js
throwSomething() // [!code error]
maybeRisky()     // [!code warning]
```

#### `NotationWordHighlight`

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

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

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

تقرأ هذه المحوّلات **سلسلة البيانات الوصفية** الخاصة بسياج الشيفرة، والممررة عبر الخيار
[`meta`](https://shikiphp.inline0.dev/ar/docs/options#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_`.
