
# Transformers

Met transformers kunt u de gemarkeerde uitvoer in elke fase aanpassen — van de ruwe bronstring, via het tokenraster tot de uiteindelijke HAST-boom en HTML. shikiphp neemt het transformer-model van Shiki hook voor hook over, zodat transformers hetzelfde werken als hun tegenhangers in JavaScript.

Geef ze door via de optie [`transformers`](https://shikiphp.inline0.dev/nl/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(),
    ],
]);
```

## De pipeline

Elke transformer is een object dat `Shikiphp\Transformer\Transformer` implementeert.
De pipeline voert de hooks in volgorde uit tijdens het opbouwen van één blok:

| Hook | Handtekening | Wanneer |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | vóór tokenisatie; kan de bron herschrijven en opties aanpassen |
| `tokens` | `(array $tokens, $context): ?array` | na tokenisatie, op het 2D-raster `ThemedToken` |
| `root` | `(Element $el, $context): ?Element` | op de HAST-root |
| `pre` | `(Element $el, $context): ?Element` | op het element `<pre>` |
| `code` | `(Element $el, $context): ?Element` | op het element `<code>` |
| `line` | `(Element $el, int $line, $context): ?Element` | per regelelement (vanaf 1) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | per tokenspan |
| `postprocess` | `(string $html, array $options, $context): ?string` | op de uiteindelijke geserialiseerde string HTML |

Elke hook is optioneel. Als u een waarde retourneert, wordt de invoer vervangen; `null` retourneren laat die ongewijzigd.

`enforce()` bepaalt de volgorde tussen transformers: `'pre'` wordt als eerste uitgevoerd, `'post'` als laatste en `null` (de standaardinstelling) ertussen, in de volgorde waarin ze zijn geregistreerd.

## Een transformer schrijven

Breid `Shikiphp\Transformer\AbstractTransformer` uit, zodat u alleen de hooks overschrijft die u nodig hebt; de rest doet niets. De transformercontext biedt `addClassToHast()` om klassen aan elementen toe te voegen.

```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" …>
```

## Ingebouwde transformers

### Notatietransformers

Deze lezen speciale commentaren voor `// [!code …]` in de bron (de commentaarsyntaxis van de taal wordt automatisch herkend) en zetten die om in klassen, waarbij het commentaar zelf wordt verwijderd. Dit zijn directe ports van de notatieset `@shikijs/transformers`.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (of `// [!code hl]`) voegt de klasse `highlighted` toe aan de regel.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` markeert een regel als toegevoegd (`diff add`) en `// [!code --]` als verwijderd (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` voegt de klasse `focused` toe.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`, `// [!code warning]` en `// [!code info]` voegen de klasse `highlighted` toe, plus `error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]` wikkelt elke voorkomens van `foo` in een span met `highlighted-word` (optioneel `// [!code word:foo:2]` om dit tot de volgende N regels te beperken).

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

### Metadatatransformers

Deze lezen de **metastring** van het codeblok, die via de optie
[`meta`](https://shikiphp.inline0.dev/nl/docs/options#meta) wordt meegegeven (`meta['__raw']`), zoals Shiki de tekst
na een afgebakend codeblok met een taal leest.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — markeert regels die in een metadatabereik zoals `{1,3-5}` zijn genoemd, met de klasse `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` — markeert woorden die in de metatekst zijn genoemd met de syntaxis `/word/` en voegt `highlighted-word` toe.

### Witruimte en structuur

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — wikkelt spaties en tabs in spans (standaard de klassen `space` en `tab`), zodat ze zichtbaar kunnen worden weergegeven. De constructor neemt `(classSpace, classTab, position)`, waarbij `position` `'all'` is (standaard), `'boundary'`, `'trailing'` of `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — past klassen toe op specifieke regels aan de hand van een lijst met items van `{line, classes}`.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

Regelnummers beginnen bij 1. Geef het object door via de optie `transformers`, net als elke andere transformer.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — maakt de `[\!code …]`-vorm ongedaan die wordt gebruikt om een letterlijk notatiecommentaar te schrijven zonder een notatietransformer te activeren.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — verwijdert de regeleindetekstknooppunten tussen regels (de `transformerRemoveLineBreak` van Shiki).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — verplaatst inline stijlen naar gegenereerde klassen en verzamelt een stylesheet die u met `getCSS()` ophaalt. Klassennamen hashen de stijl met de `cyrb53` van Shiki, zodat de uitvoer byte voor byte identiek is aan `@shikijs/transformers` 4.2.0, inclusief de scope:

- De stijl `<pre>` wordt altijd een klasse.
- Tokenspans worden alleen omgezet als ze een vooraf gebouwde stijl bevatten, zoals bij een kaart met `themes`. Met één `theme` behouden de tokenspans hun inline `style="color:…"`, net als in 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
```

De constructor neemt `(classPrefix, classSuffix, classReplacer)`; het standaardprefix is `__shiki_`.
