Skip to content

Commit

Permalink
BlueScreen: implemented PHP syntax highlighter
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Sep 26, 2023
1 parent 193f8e5 commit df5a762
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 59 deletions.
76 changes: 51 additions & 25 deletions src/Tracy/BlueScreen/CodeHighlighter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class CodeHighlighter
{
private const DisplayLines = 15;


/**
* Extract a snippet from the code, highlights the row and column, and adds line numbers.
*/
Expand Down Expand Up @@ -56,7 +57,7 @@ public static function highlightLine(string $html, int $line, int $column = 0):
1,
);
}
$out .= sprintf("<span class='tracy-line-highlight'>%{$numWidth}s: %s\n</span>%s", $n, $s, implode('', $openTags));
$out .= sprintf("<span class='tracy-line-highlight'>%{$numWidth}s: %s</span>\n%s", $n, $s, implode('', $openTags));
} else {
$out .= sprintf("<span class='tracy-line'>%{$numWidth}s:</span> %s\n", $n, $lines[$n]);
}
Expand All @@ -70,25 +71,50 @@ public static function highlightLine(string $html, int $line, int $column = 0):
/**
* Returns syntax highlighted source code.
*/
public static function highlightPhp(string $source, int $line, int $column = 0): string
public static function highlightPhp(string $code, int $line, int $column = 0): string
{
if (function_exists('ini_set')) {
ini_set('highlight.comment', '#998; font-style: italic');
ini_set('highlight.default', '#000');
ini_set('highlight.html', '#06B');
ini_set('highlight.keyword', '#D24; font-weight: bold');
ini_set('highlight.string', '#080');
}
$html = self::highlightPhpCode($code);
$html = self::highlightLine($html, $line, $column);
return "<pre class='tracy-code'><div><code>$html</code></div></pre>";
}


private static function highlightPhpCode(string $code): string
{
$code = str_replace("\r\n", "\n", $code);
$code = preg_replace('#(__halt_compiler\s*\(\)\s*;).*#is', '$1', $code);
$code = rtrim($code);
$code = preg_replace('#/\*sensitive\{\*/.*?/\*\}\*/#s', Dumper\Describer::HiddenValue, $code);

$last = $out = '';
foreach (\PhpToken::tokenize($code) as $token) {
$next = match ($token->id) {
T_COMMENT, T_DOC_COMMENT, T_INLINE_HTML => 'tracy-code-comment',
T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG, T_LINE, T_FILE, T_DIR, T_TRAIT_C, T_METHOD_C, T_FUNC_C, T_NS_C, T_CLASS_C,
T_STRING, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAME_RELATIVE => '',
T_LNUMBER, T_DNUMBER => 'tracy-dump-number',
T_VARIABLE => 'tracy-code-var',
T_ENCAPSED_AND_WHITESPACE, T_CONSTANT_ENCAPSED_STRING => 'tracy-dump-string',
T_WHITESPACE => $last,
default => 'tracy-code-keyword',
};

if ($last !== $next) {
if ($last !== '') {
$out .= '</span>';
}
$last = $next;
if ($last !== '') {
$out .= "<span class='$last'>";
}
}

$source = preg_replace('#(__halt_compiler\s*\(\)\s*;).*#is', '$1', $source);
$source = str_replace(["\r\n", "\r"], "\n", $source);
$source = preg_replace('#/\*sensitive\{\*/.*?/\*\}\*/#s', Dumper\Describer::HiddenValue, $source);
$source = explode("\n", highlight_string($source, true));
$out = $source[0]; // <code><span color=highlight.html>
$tmp = str_replace('<br />', "\n", $source[1]);
$out .= self::highlightLine($tmp, $line, $column);
$out = str_replace('&nbsp;', ' ', $out) . $source[2] . @$source[3];
return "<pre class='tracy-code'><div>$out</div></pre>";
$out .= strtr($token->text, ['<' => '&lt;', '>' => '&gt;', '&' => '&amp;', "\t" => ' ', "\n" => "\n"]);
}
if ($last !== '') {
$out .= '</span>';
}
return $out;
}


Expand All @@ -98,23 +124,23 @@ public static function highlightPhp(string $source, int $line, int $column = 0):
public static function highlightPhpCli(string $code, int $line, int $column = 0): string
{
$colors = [
'color: ' . ini_get('highlight.comment') => '1;30',
'color: ' . ini_get('highlight.default') => '1;36',
'color: ' . ini_get('highlight.html') => '1;35',
'color: ' . ini_get('highlight.keyword') => '1;37',
'color: ' . ini_get('highlight.string') => '1;32',
'tracy-code-comment' => '1;30',
'tracy-code-keyword' => '1;36',
'tracy-dump-string' => '1;36',
'tracy-dump-number' => '1;32',
'tracy-code-var' => '1;37',
'tracy-line' => '1;30',
'tracy-line-highlight' => "1;37m\e[41",
];
$stack = ['0'];

$html = preg_replace_callback(
'#<\w+(?: (class|style)=["\'](.*?)["\'])?[^>]*>|</\w+>#',
'#<\w+(?: class=["\'](.*?)["\'])?[^>]*>|</\w+>#',
function ($m) use ($colors, &$stack): string {
if ($m[0][1] === '/') {
array_pop($stack);
} else {
$stack[] = isset($m[2], $colors[$m[2]]) ? $colors[$m[2]] : '0';
$stack[] = isset($m[1], $colors[$m[1]]) ? $colors[$m[1]] : '0';
}
return "\e[0m\e[" . end($stack) . 'm';
},
Expand Down
16 changes: 15 additions & 1 deletion src/Tracy/BlueScreen/assets/bluescreen.css
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,28 @@ html.tracy-bs-visible body {
white-space: pre;
}

#tracy-bs .tracy-code-comment {
color: rgba(0, 0, 0, 0.5);
font-style: italic;
}

#tracy-bs .tracy-code-keyword {
color: #D24;
font-weight: bold;
}

#tracy-bs .tracy-code-var {
font-weight: bold;
}

#tracy-bs .tracy-line-highlight {
background: #CD1818;
color: white;
font-weight: bold;
font-style: normal;
display: block;
padding: 0 1ch;
margin: 0 -1ch;
margin: 0 -1ch -1lh;
}

#tracy-bs .tracy-column-highlight {
Expand Down
65 changes: 32 additions & 33 deletions tests/Tracy/expected/Debugger.exception.html.expect
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@

<div class="tracy-section-panel">
<p><b>File:</b> %a%</p>
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span style="color: #06B"><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span>
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span class='tracy-code-keyword'><span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">second</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">$arg2</span><span style="color: #D24; font-weight: bold">)
<span class='tracy-line'>%d%:</span> function </span>second<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>, </span><span class='tracy-code-var'>$arg2</span><span class='tracy-code-keyword'>)
<span class='tracy-line'>%d%:</span> {
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">third</span><span style="color: #D24; font-weight: bold">([</span><span style="color: #000">1</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">2</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">3</span><span style="color: #D24; font-weight: bold">]);
<span class='tracy-line'>%d%:</span> </span>third<span class='tracy-code-keyword'>([</span><span class='tracy-dump-number'>1</span><span class='tracy-code-keyword'>, </span><span class='tracy-dump-number'>2</span><span class='tracy-code-keyword'>, </span><span class='tracy-dump-number'>3</span><span class='tracy-code-keyword'>]);
<span class='tracy-line'>%d%:</span> }
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">third</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">)
<span class='tracy-line'>%d%:</span> function </span>third<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>)
<span class='tracy-line'>%d%:</span> {
</span><span class='tracy-line-highlight'>%d%: throw new Exception('The my exception', 123);
</span><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span> }
</span><span class='tracy-line-highlight'>%d%: throw new Exception('The my exception', 123);</span>
<span class='tracy-code-keyword'><span class='tracy-line'>%d%:</span> }
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">define</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #080">'MY_CONST'</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">123</span><span style="color: #D24; font-weight: bold">);
</span></span></code></div></pre>
<span class='tracy-line'>%d%:</span> </span>define<span class='tracy-code-keyword'>(</span><span class='tracy-dump-string'>'MY_CONST'</span><span class='tracy-code-keyword'>, </span><span class='tracy-dump-number'>123</span><span class='tracy-code-keyword'>);
</span></code></div></pre>
</div>
</section>

Expand All @@ -67,22 +67,22 @@
</div>

<div class="tracy-callstack-additional tracy-collapsed">
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span style="color: #06B"><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span>
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span class='tracy-code-keyword'><span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">first</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">$arg2</span><span style="color: #D24; font-weight: bold">)
<span class='tracy-line'>%d%:</span> function </span>first<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>, </span><span class='tracy-code-var'>$arg2</span><span class='tracy-code-keyword'>)
<span class='tracy-line'>%d%:</span> {
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">second</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">true</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">false</span><span style="color: #D24; font-weight: bold">);
<span class='tracy-line'>%d%:</span> </span>second<span class='tracy-code-keyword'>(</span>true<span class='tracy-code-keyword'>, </span>false<span class='tracy-code-keyword'>);
<span class='tracy-line'>%d%:</span> }
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">second</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">$arg2</span><span style="color: #D24; font-weight: bold">)
<span class='tracy-line'>%d%:</span> function </span>second<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>, </span><span class='tracy-code-var'>$arg2</span><span class='tracy-code-keyword'>)
<span class='tracy-line'>%d%:</span> {
</span><span class='tracy-line-highlight'>%d%: third([1, 2, 3]);
</span><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span> }
</span><span class='tracy-line-highlight'>%d%: third([1, 2, 3]);</span>
<span class='tracy-code-keyword'><span class='tracy-line'>%d%:</span> }
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">third</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">)
</span></span></code></div></pre>
<span class='tracy-line'>%d%:</span> function </span>third<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>)
</span></code></div></pre>

<table class="tracy-callstack-args">
<tr><th>$arg1</th><td><pre class="tracy-dump tracy-light" data-tracy-dump='[[0,1],[1,2],[2,3]]'></pre>
Expand All @@ -99,22 +99,22 @@
</div>

<div class="tracy-callstack-additional tracy-collapsed">
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span style="color: #06B"><span style="color: #D24; font-weight: bold"><span class='tracy-line'>19:</span>
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span class='tracy-code-keyword'><span class='tracy-line'>19:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">Debugger</span><span style="color: #D24; font-weight: bold">::</span><span style="color: #000">$productionMode </span><span style="color: #D24; font-weight: bold">= </span><span style="color: #000">false</span><span style="color: #D24; font-weight: bold">;
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">setHtmlMode</span><span style="color: #D24; font-weight: bold">();
<span class='tracy-line'>%d%:</span> </span>Debugger<span class='tracy-code-keyword'>::</span><span class='tracy-code-var'>$productionMode </span><span class='tracy-code-keyword'>= </span>false<span class='tracy-code-keyword'>;
<span class='tracy-line'>%d%:</span> </span>setHtmlMode<span class='tracy-code-keyword'>();
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">Debugger</span><span style="color: #D24; font-weight: bold">::</span><span style="color: #000">enable</span><span style="color: #D24; font-weight: bold">();
<span class='tracy-line'>%d%:</span> </span>Debugger<span class='tracy-code-keyword'>::</span>enable<span class='tracy-code-keyword'>();
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">first</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">$arg2</span><span style="color: #D24; font-weight: bold">)
<span class='tracy-line'>%d%:</span> function </span>first<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>, </span><span class='tracy-code-var'>$arg2</span><span class='tracy-code-keyword'>)
<span class='tracy-line'>%d%:</span> {
</span><span class='tracy-line-highlight'>%d%: second(true, false);
</span><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span> }
</span><span class='tracy-line-highlight'>%d%: second(true, false);</span>
<span class='tracy-code-keyword'><span class='tracy-line'>%d%:</span> }
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">second</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">$arg2</span><span style="color: #D24; font-weight: bold">)
</span></span></code></div></pre>
<span class='tracy-line'>%d%:</span> function </span>second<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>, </span><span class='tracy-code-var'>$arg2</span><span class='tracy-code-keyword'>)
</span></code></div></pre>

<table class="tracy-callstack-args">
<tr><th>$arg1</th><td><pre class="tracy-dump tracy-light"><span class="tracy-dump-bool">true</span></pre>
Expand All @@ -133,19 +133,18 @@
</div>

<div class="tracy-callstack-additional tracy-collapsed">
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span style="color: #06B"><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span>
<pre title="Ctrl-Click to open in editor" data-tracy-href="%a%" class='tracy-code'><div><code><span class='tracy-code-keyword'><span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> function </span><span style="color: #000">third</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #000">$arg1</span><span style="color: #D24; font-weight: bold">)
<span class='tracy-line'>%d%:</span> function </span>third<span class='tracy-code-keyword'>(</span><span class='tracy-code-var'>$arg1</span><span class='tracy-code-keyword'>)
<span class='tracy-line'>%d%:</span> {
<span class='tracy-line'>%d%:</span> throw new </span><span style="color: #000">Exception</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #080">'The my exception'</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">123</span><span style="color: #D24; font-weight: bold">);
<span class='tracy-line'>%d%:</span> throw new </span>Exception<span class='tracy-code-keyword'>(</span><span class='tracy-dump-string'>'The my exception'</span><span class='tracy-code-keyword'>, </span><span class='tracy-dump-number'>123</span><span class='tracy-code-keyword'>);
<span class='tracy-line'>%d%:</span> }
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span>
<span class='tracy-line'>%d%:</span> </span><span style="color: #000">define</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #080">'MY_CONST'</span><span style="color: #D24; font-weight: bold">, </span><span style="color: #000">123</span><span style="color: #D24; font-weight: bold">);
<span class='tracy-line'>%d%:</span> @</span><span style="color: #000">hex2bin</span><span style="color: #D24; font-weight: bold">(</span><span style="color: #080">'a'</span><span style="color: #D24; font-weight: bold">); </span><span style="color: #998; font-style: italic">// E_WARNING
</span><span class='tracy-line-highlight'>%d%: first(10, 'any string');
</span><span style="color: #D24; font-weight: bold"><span class='tracy-line'>%d%:</span> </span>
</span></code></div></pre>
<span class='tracy-line'>%d%:</span> </span>define<span class='tracy-code-keyword'>(</span><span class='tracy-dump-string'>'MY_CONST'</span><span class='tracy-code-keyword'>, </span><span class='tracy-dump-number'>123</span><span class='tracy-code-keyword'>);
<span class='tracy-line'>%d%:</span> @</span>hex2bin<span class='tracy-code-keyword'>(</span><span class='tracy-dump-string'>'a'</span><span class='tracy-code-keyword'>); </span><span class='tracy-code-comment'>// E_WARNING
</span><span class='tracy-line-highlight'>%d%: first(10, 'any string');</span>
</code></div></pre>

<table class="tracy-callstack-args">
<tr><th>$arg1</th><td><pre class="tracy-dump tracy-light"><span class="tracy-dump-number">10</span></pre>
Expand Down

0 comments on commit df5a762

Please sign in to comment.