%PDF- %PDF-
Direktori : /home/silvzytp/calling_code/vendor/nunomaduro/termwind/src/Html/ |
Current File : //home/silvzytp/calling_code/vendor/nunomaduro/termwind/src/Html/CodeRenderer.php |
<?php declare(strict_types=1); namespace Termwind\Html; use Termwind\Components\Element; use Termwind\Termwind; use Termwind\ValueObjects\Node; /** * @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($line, strlen($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 $source, int $startLine): array { $source = str_replace(["\r\n", "\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_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG, T_STRING, T_VARIABLE, T_DIR, T_FILE, T_METHOD_C, T_DNUMBER, T_LNUMBER, T_NS_C, T_LINE, T_CLASS_C, T_FUNC_C, T_TRAIT_C => self::TOKEN_DEFAULT, T_COMMENT, T_DOC_COMMENT => self::TOKEN_COMMENT, T_ENCAPSED_AND_WHITESPACE, T_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 * @param int $startLine * @return array<int, array<int, array{0: string, 1: non-empty-string}>> */ private function splitToLines(array $tokens, int $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 * @param int $markLine * @return string */ private function lineNumbers(array $lines, int $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 (0 !== $markLine) { $snippet .= ($markLine === $i + 1 ? $this->styleToken(self::ACTUAL_LINE_MARK, $mark) : self::NO_MARK ); $coloredLineNumber = ($markLine === $i + 1 ? $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 $token, int $lineNumber, int $length): string { return $this->styleToken( $token, str_pad((string) ($lineNumber + 1), $length, ' ', STR_PAD_LEFT) ); } /** * Formats string and applies color according to a color schema. */ private function styleToken(string $token, string $string): string { return (string) Termwind::span($string, self::THEME[$token]); } }