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