Zum Inhalt springen

Transformer

Mit Transformern kannst du die hervorgehobene Ausgabe in jeder Phase neu gestalten – vom rohen Quelltext über das Token-Raster bis zum fertigen HAST-Baum und HTML. shikiphp übernimmt Shikis Transformermodell Hook für Hook, sodass sich Transformer genauso verhalten wie ihre JavaScript-Entsprechungen.

Übergib sie über die 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(),
    ],
]);

Die Pipeline

Jeder Transformer ist ein Objekt, das Shikiphp\Transformer\Transformer implementiert. Die Pipeline führt die Hooks der Reihe nach beim Aufbau eines einzelnen Blocks aus:

Hook Signatur Zeitpunkt
preprocess (string $code, array &$options, $context): ?string vor der Tokenisierung; kann den Quelltext umschreiben und Optionen ändern
tokens (array $tokens, $context): ?array nach der Tokenisierung, auf dem 2D-ThemedToken-Raster
root (Element $el, $context): ?Element auf der HAST-Wurzel
pre (Element $el, $context): ?Element auf dem <pre>-Element
code (Element $el, $context): ?Element auf dem <code>-Element
line (Element $el, int $line, $context): ?Element pro Zeilenelement (ab 1 gezählt)
span (Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element pro Token-Span
postprocess (string $html, array $options, $context): ?string auf dem zuletzt serialisierten HTML-String

Jeder Hook ist optional. Ein Rückgabewert ersetzt die Eingabe; null lässt sie unverändert.

enforce() legt die Reihenfolge der Transformer fest: 'pre' wird zuerst ausgeführt, 'post' zuletzt und null (der Standard) dazwischen, in der Reihenfolge ihrer Registrierung.

Transformer schreiben

Erweitere Shikiphp\Transformer\AbstractTransformer, damit du nur die Hooks überschreiben musst, die du benötigst; die übrigen bleiben wirkungslos. Der Transformer-Kontext stellt addClassToHast() bereit, um Elementen Klassen hinzuzufügen.

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

Integrierte Transformer

Notations-Transformer

Diese lesen spezielle // [!code …]-Kommentare im Quelltext (die Kommentar-Syntax der Sprache wird automatisch erkannt) und wandeln sie in Klassen um; der Kommentar selbst wird entfernt. Sie sind direkte Portierungen des @shikijs/transformers-Notationssatzes.

NotationHighlight

Shikiphp\Transformer\Notation\NotationHighlight — // [!code highlight] (oder // [!code hl]) fügt der Zeile die Klasse highlighted hinzu.

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

NotationDiff

Shikiphp\Transformer\Notation\NotationDiff — // [!code ++] markiert eine Zeile als hinzugefügt (diff add), // [!code --] als entfernt (diff remove).

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

NotationFocus

Shikiphp\Transformer\Notation\NotationFocus — // [!code focus] fügt die Klasse focused hinzu.

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

NotationErrorLevel

Shikiphp\Transformer\Notation\NotationErrorLevel — // [!code error], // [!code warning] und // [!code info] fügen die Klasse highlighted sowie error / warning / info hinzu.

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

NotationWordHighlight

Shikiphp\Transformer\Notation\NotationWordHighlight — // [!code word:foo] (optional // [!code word:foo:2], um die Anzahl auf die nächsten N Zeilen zu begrenzen) umschließt jedes Vorkommen von foo mit einem Span, der highlighted-word enthält.

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

Metadaten-Transformer

Diese lesen den Metadaten-String des Code-Fences, der über die Option meta bereitgestellt wird (meta['__raw']) – so wie Shiki den Text nach der Sprache eines abgegrenzten Codeblocks ausliest.

MetaHighlight

Shikiphp\Transformer\MetaHighlight — hebt die Zeilen hervor, die in einem Metadatenbereich wie {1,3-5} angegeben sind, und fügt die Klasse highlighted hinzu.

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 — hebt Wörter hervor, die im Metadaten-String mit der Syntax /word/ angegeben sind, und fügt highlighted-word hinzu.

Leerraum und Struktur

RenderWhitespace

Shikiphp\Transformer\RenderWhitespace — umschließt Leerzeichen und Tabulatoren mit Spans (standardmäßig die Klassen space und tab), damit sie sichtbar dargestellt werden können. Der Konstruktor erhält (classSpace, classTab, position), wobei position 'all' (Standard), 'boundary', 'trailing' oder 'leading' ist.

CompactLineOptions

Shikiphp\Transformer\CompactLineOptions — wendet Klassen auf bestimmte Zeilen an, basierend auf einer Liste von {line, classes}-Einträgen.

PHP
use Shikiphp\Transformer\CompactLineOptions;

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

Zeilennummern beginnen bei 1. Übergib das Objekt wie jeden anderen Transformer über die Option transformers.

RemoveNotationEscape

Shikiphp\Transformer\RemoveNotationEscape — hebt die Escape-Sequenz [\!code …] auf, mit der du einen wörtlichen Notationskommentar schreiben kannst, ohne einen Notations-Transformer auszulösen.

RemoveLineBreak

Shikiphp\Transformer\RemoveLineBreak — entfernt die Zeilenumbruch-Textknoten zwischen den Zeilen (Shikis transformerRemoveLineBreak).

StyleToClass

Shikiphp\Transformer\StyleToClass — verschiebt Inline-Stile in generierte Klassen und sammelt ein Stylesheet, das du mit getCSS() abrufst. Klassennamen hashen den Stil mit cyrb53 von Shiki, sodass die Ausgabe byteidentisch mit @shikijs/transformers 4.2.0 ist, einschließlich des Geltungsbereichs:

  • Der Stil <pre> wird immer zu einer Klasse.
  • Token-Spans werden nur dann umgewandelt, wenn sie einen vorgefertigten Stil enthalten, wie bei einer themes-Map. Bei einem einzelnen theme behalten die Token-Spans ihre Inline-style="color:…", wie 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

Der Konstruktor erhält (classPrefix, classSuffix, classReplacer); das Präfix ist standardmäßig __shiki_.