Przejdź do treści

Transformery

Transformery pozwalają przekształcać wyróżnione wyjście na każdym etapie — od surowego ciągu źródłowego, przez siatkę tokenów, po końcowe drzewo HAST i HTML. shikiphp odwzorowuje model transformerów Shiki hook po hooku, dlatego transformery działają identycznie jak ich odpowiedniki JavaScript.

Przekaż je za pomocą opcji 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(),
    ],
]);

Potok przetwarzania

Każdy transformer to obiekt implementujący Shikiphp\Transformer\Transformer. Potok uruchamia hooki w podanej kolejności podczas tworzenia pojedynczego bloku:

Hook Sygnatura Kiedy
preprocess (string $code, array &$options, $context): ?string przed tokenizacją; może przepisać źródło i zmodyfikować opcje
tokens (array $tokens, $context): ?array po tokenizacji, na siatce ThemedToken 2D
root (Element $el, $context): ?Element na korzeniu HAST
pre (Element $el, $context): ?Element na elemencie <pre>
code (Element $el, $context): ?Element na elemencie <code>
line (Element $el, int $line, $context): ?Element dla każdego elementu wiersza (indeksowanie od 1)
span (Element $el, int $line, int $col, Element $lineEl, ThemedToken $token, $context): ?Element dla każdego zakresu tokenu
postprocess (string $html, array $options, $context): ?string na końcowym zserializowanym ciągu HTML

Każdy hook jest opcjonalny. Zwrócenie wartości zastępuje dane wejściowe; zwrócenie null pozostawia je bez zmian.

enforce() kontroluje kolejność działania transformerów: najpierw uruchamiane jest 'pre', na końcu 'post', a null (domyślnie) działa pomiędzy nimi, zgodnie z kolejnością rejestracji.

Pisanie transformera

Rozszerz Shikiphp\Transformer\AbstractTransformer i nadpisz tylko potrzebne hooki; pozostałe nie wykonują żadnych działań. Kontekst transformera udostępnia addClassToHast() do dodawania klas do elementów.

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

Wbudowane transformery

Transformery notacji

Odczytują specjalne komentarze // [!code …] w kodzie źródłowym (składnia komentarzy jest wykrywana automatycznie), zamieniają je na klasy i usuwają same komentarze. To bezpośrednie porty zestawu notacji @shikijs/transformers.

NotationHighlight

Shikiphp\Transformer\Notation\NotationHighlight — // [!code highlight] (lub // [!code hl]) dodaje do wiersza klasę highlighted.

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

NotationDiff

Shikiphp\Transformer\Notation\NotationDiff — // [!code ++] oznacza wiersz jako Dodany (diff add), a // [!code --] jako usunięty (diff remove).

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

NotationFocus

Shikiphp\Transformer\Notation\NotationFocus — // [!code focus] dodaje klasę focused.

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

NotationErrorLevel

Shikiphp\Transformer\Notation\NotationErrorLevel — // [!code error], // [!code warning] i // [!code info] dodają klasę highlighted oraz error / warning / info.

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

NotationWordHighlight

Shikiphp\Transformer\Notation\NotationWordHighlight — // [!code word:foo] (opcjonalnie // [!code word:foo:2], aby ograniczyć działanie do kolejnych N wierszy) opakowuje każde wystąpienie foo w element span z highlighted-word.

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

Transformery metadanych

Odczytują one ciąg metadanych bloku kodu, przekazany przez opcję meta (meta['__raw']), podobnie jak Shiki odczytuje tekst po języku bloku kodu z potrójnymi backtickami.

MetaHighlight

Shikiphp\Transformer\MetaHighlight — wyróżnia wiersze wskazane w zakresie metadanych, na przykład {1,3-5}, dodając klasę 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 — wyróżnia słowa wskazane w ciągu metadanych za pomocą składni /word/, dodając highlighted-word.

Białe znaki i struktura

RenderWhitespace

Shikiphp\Transformer\RenderWhitespace — opakowuje znaki spacji i tabulatora w spanach (domyślnie klasy space i tab), aby można je było widocznie wyrenderować. Konstruktor przyjmuje (classSpace, classTab, position), gdzie position to 'all' (domyślnie), 'boundary', 'trailing' lub 'leading'.

CompactLineOptions

Shikiphp\Transformer\CompactLineOptions — przypisuje klasy określonym wierszom na podstawie listy wpisów {line, classes}.

PHP
use Shikiphp\Transformer\CompactLineOptions;

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

Numery wierszy zaczynają się od 1. Przekaż obiekt w opcji transformers, tak jak każdy inny transformer.

RemoveNotationEscape

Shikiphp\Transformer\RemoveNotationEscape — usuwa znaki ucieczki z formy [\!code …], używanej do zapisania dosłownego komentarza notacji bez uruchamiania transformera notacji.

RemoveLineBreak

Shikiphp\Transformer\RemoveLineBreak — usuwa węzły tekstowe znaków końca wiersza między wierszami (Shiki: transformerRemoveLineBreak).

StyleToClass

Shikiphp\Transformer\StyleToClass — przenosi style inline do generowanych klas i gromadzi arkusz stylów pobierany za pomocą getCSS(). Nazwy klas są skrótem stylu obliczonym przy użyciu Shiki: cyrb53, dlatego wynik jest identyczny bajt po bajcie z @shikijs/transformers 4.2.0, łącznie z zakresem:

  • Styl <pre> zawsze staje się klasą.
  • Spany tokenów są konwertowane tylko wtedy, gdy mają wstępnie utworzony styl, co ma miejsce w przypadku mapy themes. Przy pojedynczym theme spany tokenów zachowują style inline style="color:…", tak jak w 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

Konstruktor przyjmuje (classPrefix, classSuffix, classReplacer); domyślny prefiks to __shiki_.