Вход Регистрация
Файл: vendor/nunomaduro/termwind/src/Html/CodeRenderer.php
Строк: 372
<?php

declare(strict_types=1);

namespace 
TermwindHtml;

use 
TermwindComponentsElement;
use 
TermwindTermwind;
use 
TermwindValueObjectsNode;

/**
 * @internal
 */
final class CodeRenderer
{
    public const 
TOKEN_DEFAULT 'token_default';

    public const 
TOKEN_COMMENT 'token_comment';

    public const 
TOKEN_STRING 'token_string';

    public const 
TOKEN_HTML 'token_html';

    public const 
TOKEN_KEYWORD 'token_keyword';

    public const 
ACTUAL_LINE_MARK 'actual_line_mark';

    public const 
LINE_NUMBER 'line_number';

    private const 
ARROW_SYMBOL_UTF8 '➜';

    private const 
DELIMITER_UTF8 '▕ '// '▶';

    
private const LINE_NUMBER_DIVIDER 'line_divider';

    private const 
MARKED_LINE_NUMBER 'marked_line';

    private const 
WIDTH 3;

    
/**
     * Holds the theme.
     *
     * @var array<string, string>
     */
    
private const THEME = [
        
self::TOKEN_STRING => 'text-gray',
        
self::TOKEN_COMMENT => 'text-gray italic',
        
self::TOKEN_KEYWORD => 'text-magenta strong',
        
self::TOKEN_DEFAULT => 'strong',
        
self::TOKEN_HTML => 'text-blue strong',

        
self::ACTUAL_LINE_MARK => 'text-red strong',
        
self::LINE_NUMBER => 'text-gray',
        
self::MARKED_LINE_NUMBER => 'italic strong',
        
self::LINE_NUMBER_DIVIDER => 'text-gray',
    ];

    private 
string $delimiter self::DELIMITER_UTF8;

    private 
string $arrow self::ARROW_SYMBOL_UTF8;

    private const 
NO_MARK '    ';

    
/**
     * Highlights HTML content from a given node and converts to the content element.
     */
    
public function toElement(Node $node): Element
    
{
        
$line max((int) $node->getAttribute('line'), 0);
        
$startLine max((int) $node->getAttribute('start-line'), 1);

        
$html $node->getHtml();
        
$lines explode("n"$html);
        
$extraSpaces $this->findExtraSpaces($lines);

        if (
$extraSpaces !== '') {
            
$lines array_map(static function (string $line) use ($extraSpaces): string {
                return 
str_starts_with($line$extraSpaces) ? substr($linestrlen($extraSpaces)) : $line;
            }, 
$lines);
            
$html implode("n"$lines);
        }

        
$tokenLines $this->getHighlightedLines(trim($html"n"), $startLine);
        
$lines $this->colorLines($tokenLines);
        
$lines $this->lineNumbers($lines$line);

        return 
Termwind::div(trim($lines"n"));
    }

    
/**
     * Finds extra spaces which should be removed from HTML.
     *
     * @param  array<int, string>  $lines
     */
    
private function findExtraSpaces(array $lines): string
    
{
        foreach (
$lines as $line) {
            if (
$line === '') {
                continue;
            }

            if (
preg_replace('/s+/'''$line) === '') {
                return 
$line;
            }
        }

        return 
'';
    }

    
/**
     * Returns content split into lines with numbers.
     *
     * @return array<int, array<int, array{0: string, 1: non-empty-string}>>
     */
    
private function getHighlightedLines(string $sourceint $startLine): array
    {
        
$source str_replace(["rn""r"], "n"$source);
        
$tokens $this->tokenize($source);

        return 
$this->splitToLines($tokens$startLine 1);
    }

    
/**
     * Splits content into tokens.
     *
     * @return array<int, array{0: string, 1: string}>
     */
    
private function tokenize(string $source): array
    {
        
$tokens token_get_all($source);

        
$output = [];
        
$currentType null;
        
$newType self::TOKEN_KEYWORD;
        
$buffer '';

        foreach (
$tokens as $token) {
            if (
is_array($token)) {
                if (
$token[0] !== T_WHITESPACE) {
                    
$newType match ($token[0]) {
                        
T_OPEN_TAGT_OPEN_TAG_WITH_ECHOT_CLOSE_TAGT_STRINGT_VARIABLE,
                        
T_DIRT_FILET_METHOD_CT_DNUMBERT_LNUMBERT_NS_C,
                        
T_LINET_CLASS_CT_FUNC_CT_TRAIT_C => self::TOKEN_DEFAULT,
                        
T_COMMENTT_DOC_COMMENT => self::TOKEN_COMMENT,
                        
T_ENCAPSED_AND_WHITESPACET_CONSTANT_ENCAPSED_STRING => self::TOKEN_STRING,
                        
T_INLINE_HTML => self::TOKEN_HTML,
                        default => 
self::TOKEN_KEYWORD
                    
};
                }
            } else {
                
$newType $token === '"' self::TOKEN_STRING self::TOKEN_KEYWORD;
            }

            if (
$currentType === null) {
                
$currentType $newType;
            }

            if (
$currentType !== $newType) {
                
$output[] = [$currentType$buffer];
                
$buffer '';
                
$currentType $newType;
            }

            
$buffer .= is_array($token) ? $token[1] : $token;
        }

        
$output[] = [$newType$buffer];

        return 
$output;
    }

    
/**
     * Splits tokens into lines.
     *
     * @param  array<int, array{0: string, 1: string}>  $tokens
     * @return array<int, array<int, array{0: string, 1: non-empty-string}>>
     */
    
private function splitToLines(array $tokensint $startLine): array
    {
        
$lines = [];

        
$line = [];
        foreach (
$tokens as $token) {
            foreach (
explode("n"$token[1]) as $count => $tokenLine) {
                if (
$count 0) {
                    
$lines[$startLine++] = $line;
                    
$line = [];
                }

                if (
$tokenLine === '') {
                    continue;
                }

                
$line[] = [$token[0], $tokenLine];
            }
        }

        
$lines[$startLine++] = $line;

        return 
$lines;
    }

    
/**
     * Applies colors to tokens according to a color schema.
     *
     * @param  array<int, array<int, array{0: string, 1: non-empty-string}>>  $tokenLines
     * @return array<int, string>
     */
    
private function colorLines(array $tokenLines): array
    {
        
$lines = [];

        foreach (
$tokenLines as $lineCount => $tokenLine) {
            
$line '';
            foreach (
$tokenLine as $token) {
                [
$tokenType$tokenValue] = $token;
                
$line .= $this->styleToken($tokenType$tokenValue);
            }

            
$lines[$lineCount] = $line;
        }

        return 
$lines;
    }

    
/**
     * Prepends line numbers into lines.
     *
     * @param  array<int, string>  $lines
     */
    
private function lineNumbers(array $linesint $markLine): string
    
{
        
$lastLine = (int) array_key_last($lines);
        
$lineLength strlen((string) ($lastLine 1));
        
$lineLength $lineLength self::WIDTH self::WIDTH $lineLength;

        
$snippet '';
        
$mark '  '.$this->arrow.' ';
        foreach (
$lines as $i => $line) {
            
$coloredLineNumber $this->coloredLineNumber(self::LINE_NUMBER$i$lineLength);

            if (
!== $markLine) {
                
$snippet .= ($markLine === $i 1
                    
$this->styleToken(self::ACTUAL_LINE_MARK$mark)
                    : 
self::NO_MARK
                
);

                
$coloredLineNumber = ($markLine === $i ?
                    
$this->coloredLineNumber(self::MARKED_LINE_NUMBER$i$lineLength) :
                    
$coloredLineNumber
                
);
            }

            
$snippet .= $coloredLineNumber;
            
$snippet .= $this->styleToken(self::LINE_NUMBER_DIVIDER$this->delimiter);
            
$snippet .= $line.PHP_EOL;
        }

        return 
$snippet;
    }

    
/**
     * Formats line number and applies color according to a color schema.
     */
    
private function coloredLineNumber(string $tokenint $lineNumberint $length): string
    
{
        return 
$this->styleToken(
            
$tokenstr_pad((string) ($lineNumber 1), $length' 'STR_PAD_LEFT)
        );
    }

    
/**
     * Formats string and applies color according to a color schema.
     */
    
private function styleToken(string $tokenstring $string): string
    
{
        return (string) 
Termwind::span($stringself::THEME[$token]);
    }
}
Онлайн: 0
Реклама