
# Transformers

Transformers låter dig omforma den markerade utdatan i varje steg — från
råkällsträngen via tokengallret till det slutliga HAST-trädet och HTML.
shikiphp överför Shikis transformer-modell hook för hook, så transformers fungerar
på samma sätt som motsvarande komponenter i JavaScript.

Skicka dem via alternativet [`transformers`](https://shikiphp.inline0.dev/sv/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(),
    ],
]);
```

## Flödet

Varje transformer är ett objekt som implementerar `Shikiphp\Transformer\Transformer`.
Flödet kör hookarna i ordning medan ett block byggs:

| Hook | Signatur | När |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | före tokenisering; kan skriva om källan och ändra alternativ |
| `tokens` | `(array $tokens, $context): ?array` | efter tokenisering, i det tvådimensionella `ThemedToken`-gallret |
| `root` | `(Element $el, $context): ?Element` | på HAST-roten |
| `pre` | `(Element $el, $context): ?Element` | på elementet `<pre>` |
| `code` | `(Element $el, $context): ?Element` | på elementet `<code>` |
| `line` | `(Element $el, int $line, $context): ?Element` | per rad-element (1-baserat) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | per tokenintervall |
| `postprocess` | `(string $html, array $options, $context): ?string` | på den slutligt serialiserade HTML-strängen |

Alla hookar är valfria. Ett returvärde ersätter indata; om du returnerar `null`
lämnas den oförändrad.

`enforce()` styr ordningen mellan transformers: `'pre'` körs först, `'post'`
sist och `null` (standardvärdet) däremellan i registreringsordning.

## Skriva en transformer

Utöka `Shikiphp\Transformer\AbstractTransformer` så att du bara behöver åsidosätta de hookar
du använder; resten gör ingenting. Transformer-kontexten ger åtkomst till
`addClassToHast()` för att lägga till klasser på element.

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

## Inbyggda transformers

### Noteringstransformers

De här läser särskilda `// [!code …]`-kommentarer i källan (kommentarsyntaxen för
språket identifieras automatiskt) och omvandlar dem till klasser,
samtidigt som själva kommentaren tas bort. De är direkta portar av `@shikijs/transformers`s
notationsuppsättning.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (eller
`// [!code hl]`) lägger till klassen `highlighted` på sin rad.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` markerar en rad som
tillagd (`diff add`) och `// [!code --]` som borttagen (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` lägger till klassen
`focused`.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`,
`// [!code warning]` och `// [!code info]` lägger till klassen `highlighted` samt
`error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]`
(vid behov `// [!code word:foo:2]` för att begränsa till de följande N raderna) omsluter varje
förekomst av `foo` i ett span med `highlighted-word`.

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

### Metatransformatorer

Dessa läser kodstängslets **metasträng** som anges via
[`meta`](https://shikiphp.inline0.dev/sv/docs/options#meta)-alternativet (`meta['__raw']`), på samma sätt som Shiki läser
texten efter språket i ett kodblock med kodstängsel.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — markerar rader som anges i ett metaintervall
som `{1,3-5}` och lägger till klassen `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` — markerar ord som anges i metasträngen med
syntaxen `/word/` och lägger till `highlighted-word`.

### Blanksteg och struktur

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — omsluter blanksteg och tabbtecken i
span-element (klasserna `space` och `tab` som standard) så att de kan visas synligt.
Konstruktorn tar `(classSpace, classTab, position)` där `position` är
`'all'` (standard), `'boundary'`, `'trailing'` eller `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — tillämpar klasser på specifika rader
från en lista med `{line, classes}` poster.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

Radnumreringen börjar på 1. Skicka objektet i alternativet `transformers` som vilken
annan transformator som helst.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — avkodar formen `[\!code …]` som används
för att skriva en bokstavlig noteringskommentar utan att aktivera en noteringstransformator.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — tar bort radbrytningstextnoderna
mellan rader (Shikis `transformerRemoveLineBreak`).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — flyttar inline-format till genererade klasser
och samlar en formatmall som du hämtar med `getCSS()`. Klassnamnen hashas från formatet
med Shikis `cyrb53`, så utdata är byteidentiska med `@shikijs/transformers`
4.2.0, inklusive dess omfattning:

- Stilen `<pre>` blir alltid en klass.
- Tokenintervall konverteras bara när de har en förbyggd stil, vilket är fallet med en
  `themes`-karta. Med en enda `theme` behåller tokenintervallen sin
  inline-`style="color:…"`, precis som i 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
```

Konstruktorn tar `(classPrefix, classSuffix, classReplacer)`; standardprefixet
är `__shiki_`.
