annotate vendor/jakub-onderka/php-console-highlighter/src/Highlighter.php @ 4:a9cd425dd02b

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:11:55 +0000
parents
children
rev   line source
Chris@4 1 <?php
Chris@4 2 namespace JakubOnderka\PhpConsoleHighlighter;
Chris@4 3
Chris@4 4 use JakubOnderka\PhpConsoleColor\ConsoleColor;
Chris@4 5
Chris@4 6 class Highlighter
Chris@4 7 {
Chris@4 8 const TOKEN_DEFAULT = 'token_default',
Chris@4 9 TOKEN_COMMENT = 'token_comment',
Chris@4 10 TOKEN_STRING = 'token_string',
Chris@4 11 TOKEN_HTML = 'token_html',
Chris@4 12 TOKEN_KEYWORD = 'token_keyword';
Chris@4 13
Chris@4 14 const ACTUAL_LINE_MARK = 'actual_line_mark',
Chris@4 15 LINE_NUMBER = 'line_number';
Chris@4 16
Chris@4 17 /** @var ConsoleColor */
Chris@4 18 private $color;
Chris@4 19
Chris@4 20 /** @var array */
Chris@4 21 private $defaultTheme = array(
Chris@4 22 self::TOKEN_STRING => 'red',
Chris@4 23 self::TOKEN_COMMENT => 'yellow',
Chris@4 24 self::TOKEN_KEYWORD => 'green',
Chris@4 25 self::TOKEN_DEFAULT => 'default',
Chris@4 26 self::TOKEN_HTML => 'cyan',
Chris@4 27
Chris@4 28 self::ACTUAL_LINE_MARK => 'red',
Chris@4 29 self::LINE_NUMBER => 'dark_gray',
Chris@4 30 );
Chris@4 31
Chris@4 32 /**
Chris@4 33 * @param ConsoleColor $color
Chris@4 34 * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
Chris@4 35 */
Chris@4 36 public function __construct(ConsoleColor $color)
Chris@4 37 {
Chris@4 38 $this->color = $color;
Chris@4 39
Chris@4 40 foreach ($this->defaultTheme as $name => $styles) {
Chris@4 41 if (!$this->color->hasTheme($name)) {
Chris@4 42 $this->color->addTheme($name, $styles);
Chris@4 43 }
Chris@4 44 }
Chris@4 45 }
Chris@4 46
Chris@4 47 /**
Chris@4 48 * @param string $source
Chris@4 49 * @param int $lineNumber
Chris@4 50 * @param int $linesBefore
Chris@4 51 * @param int $linesAfter
Chris@4 52 * @return string
Chris@4 53 * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
Chris@4 54 * @throws \InvalidArgumentException
Chris@4 55 */
Chris@4 56 public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2)
Chris@4 57 {
Chris@4 58 $tokenLines = $this->getHighlightedLines($source);
Chris@4 59
Chris@4 60 $offset = $lineNumber - $linesBefore - 1;
Chris@4 61 $offset = max($offset, 0);
Chris@4 62 $length = $linesAfter + $linesBefore + 1;
Chris@4 63 $tokenLines = array_slice($tokenLines, $offset, $length, $preserveKeys = true);
Chris@4 64
Chris@4 65 $lines = $this->colorLines($tokenLines);
Chris@4 66
Chris@4 67 return $this->lineNumbers($lines, $lineNumber);
Chris@4 68 }
Chris@4 69
Chris@4 70 /**
Chris@4 71 * @param string $source
Chris@4 72 * @return string
Chris@4 73 * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
Chris@4 74 * @throws \InvalidArgumentException
Chris@4 75 */
Chris@4 76 public function getWholeFile($source)
Chris@4 77 {
Chris@4 78 $tokenLines = $this->getHighlightedLines($source);
Chris@4 79 $lines = $this->colorLines($tokenLines);
Chris@4 80 return implode(PHP_EOL, $lines);
Chris@4 81 }
Chris@4 82
Chris@4 83 /**
Chris@4 84 * @param string $source
Chris@4 85 * @return string
Chris@4 86 * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
Chris@4 87 * @throws \InvalidArgumentException
Chris@4 88 */
Chris@4 89 public function getWholeFileWithLineNumbers($source)
Chris@4 90 {
Chris@4 91 $tokenLines = $this->getHighlightedLines($source);
Chris@4 92 $lines = $this->colorLines($tokenLines);
Chris@4 93 return $this->lineNumbers($lines);
Chris@4 94 }
Chris@4 95
Chris@4 96 /**
Chris@4 97 * @param string $source
Chris@4 98 * @return array
Chris@4 99 */
Chris@4 100 private function getHighlightedLines($source)
Chris@4 101 {
Chris@4 102 $source = str_replace(array("\r\n", "\r"), "\n", $source);
Chris@4 103 $tokens = $this->tokenize($source);
Chris@4 104 return $this->splitToLines($tokens);
Chris@4 105 }
Chris@4 106
Chris@4 107 /**
Chris@4 108 * @param string $source
Chris@4 109 * @return array
Chris@4 110 */
Chris@4 111 private function tokenize($source)
Chris@4 112 {
Chris@4 113 $tokens = token_get_all($source);
Chris@4 114
Chris@4 115 $output = array();
Chris@4 116 $currentType = null;
Chris@4 117 $buffer = '';
Chris@4 118
Chris@4 119 foreach ($tokens as $token) {
Chris@4 120 if (is_array($token)) {
Chris@4 121 switch ($token[0]) {
Chris@4 122 case T_WHITESPACE:
Chris@4 123 break;
Chris@4 124
Chris@4 125 case T_OPEN_TAG:
Chris@4 126 case T_OPEN_TAG_WITH_ECHO:
Chris@4 127 case T_CLOSE_TAG:
Chris@4 128 case T_STRING:
Chris@4 129 case T_VARIABLE:
Chris@4 130
Chris@4 131 // Constants
Chris@4 132 case T_DIR:
Chris@4 133 case T_FILE:
Chris@4 134 case T_METHOD_C:
Chris@4 135 case T_DNUMBER:
Chris@4 136 case T_LNUMBER:
Chris@4 137 case T_NS_C:
Chris@4 138 case T_LINE:
Chris@4 139 case T_CLASS_C:
Chris@4 140 case T_FUNC_C:
Chris@4 141 case T_TRAIT_C:
Chris@4 142 $newType = self::TOKEN_DEFAULT;
Chris@4 143 break;
Chris@4 144
Chris@4 145 case T_COMMENT:
Chris@4 146 case T_DOC_COMMENT:
Chris@4 147 $newType = self::TOKEN_COMMENT;
Chris@4 148 break;
Chris@4 149
Chris@4 150 case T_ENCAPSED_AND_WHITESPACE:
Chris@4 151 case T_CONSTANT_ENCAPSED_STRING:
Chris@4 152 $newType = self::TOKEN_STRING;
Chris@4 153 break;
Chris@4 154
Chris@4 155 case T_INLINE_HTML:
Chris@4 156 $newType = self::TOKEN_HTML;
Chris@4 157 break;
Chris@4 158
Chris@4 159 default:
Chris@4 160 $newType = self::TOKEN_KEYWORD;
Chris@4 161 }
Chris@4 162 } else {
Chris@4 163 $newType = $token === '"' ? self::TOKEN_STRING : self::TOKEN_KEYWORD;
Chris@4 164 }
Chris@4 165
Chris@4 166 if ($currentType === null) {
Chris@4 167 $currentType = $newType;
Chris@4 168 }
Chris@4 169
Chris@4 170 if ($currentType !== $newType) {
Chris@4 171 $output[] = array($currentType, $buffer);
Chris@4 172 $buffer = '';
Chris@4 173 $currentType = $newType;
Chris@4 174 }
Chris@4 175
Chris@4 176 $buffer .= is_array($token) ? $token[1] : $token;
Chris@4 177 }
Chris@4 178
Chris@4 179 if (isset($newType)) {
Chris@4 180 $output[] = array($newType, $buffer);
Chris@4 181 }
Chris@4 182
Chris@4 183 return $output;
Chris@4 184 }
Chris@4 185
Chris@4 186 /**
Chris@4 187 * @param array $tokens
Chris@4 188 * @return array
Chris@4 189 */
Chris@4 190 private function splitToLines(array $tokens)
Chris@4 191 {
Chris@4 192 $lines = array();
Chris@4 193
Chris@4 194 $line = array();
Chris@4 195 foreach ($tokens as $token) {
Chris@4 196 foreach (explode("\n", $token[1]) as $count => $tokenLine) {
Chris@4 197 if ($count > 0) {
Chris@4 198 $lines[] = $line;
Chris@4 199 $line = array();
Chris@4 200 }
Chris@4 201
Chris@4 202 if ($tokenLine === '') {
Chris@4 203 continue;
Chris@4 204 }
Chris@4 205
Chris@4 206 $line[] = array($token[0], $tokenLine);
Chris@4 207 }
Chris@4 208 }
Chris@4 209
Chris@4 210 $lines[] = $line;
Chris@4 211
Chris@4 212 return $lines;
Chris@4 213 }
Chris@4 214
Chris@4 215 /**
Chris@4 216 * @param array $tokenLines
Chris@4 217 * @return array
Chris@4 218 * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
Chris@4 219 * @throws \InvalidArgumentException
Chris@4 220 */
Chris@4 221 private function colorLines(array $tokenLines)
Chris@4 222 {
Chris@4 223 $lines = array();
Chris@4 224 foreach ($tokenLines as $lineCount => $tokenLine) {
Chris@4 225 $line = '';
Chris@4 226 foreach ($tokenLine as $token) {
Chris@4 227 list($tokenType, $tokenValue) = $token;
Chris@4 228 if ($this->color->hasTheme($tokenType)) {
Chris@4 229 $line .= $this->color->apply($tokenType, $tokenValue);
Chris@4 230 } else {
Chris@4 231 $line .= $tokenValue;
Chris@4 232 }
Chris@4 233 }
Chris@4 234 $lines[$lineCount] = $line;
Chris@4 235 }
Chris@4 236
Chris@4 237 return $lines;
Chris@4 238 }
Chris@4 239
Chris@4 240 /**
Chris@4 241 * @param array $lines
Chris@4 242 * @param null|int $markLine
Chris@4 243 * @return string
Chris@4 244 * @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
Chris@4 245 */
Chris@4 246 private function lineNumbers(array $lines, $markLine = null)
Chris@4 247 {
Chris@4 248 end($lines);
Chris@4 249 $lineStrlen = strlen(key($lines) + 1);
Chris@4 250
Chris@4 251 $snippet = '';
Chris@4 252 foreach ($lines as $i => $line) {
Chris@4 253 if ($markLine !== null) {
Chris@4 254 $snippet .= ($markLine === $i + 1 ? $this->color->apply(self::ACTUAL_LINE_MARK, ' > ') : ' ');
Chris@4 255 }
Chris@4 256
Chris@4 257 $snippet .= $this->color->apply(self::LINE_NUMBER, str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| ');
Chris@4 258 $snippet .= $line . PHP_EOL;
Chris@4 259 }
Chris@4 260
Chris@4 261 return $snippet;
Chris@4 262 }
Chris@4 263 }