
# Transformere

Transformere lader dig ændre det fremhævede output i hvert trin — fra
rå kildestreng gennem tokengitteret til det endelige HAST-træ og HTML.
shikiphp viderefører Shikis transformermodel med de samme hooks, så transformere opfører sig
på samme måde som deres modstykker i JavaScript.

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

## Pipelineforløbet

Hver transformer er et objekt, der implementerer `Shikiphp\Transformer\Transformer`.
Forløbet kører hooksene i rækkefølge, mens en enkelt blok bygges:

| Hook | Signatur | Tidspunkt |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | før tokenisering; kan omskrive kilden og ændre indstillinger |
| `tokens` | `(array $tokens, $context): ?array` | efter tokenisering i det 2D `ThemedToken`-gitter |
| `root` | `(Element $el, $context): ?Element` | på HAST-roden |
| `pre` | `(Element $el, $context): ?Element` | på elementet `<pre>` |
| `code` | `(Element $el, $context): ?Element` | på elementet `<code>` |
| `line` | `(Element $el, int $line, $context): ?Element` | for hvert linjeelement (starter ved 1) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | for hvert tokenområde |
| `postprocess` | `(string $html, array $options, $context): ?string` | på den endeligt serialiserede HTML-streng |

Alle hooks er valgfrie. Hvis du returnerer en værdi, erstatter den inputtet; hvis du returnerer `null`,
forbliver det uændret.

`enforce()` styrer rækkefølgen mellem transformere: `'pre'` køres først, `'post'`
sidst, og `null` (standardværdien) køres imellem dem i registreringsrækkefølge.

## Skriv en transformer

Udvid `Shikiphp\Transformer\AbstractTransformer`, så du kun behøver at tilsidesætte de hooks,
du skal bruge; resten gør ingenting. Transformerens kontekst giver adgang til
`addClassToHast()`, så du kan føje klasser til elementer.

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

## Indbyggede transformere

### Transformere til notation

Disse læser særlige `// [!code …]`-kommentarer i kilden (kommentarsyntaksen for sproget registreres automatisk) og oversætter dem til klasser, mens selve kommentaren fjernes. De er direkte porte af `@shikijs/transformers`-notationssættet.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (eller `// [!code hl]`) tilføjer klassen `highlighted` til linjen.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` markerer en linje som tilføjet (`diff add`), og `// [!code --]` som fjernet (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` tilføjer klassen `focused`.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`, `// [!code warning]` og `// [!code info]` tilføjer klassen `highlighted` plus `error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]` (eventuelt `// [!code word:foo:2]` for at begrænse det til de næste N linjer) omslutter hver forekomst af `foo` i et span med `highlighted-word`.

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

### Metatransformere

Disse læser kodeblokkens **metastreng**, som angives via indstillingen [`meta`](https://shikiphp.inline0.dev/da/docs/options#meta) (`meta['__raw']`), på samme måde som Shiki læser teksten efter sproget i en indhegnet kodeblok.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — fremhæver linjer angivet i et metainterval som `{1,3-5}` og tilføjer 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` — fremhæver ord angivet i metastrengen med syntaksen `/word/` og tilføjer `highlighted-word`.

### Mellemrum og struktur

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — omslutter mellemrumstegn og tabulatortegn i spans (som standard med klasserne `space` og `tab`), så de kan vises tydeligt. Konstruktøren tager `(classSpace, classTab, position)`, hvor `position` er `'all'` (standard), `'boundary'`, `'trailing'` eller `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — anvender klasser på bestemte linjer ud fra en liste med `{line, classes}`-elementer.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

Linjenumre starter ved 1. Send objektet i indstillingen `transformers` som enhver anden transformer.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — fjerner escape-tegnet fra formen `[\!code …]`, der bruges til at skrive en bogstavelig notationskommentar uden at aktivere en notationstransformer.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — fjerner linjeskiftstekstnoderne mellem linjer (Shikis `transformerRemoveLineBreak`).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — flytter inline-stile til genererede klasser og samler et stilark, som du henter med `getCSS()`. Klassenavne beregnes som hash af stilen med Shikis `cyrb53`, så outputtet er byteidentisk med `@shikijs/transformers` 4.2.0, inklusive dets scope:

- Stilen `<pre>` bliver altid til en klasse.
- Token-spans konverteres kun, når de har en forudbygget stil, hvilket er tilfældet med et `themes`-kort. Med et enkelt `theme` beholder token-spans deres inline-`style="color:…"`, 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
```

Konstruktøren tager `(classPrefix, classSuffix, classReplacer)`; standardpræfikset er `__shiki_`.
