Vai al contenuto

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:

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.

JavaScript
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).

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

NotationFocus

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

JavaScript
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.

JavaScript
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.

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

Trasformatori dei metadati

Leggono la stringa di metadati del blocco di codice fornita tramite l'opzione 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_.