
# Muuntimet

Muuntimilla voit muokata korostettua tulostetta kaikissa vaiheissa —
lähdemerkkijonosta tokenruudukon kautta lopulliseen HAST-puuhun ja HTML:iin.
shikiphp siirtää Shiki-muunninmallin koukku koukulta, joten muuntimet toimivat
samoin kuin niiden JavaScript-vastineet.

Välitä ne [`transformers`](https://shikiphp.inline0.dev/fi/docs/options#transformers)-valinnan kautta:

```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(),
    ],
]);
```

## Käsittelyputki

Kukin muunnin on olio, joka toteuttaa `Shikiphp\Transformer\Transformer`-rajapinnan.
Putki suorittaa koukut järjestyksessä yhden lohkon muodostamisen aikana:

| Koukku | Määrittely | Milloin |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | ennen tokenisointia; voi kirjoittaa lähteen uudelleen ja muuttaa asetuksia |
| `tokens` | `(array $tokens, $context): ?array` | tokenisoinnin jälkeen, 2D-`ThemedToken`-ruudukossa |
| `root` | `(Element $el, $context): ?Element` | HAST-juurielementissä |
| `pre` | `(Element $el, $context): ?Element` | `<pre>`-elementissä |
| `code` | `(Element $el, $context): ?Element` | `<code>`-elementissä |
| `line` | `(Element $el, int $line, $context): ?Element` | rivielementeissä (alkaen numerosta 1) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | tokenin välein span-elementissä |
| `postprocess` | `(string $html, array $options, $context): ?string` | lopullisessa sarjoitetussa HTML-merkkijonossa |

Kaikki koukut ovat valinnaisia. Palautettu arvo korvaa syötteen; arvon `null` palauttaminen
säilyttää sen ennallaan.

`enforce()` määrittää järjestyksen muuntimien välillä: `'pre'` suoritetaan ensin, `'post'`
viimeisenä ja `null` (oletus) rekisteröintijärjestyksessä niiden välissä.

## Muuntimen kirjoittaminen

Laajenna `Shikiphp\Transformer\AbstractTransformer`-luokkaa, jotta sinun tarvitsee korvata vain tarvitsemasi koukut;
muut eivät tee mitään. Muuntimen konteksti tarjoaa `addClassToHast()`-funktion
luokkien lisäämiseen elementteihin.

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

## Valmiit muuntimet

### Merkintämuuntimet

Nämä lukevat lähteen erityiset `// [!code …]`-kommentit (kommenttimerkintä
havaitaan automaattisesti kielen mukaan) ja muuntavat ne luokiksi
poistaen itse kommentin. Ne ovat suoria siirtoja `@shikijs/transformers`-merkintäjoukosta.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (tai
`// [!code hl]`) lisää riville luokan `highlighted`.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` merkitsee rivin lisätyksi (`diff add`), `// [!code --]` poistetuksi (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` lisää luokan
`focused`.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`,
`// [!code warning]` ja `// [!code info]` lisäävät luokan `highlighted` sekä
`error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]`
(valinnaisesti `// [!code word:foo:2]` rajaamaan vaikutus seuraaviin N riviin) ympäröi jokaisen
`foo`-esiintymän span-elementillä, jossa on `highlighted-word`.

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

### Metamuuntimet

Nämä lukevat koodiaitausten kautta annetun **metamerkkijonon**
[`meta`](https://shikiphp.inline0.dev/fi/docs/options#meta)-valinnasta (`meta['__raw']`), samalla tavoin kuin Shiki lukee
koodiaitauksen kielen jälkeisen tekstin.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — korostaa metavälillä nimettyjä rivejä,
esimerkiksi `{1,3-5}`, ja lisää luokan `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` — korostaa metamerkkijonossa `/word/`-syntaksilla
nimettyjä sanoja ja lisää luokan `highlighted-word`.

### Tyhjemerkit ja rakenne

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — ympäröi välilyönti- ja sarkainmerkit
span-elementeillä (luokat ovat oletuksena `space` ja `tab`), jotta ne voidaan näyttää näkyvästi.
Konstruktorille annetaan `(classSpace, classTab, position)`, jossa `position` on
`'all'` (oletus), `'boundary'`, `'trailing'` tai `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — lisää luokkia tietyille riveille
`{line, classes}`-alkioiden luettelon perusteella.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

Rivinumerointi alkaa numerosta 1. Välitä olio `transformers`-valinnassa kuten mikä tahansa
muu muunnin.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — poistaa suojauksen `[\!code …]`-muodosta,
jolla kirjoitetaan kirjaimellinen merkintäkommentti käynnistämättä merkintämuunninta.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — poistaa rivinvaihtotekstisolmut rivien välistä
(Shiki-olion `transformerRemoveLineBreak`).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — siirtää sisäiset tyylit luoduiksi luokiksi
ja kerää tyylitiedoston, jonka haet komennolla `getCSS()`. Luokkien nimet tiivistävät tyylin
Shiki-olion `cyrb53`-toiminnolla, joten tuloste on tavutasolla identtinen kohteen `@shikijs/transformers`
4.2.0 kanssa, myös sen rajauksen osalta:

- `<pre>`-tyyli muuttuu aina luokaksi.
- Tokenien span-elementit muunnetaan vain, jos niissä on valmiiksi luotu tyyli, kuten silloin,
  kun käytössä on `themes`-kartta. Yhdellä `theme`-teemalla tokenien span-elementit säilyttävät
  sisäisen `style="color:…"`-tyylinsä, kuten kohteessa 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
```

Konstruktorille annetaan `(classPrefix, classSuffix, classReplacer)`; oletusetuliite
on `__shiki_`.
