
# Transformatoare

Transformatoarele vă permit să remodelați rezultatul evidențiat în fiecare etapă — de la șirul sursă
brut, prin grila de tokenuri, până la arborele HAST final și HTML.
shikiphp preia modelul de transformatoare al Shiki hook cu hook, astfel încât transformatoarele se comportă
la fel ca omoloagele lor JavaScript.

Transmiteți-le prin opțiunea [`transformers`](https://shikiphp.inline0.dev/ro/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(),
    ],
]);
```

## Procesul de lucru

Fiecare transformator este un obiect care implementează `Shikiphp\Transformer\Transformer`.
Fluxul execută hook-urile în ordine, pe parcursul construirii unui singur bloc:

| Hook | Semnătură | Când |
| --- | --- | --- |
| `preprocess` | `(string $code, array &$options, $context): ?string` | înainte de tokenizare; poate rescrie sursa și modifica opțiunile |
| `tokens` | `(array $tokens, $context): ?array` | după tokenizare, asupra grilei 2D `ThemedToken` |
| `root` | `(Element $el, $context): ?Element` | asupra rădăcinii HAST |
| `pre` | `(Element $el, $context): ?Element` | asupra elementului `<pre>` |
| `code` | `(Element $el, $context): ?Element` | asupra elementului `<code>` |
| `line` | `(Element $el, int $line, $context): ?Element` | pentru fiecare element de linie (începând de la 1) |
| `span` | `(Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element` | pentru fiecare interval de token |
| `postprocess` | `(string $html, array $options, $context): ?string` | asupra șirului HTML serializat final |

Toate hook-urile sunt opționale. Returnarea unei valori înlocuiește intrarea; returnarea lui `null`
o păstrează neschimbată.

`enforce()` controlează ordinea transformatoarelor: `'pre'` rulează primul, `'post'`
rulează ultimul, iar `null` (valoarea implicită) rulează între acestea, în ordinea înregistrării.

## Scrierea unui transformator

Extindeți `Shikiphp\Transformer\AbstractTransformer` pentru a suprascrie doar hook-urile
necesare; restul nu fac nimic. Contextul transformatorului expune
`addClassToHast()` pentru adăugarea claselor la elemente.

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

## Transformatoare incluse

### Transformatoare de notație

Acestea citesc comentarii speciale `// [!code …]` din sursă (sintaxa comentariilor
limbii este detectată automat) și le transformă în clase,
eliminând comentariul propriu-zis. Sunt portări directe ale setului de
notație `@shikijs/transformers`.

#### `NotationHighlight`

`Shikiphp\Transformer\Notation\NotationHighlight` — `// [!code highlight]` (sau
`// [!code hl]`) adaugă clasa `highlighted` liniei respective.

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

#### `NotationDiff`

`Shikiphp\Transformer\Notation\NotationDiff` — `// [!code ++]` marchează o linie ca
adăugată (`diff add`), iar `// [!code --]` ca ștearsă (`diff remove`).

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

#### `NotationFocus`

`Shikiphp\Transformer\Notation\NotationFocus` — `// [!code focus]` adaugă clasa
`focused`.

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

#### `NotationErrorLevel`

`Shikiphp\Transformer\Notation\NotationErrorLevel` — `// [!code error]`,
`// [!code warning]` și `// [!code info]` adaugă clasa `highlighted` plus
`error` / `warning` / `info`.

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

#### `NotationWordHighlight`

`Shikiphp\Transformer\Notation\NotationWordHighlight` — `// [!code word:foo]`
(opțional `// [!code word:foo:2]` pentru a limita la următoarele N linii) încadrează fiecare
apariție a `foo` într-un span care conține `highlighted-word`.

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

### Transformatoare meta

Acestea citesc șirul **meta** al blocului de cod, furnizat prin opțiunea
[`meta`](https://shikiphp.inline0.dev/ro/docs/options#meta) (`meta['__raw']`), așa cum Shiki citește
textul de după limbajul unui bloc de cod delimitat.

#### `MetaHighlight`

`Shikiphp\Transformer\MetaHighlight` — evidențiază liniile specificate într-un interval meta
precum `{1,3-5}`, adăugând clasa `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` — evidențiază cuvintele specificate în șirul meta
cu sintaxa `/word/`, adăugând `highlighted-word`.

### Spații albe și structură

#### `RenderWhitespace`

`Shikiphp\Transformer\RenderWhitespace` — încadrează caracterele spațiu și tabulație în
span-uri (clasele `space` și `tab` în mod implicit), astfel încât să poată fi redate vizibil.
Constructorul primește `(classSpace, classTab, position)`, unde `position` este
`'all'` (implicit), `'boundary'`, `'trailing'` sau `'leading'`.

#### `CompactLineOptions`

`Shikiphp\Transformer\CompactLineOptions` — aplică clase unor linii specifice
dintr-o listă de intrări `{line, classes}`.

```php
use Shikiphp\Transformer\CompactLineOptions;

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

Numerotarea liniilor începe de la 1. Transmiteți obiectul în opțiunea `transformers`, ca orice
alt transformator.

#### `RemoveNotationEscape`

`Shikiphp\Transformer\RemoveNotationEscape` — elimină escape-ul din forma `[\!code …]`
folosită pentru a scrie un comentariu de notație literală fără a declanșa un transformator
de notație.

#### `RemoveLineBreak`

`Shikiphp\Transformer\RemoveLineBreak` — elimină nodurile text cu întrerupere de linie
dintre linii (`transformerRemoveLineBreak` din Shiki).

### `StyleToClass`

`Shikiphp\Transformer\StyleToClass` — mută stilurile inline în clase generate
și colectează o foaie de stiluri pe care o preluați cu `getCSS()`. Numele claselor hash-uiesc stilul
cu Shiki al `cyrb53`, astfel încât rezultatul este identic la nivel de octeți cu 4.2.0
`@shikijs/transformers`, inclusiv domeniul său de aplicare:

- Stilul `<pre>` devine întotdeauna o clasă.
- Span-urile tokenurilor sunt convertite doar când au un stil predefinit, ceea ce se
  întâmplă cu o mapare `themes`. Cu un singur `theme`, span-urile tokenurilor își păstrează
  stilul inline `style="color:…"`, la fel ca în 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
```

Constructorul primește `(classPrefix, classSuffix, classReplacer)`; prefixul implicit
este `__shiki_`.
