Aller au contenu

Transformateurs

Les transformateurs vous permettent de remanier la sortie mise en évidence à chaque étape — de la chaîne source brute à l’arbre HAST final, en passant par la grille de jetons et HTML. shikiphp reprend le modèle de transformateurs de Shiki hook par hook ; les transformateurs se comportent donc comme leurs équivalents JavaScript.

Transmettez-les via l’option 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(),
    ],
]);

Le pipeline

Chaque transformateur est un objet qui implémente Shikiphp\Transformer\Transformer. Le pipeline exécute les hooks dans l’ordre lors de la construction d’un bloc :

Hook Signature Quand
preprocess (string $code, array &$options, $context): ?string avant la tokenisation ; peut réécrire la source et modifier les options
tokens (array $tokens, $context): ?array après la tokenisation, sur la grille ThemedToken en 2D
root (Element $el, $context): ?Element sur la racine HAST
pre (Element $el, $context): ?Element sur l’élément <pre>
code (Element $el, $context): ?Element sur l’élément <code>
line (Element $el, int $line, $context): ?Element pour chaque élément de ligne (indexation à partir de 1)
span (Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element pour chaque étendue de jeton
postprocess (string $html, array $options, $context): ?string sur la chaîne HTML finale sérialisée

Tous les hooks sont facultatifs. Le renvoi d’une valeur remplace l’entrée ; le renvoi de null la laisse inchangée.

enforce() contrôle l’ordre entre les transformateurs : 'pre' s’exécute en premier, 'post' en dernier et null (la valeur par défaut) entre les deux, dans l’ordre d’enregistrement.

Écrire un transformateur

Étendez Shikiphp\Transformer\AbstractTransformer pour ne remplacer que les hooks dont vous avez besoin ; les autres ne font rien. Le contexte du transformateur expose addClassToHast() pour ajouter des classes aux éléments.

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

Transformateurs intégrés

Transformateurs de notation

Ils lisent les commentaires // [!code …] spéciaux dans la source (la syntaxe de commentaire du langage est détectée automatiquement) et les convertissent en classes en supprimant le commentaire lui-même. Il s’agit de ports directs de l’ensemble de notations @shikijs/transformers.

NotationHighlight

Shikiphp\Transformer\Notation\NotationHighlight — // [!code highlight] (ou // [!code hl]) ajoute la classe highlighted à sa ligne.

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

NotationDiff

Shikiphp\Transformer\Notation\NotationDiff — // [!code ++] marque une ligne comme ajoutée (diff add) ; // [!code --] la marque comme supprimée (diff remove).

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

NotationFocus

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

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

NotationErrorLevel

Shikiphp\Transformer\Notation\NotationErrorLevel — // [!code error], // [!code warning] et // [!code info] ajoutent la classe highlighted ainsi que error / warning / info.

JavaScript
throwSomething() // [!code error]
maybeRisky()     // [!code warning]

NotationWordHighlight

Shikiphp\Transformer\Notation\NotationWordHighlight — // [!code word:foo] (avec // [!code word:foo:2] facultatif pour limiter l’effet aux N lignes suivantes) entoure chaque occurrence de foo dans une étendue portant highlighted-word.

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

Transformateurs de métadonnées

Ils lisent la chaîne de métadonnées de la clôture de code fournie via l’option meta (meta['__raw']), comme Shiki lit le texte après le langage d’un bloc de code clôturé.

MetaHighlight

Shikiphp\Transformer\MetaHighlight — met en évidence les lignes indiquées dans une plage de métadonnées comme {1,3-5} et ajoute 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 — met en évidence les mots indiqués dans la chaîne de métadonnées avec la syntaxe /word/ et ajoute highlighted-word.

Espaces blancs et structure

RenderWhitespace

Shikiphp\Transformer\RenderWhitespace — entoure les espaces et les tabulations d’étendues (classes space et tab par défaut) pour les rendre visibles. Le constructeur prend (classSpace, classTab, position), où position vaut 'all' (par défaut), 'boundary', 'trailing' ou 'leading'.

CompactLineOptions

Shikiphp\Transformer\CompactLineOptions — applique des classes à des lignes précises à partir d’une liste d’entrées {line, classes}.

PHP
use Shikiphp\Transformer\CompactLineOptions;

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

La numérotation des lignes commence à 1. Transmettez l’objet dans l’option transformers, comme tout autre transformateur.

RemoveNotationEscape

Shikiphp\Transformer\RemoveNotationEscape — annule l’échappement de la forme [\!code …], qui permet d’écrire un commentaire de notation littéral sans déclencher de transformateur de notation.

RemoveLineBreak

Shikiphp\Transformer\RemoveLineBreak — supprime les nœuds de texte de saut de ligne entre les lignes (Shiki de transformerRemoveLineBreak).

StyleToClass

Shikiphp\Transformer\StyleToClass — déplace les styles en ligne vers des classes générées et collecte une feuille de style que vous récupérez avec getCSS(). Les noms de classe sont des empreintes du style calculées avec Shiki et son cyrb53 ; la sortie est donc identique octet pour octet à celle de @shikijs/transformers 4.2.0, portée comprise :

  • Le style <pre> devient toujours une classe.
  • Les étendues de jeton sont converties uniquement si elles portent un style prédéfini, comme c’est le cas avec une map themes. Avec un seul theme, les étendues de jeton conservent leurs styles en ligne style="color:…", comme dans 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

Le constructeur prend (classPrefix, classSuffix, classReplacer) ; le préfixe par défaut est __shiki_.