
# Trasformatori

I transformer consentono di rielaborare l'output evidenziato in ogni fase, dalla stringa sorgente grezza, alla griglia di token, fino all'albero HAST finale e a HTML.
shikiphp riproduce fedelmente il modello dei transformer di Shiki, hook per hook, così si comportano come le controparti di JavaScript.

Passateli tramite l'opzione [`transformers`](https://shikiphp.inline0.dev/it/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(),
    ],
]);
```

## La pipeline

Ogni trasformatore è un oggetto che implementa `Shikiphp\Transformer\Transformer`.
La pipeline esegue gli hook in ordine durante la creazione di un singolo blocco:

| Hook | Firma | Quando |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | prima della tokenizzazione; può riscrivere la sorgente e modificare le opzioni |
| `tokens` | `(array $tokens, $context): ?array` | dopo la tokenizzazione, sulla griglia 2D `ThemedToken` |
| `root` | `(Element $el, $context): ?Element` | sulla radice HAST |
| `pre` | `(Element $el, $context): ?Element` | sull'elemento `<pre>` |
| `code` | `(Element $el, $context): ?Element` | sull'elemento `<code>` |
| `line` | `(Element $el, int $line, $context): ?Element` | per ogni elemento riga (base 1) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | per ogni span di token |
| `postprocess` | `(string $html, array $options, $context): ?string` | sulla stringa HTML serializzata finale |

Tutti gli hook sono facoltativi. Restituire un valore sostituisce l'input; restituire `null` lo lascia invariato.

`enforce()` controlla l'ordine tra i trasformatori: `'pre'` viene eseguito per primo, `'post'` per ultimo e `null` (valore predefinito) nel mezzo, nell'ordine di registrazione.

## Scrivere un trasformatore

Estendete `Shikiphp\Transformer\AbstractTransformer` per sovrascrivere solo gli hook necessari; gli altri non eseguono operazioni. Il contesto del trasformatore espone `addClassToHast()` per aggiungere classi agli elementi.

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

## Trasformatori integrati

### Trasformatori di notazione

Leggono commenti `// [!code …]` speciali nel sorgente (la sintassi dei commenti del linguaggio viene rilevata automaticamente), li convertono in classi e rimuovono il commento. Sono port diretti del set di notazioni `@shikijs/transformers`.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (o `// [!code hl]`) aggiunge la classe `highlighted` alla riga.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` contrassegna una riga come aggiunta (`diff add`), mentre `// [!code --]` la contrassegna come rimossa (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` aggiunge la classe `focused`.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`, `// [!code warning]` e `// [!code info]` aggiungono la classe `highlighted` e `error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]` (facoltativamente `// [!code word:foo:2]` per limitarsi alle successive N righe) racchiude ogni occorrenza di `foo` in uno span con `highlighted-word`.

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

### Trasformatori dei metadati

Leggono la **stringa di metadati** del blocco di codice fornita tramite l'opzione [`meta`](https://shikiphp.inline0.dev/it/docs/options#meta) (`meta['__raw']`), come fa Shiki con il testo dopo il linguaggio di un blocco di codice delimitato.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — evidenzia le righe indicate in un intervallo di metadati, come `{1,3-5}`, aggiungendo la classe `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` — evidenzia le parole indicate nella stringa di metadati con la sintassi `/word/`, aggiungendo `highlighted-word`.

### Spazi bianchi e struttura

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — racchiude spazi e tabulazioni in span (classi predefinite `space` e `tab`) per renderli visibili.
Il costruttore accetta `(classSpace, classTab, position)`, dove `position` è `'all'` (predefinito), `'boundary'`, `'trailing'` o `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — applica classi a righe specifiche usando un elenco di voci `{line, classes}`.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

I numeri di riga partono da 1. Passate l'oggetto nell'opzione `transformers`, come qualsiasi altro trasformatore.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — rimuove gli escape dalla forma `[\!code …]` usata per scrivere un commento di notazione letterale senza attivare un trasformatore di notazione.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — rimuove i nodi di testo delle interruzioni di riga tra le righe (Shiki di `transformerRemoveLineBreak`).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — sposta gli stili inline in classi generate e raccoglie un foglio di stile recuperabile con `getCSS()`. I nomi delle classi calcolano un hash dello stile con Shiki `cyrb53`, quindi l'output è identico byte per byte a `@shikijs/transformers` 4.2.0, incluso il relativo scope:

- Lo stile `<pre>` diventa sempre una classe.
- Gli span dei token vengono convertiti solo quando hanno uno stile predefinito, come nel caso di una mappa `themes`. Con un unico `theme`, gli span dei token mantengono il loro `style="color:…"` inline, come 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
```

Il costruttore accetta `(classPrefix, classSuffix, classReplacer)`; il prefisso predefinito è `__shiki_`.
