Chris@17: 'red', Chris@17: self::TOKEN_COMMENT => 'yellow', Chris@17: self::TOKEN_KEYWORD => 'green', Chris@17: self::TOKEN_DEFAULT => 'default', Chris@17: self::TOKEN_HTML => 'cyan', Chris@17: Chris@17: self::ACTUAL_LINE_MARK => 'red', Chris@17: self::LINE_NUMBER => 'dark_gray', Chris@17: ); Chris@17: Chris@17: /** Chris@17: * @param ConsoleColor $color Chris@17: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@17: */ Chris@17: public function __construct(ConsoleColor $color) Chris@17: { Chris@17: $this->color = $color; Chris@17: Chris@17: foreach ($this->defaultTheme as $name => $styles) { Chris@17: if (!$this->color->hasTheme($name)) { Chris@17: $this->color->addTheme($name, $styles); Chris@17: } Chris@17: } Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param string $source Chris@17: * @param int $lineNumber Chris@17: * @param int $linesBefore Chris@17: * @param int $linesAfter Chris@17: * @return string Chris@17: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@17: * @throws \InvalidArgumentException Chris@17: */ Chris@17: public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2) Chris@17: { Chris@17: $tokenLines = $this->getHighlightedLines($source); Chris@17: Chris@17: $offset = $lineNumber - $linesBefore - 1; Chris@17: $offset = max($offset, 0); Chris@17: $length = $linesAfter + $linesBefore + 1; Chris@17: $tokenLines = array_slice($tokenLines, $offset, $length, $preserveKeys = true); Chris@17: Chris@17: $lines = $this->colorLines($tokenLines); Chris@17: Chris@17: return $this->lineNumbers($lines, $lineNumber); Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param string $source Chris@17: * @return string Chris@17: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@17: * @throws \InvalidArgumentException Chris@17: */ Chris@17: public function getWholeFile($source) Chris@17: { Chris@17: $tokenLines = $this->getHighlightedLines($source); Chris@17: $lines = $this->colorLines($tokenLines); Chris@17: return implode(PHP_EOL, $lines); Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param string $source Chris@17: * @return string Chris@17: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@17: * @throws \InvalidArgumentException Chris@17: */ Chris@17: public function getWholeFileWithLineNumbers($source) Chris@17: { Chris@17: $tokenLines = $this->getHighlightedLines($source); Chris@17: $lines = $this->colorLines($tokenLines); Chris@17: return $this->lineNumbers($lines); Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param string $source Chris@17: * @return array Chris@17: */ Chris@17: private function getHighlightedLines($source) Chris@17: { Chris@17: $source = str_replace(array("\r\n", "\r"), "\n", $source); Chris@17: $tokens = $this->tokenize($source); Chris@17: return $this->splitToLines($tokens); Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param string $source Chris@17: * @return array Chris@17: */ Chris@17: private function tokenize($source) Chris@17: { Chris@17: $tokens = token_get_all($source); Chris@17: Chris@17: $output = array(); Chris@17: $currentType = null; Chris@17: $buffer = ''; Chris@17: Chris@17: foreach ($tokens as $token) { Chris@17: if (is_array($token)) { Chris@17: switch ($token[0]) { Chris@17: case T_WHITESPACE: Chris@17: break; Chris@17: Chris@17: case T_OPEN_TAG: Chris@17: case T_OPEN_TAG_WITH_ECHO: Chris@17: case T_CLOSE_TAG: Chris@17: case T_STRING: Chris@17: case T_VARIABLE: Chris@17: Chris@17: // Constants Chris@17: case T_DIR: Chris@17: case T_FILE: Chris@17: case T_METHOD_C: Chris@17: case T_DNUMBER: Chris@17: case T_LNUMBER: Chris@17: case T_NS_C: Chris@17: case T_LINE: Chris@17: case T_CLASS_C: Chris@17: case T_FUNC_C: Chris@17: case T_TRAIT_C: Chris@17: $newType = self::TOKEN_DEFAULT; Chris@17: break; Chris@17: Chris@17: case T_COMMENT: Chris@17: case T_DOC_COMMENT: Chris@17: $newType = self::TOKEN_COMMENT; Chris@17: break; Chris@17: Chris@17: case T_ENCAPSED_AND_WHITESPACE: Chris@17: case T_CONSTANT_ENCAPSED_STRING: Chris@17: $newType = self::TOKEN_STRING; Chris@17: break; Chris@17: Chris@17: case T_INLINE_HTML: Chris@17: $newType = self::TOKEN_HTML; Chris@17: break; Chris@17: Chris@17: default: Chris@17: $newType = self::TOKEN_KEYWORD; Chris@17: } Chris@17: } else { Chris@17: $newType = $token === '"' ? self::TOKEN_STRING : self::TOKEN_KEYWORD; Chris@17: } Chris@17: Chris@17: if ($currentType === null) { Chris@17: $currentType = $newType; Chris@17: } Chris@17: Chris@17: if ($currentType !== $newType) { Chris@17: $output[] = array($currentType, $buffer); Chris@17: $buffer = ''; Chris@17: $currentType = $newType; Chris@17: } Chris@17: Chris@17: $buffer .= is_array($token) ? $token[1] : $token; Chris@17: } Chris@17: Chris@17: if (isset($newType)) { Chris@17: $output[] = array($newType, $buffer); Chris@17: } Chris@17: Chris@17: return $output; Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param array $tokens Chris@17: * @return array Chris@17: */ Chris@17: private function splitToLines(array $tokens) Chris@17: { Chris@17: $lines = array(); Chris@17: Chris@17: $line = array(); Chris@17: foreach ($tokens as $token) { Chris@17: foreach (explode("\n", $token[1]) as $count => $tokenLine) { Chris@17: if ($count > 0) { Chris@17: $lines[] = $line; Chris@17: $line = array(); Chris@17: } Chris@17: Chris@17: if ($tokenLine === '') { Chris@17: continue; Chris@17: } Chris@17: Chris@17: $line[] = array($token[0], $tokenLine); Chris@17: } Chris@17: } Chris@17: Chris@17: $lines[] = $line; Chris@17: Chris@17: return $lines; Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param array $tokenLines Chris@17: * @return array Chris@17: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@17: * @throws \InvalidArgumentException Chris@17: */ Chris@17: private function colorLines(array $tokenLines) Chris@17: { Chris@17: $lines = array(); Chris@17: foreach ($tokenLines as $lineCount => $tokenLine) { Chris@17: $line = ''; Chris@17: foreach ($tokenLine as $token) { Chris@17: list($tokenType, $tokenValue) = $token; Chris@17: if ($this->color->hasTheme($tokenType)) { Chris@17: $line .= $this->color->apply($tokenType, $tokenValue); Chris@17: } else { Chris@17: $line .= $tokenValue; Chris@17: } Chris@17: } Chris@17: $lines[$lineCount] = $line; Chris@17: } Chris@17: Chris@17: return $lines; Chris@17: } Chris@17: Chris@17: /** Chris@17: * @param array $lines Chris@17: * @param null|int $markLine Chris@17: * @return string Chris@17: * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException Chris@17: */ Chris@17: private function lineNumbers(array $lines, $markLine = null) Chris@17: { Chris@17: end($lines); Chris@17: $lineStrlen = strlen(key($lines) + 1); Chris@17: Chris@17: $snippet = ''; Chris@17: foreach ($lines as $i => $line) { Chris@17: if ($markLine !== null) { Chris@17: $snippet .= ($markLine === $i + 1 ? $this->color->apply(self::ACTUAL_LINE_MARK, ' > ') : ' '); Chris@17: } Chris@17: Chris@17: $snippet .= $this->color->apply(self::LINE_NUMBER, str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| '); Chris@17: $snippet .= $line . PHP_EOL; Chris@17: } Chris@17: Chris@17: return $snippet; Chris@17: } Chris@17: }