
# Transformátory

Transformátory umožňujú upravovať zvýraznený výstup v každej fáze — od
surového zdrojového reťazca cez mriežku tokenov až po výsledný strom HAST a HTML.
shikiphp preberá model transformátorov Shiki háčik za háčikom, takže transformátory sa správajú
rovnako ako ich náprotivky v JavaScript.

Odovzdajte ich cez možnosť [`transformers`](https://shikiphp.inline0.dev/sk/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(),
    ],
]);
```

## Proces spracovania

Každý transformátor je objekt implementujúci `Shikiphp\Transformer\Transformer`.
Reťazec v danom poradí spustí háčiky pri zostavovaní jedného bloku:

| Háčik | Signatúra | Kedy |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | pred tokenizáciou; môže prepísať zdroj a zmeniť možnosti |
| `tokens` | `(array $tokens, $context): ?array` | po tokenizácii, v 2D mriežke `ThemedToken` |
| `root` | `(Element $el, $context): ?Element` | na koreni HAST |
| `pre` | `(Element $el, $context): ?Element` | na prvku `<pre>` |
| `code` | `(Element $el, $context): ?Element` | na prvku `<code>` |
| `line` | `(Element $el, int $line, $context): ?Element` | pre každý prvok riadka (číslovanie od 1) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | pre každý rozsah tokenu |
| `postprocess` | `(string $html, array $options, $context): ?string` | nad výsledným serializovaným reťazcom HTML |

Každý háčik je voliteľný. Vrátená hodnota nahradí vstup; vrátenie `null`
ho ponechá nezmenený.

`enforce()` určuje poradie transformátorov: `'pre'` sa spustí ako prvý, `'post'`
ako posledný a `null` (predvolené nastavenie) sa spustí medzi nimi v poradí registrácie.

## Písanie transformátora

Rozšírte `Shikiphp\Transformer\AbstractTransformer`, aby ste museli prepísať iba tie háčiky,
ktoré potrebujete; ostatné nerobia nič. Kontext transformátora poskytuje
`addClassToHast()` na pridávanie tried prvkom.

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

## Vstavané transformátory

### Transformátory zápisu

Čítajú špeciálne komentáre `// [!code …]` v zdrojovom kóde (syntax komentárov daného
jazyka sa zisťuje automaticky) a prevádzajú ich na triedy, pričom samotný
komentár odstraňujú. Ide o priame porty zo sady zápisu `@shikijs/transformers`.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (alebo
`// [!code hl]`) pridá na riadok triedu `highlighted`.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` označí riadok ako
pridaný (`diff add`), `// [!code --]` ako odstránený (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` pridá triedu
`focused`.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`,
`// [!code warning]` a `// [!code info]` pridajú triedu `highlighted` aj
`error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]`
(voliteľne `// [!code word:foo:2]` na obmedzenie na nasledujúcich N riadkov) obalí každý
výskyt `foo` prvkom span s triedou `highlighted-word`.

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

### Transformátory metadát

Čítajú **reťazec metadát** bloku kódu dodaný cez možnosť
[`meta`](https://shikiphp.inline0.dev/sk/docs/options#meta) (`meta['__raw']`), podobne ako Shiki číta
text za jazykom ohraničeného bloku kódu.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — zvýrazní riadky uvedené v rozsahu metadát,
napríklad `{1,3-5}`, a pridá triedu `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` — zvýrazní slová uvedené v reťazci
metadát pomocou syntaxe `/word/` a pridá `highlighted-word`.

### Medzery a štruktúra

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — obalí medzery a tabulátory prvkami
span (predvolené triedy `space` a `tab`), aby sa dali viditeľne vykresliť.
Konstruktor prijíma `(classSpace, classTab, position)`, pričom `position` je
`'all'` (predvolené), `'boundary'`, `'trailing'` alebo `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — použije triedy na konkrétne riadky
zo zoznamu položiek `{line, classes}`.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

Čísla riadkov začínajú od 1. Odovzdajte objekt v možnosti `transformers`, rovnako
ako ktorýkoľvek iný transformátor.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — zruší escapovanie zápisu `[\!code …]`,
ktorý slúži na zápis doslovného komentára so zápisom bez spustenia transformátora zápisu.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — odstráni textové uzly zalomenia riadka
medzi riadkami (Shiki: `transformerRemoveLineBreak`).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — presunie vložené štýly do vygenerovaných tried
a zhromaždí štýlopis, ktorý načítate pomocou `getCSS()`. Názvy tried sú hashom štýlu
pomocou Shiki `cyrb53`, takže výstup je bajt po bajte zhodný s `@shikijs/transformers`
4.2.0 vrátane jeho rozsahu pôsobnosti:

- Štýl `<pre>` sa vždy zmení na triedu.
- Rozsahy tokenov sa konvertujú iba vtedy, keď obsahujú vopred vytvorený štýl, čo platí
  pre mapu `themes`. Pri jedinom `theme` si rozsahy tokenov zachovajú vložený
  `style="color:…"`, rovnako ako v 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
```

Konstruktor prijíma `(classPrefix, classSuffix, classReplacer)`; predpona je predvolene
`__shiki_`.
