Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2e9ac2d

Browse files
committed
[Debug] [TwigBundle] Added syntax highlighters for PHP and Twig
1 parent 74f7357 commit 2e9ac2d

File tree

11 files changed

+424
-4
lines changed

11 files changed

+424
-4
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Debug;
13+
14+
use Symfony\Component\Debug\Highlighter\Highlighter;
15+
16+
/**
17+
* Simple Twig syntax highlighter
18+
*
19+
* @author Martin Hasoň <[email protected]>
20+
*/
21+
class TwigHighlighter extends Highlighter
22+
{
23+
protected $regexString = '"[^"\\\\]*?(?:\\\\.[^"\\\\]*?)*?"|\'[^\'\\\\]*?(?:\\\\.[^\'\\\\]*?)*?\'';
24+
protected $regexTags;
25+
protected $regex;
26+
27+
public function __construct()
28+
{
29+
$this->regexTags = '{({{-?|{%-?|{#-?)((?:'.$this->regexString.'|[^"\']*?)+?)(-?}}|-?%}|-?#})}s';
30+
$this->regexKeywords = 'and|or|with';
31+
$this->regex = '
32+
/(?:
33+
(?P<string>'.$this->regexString.')|
34+
(?P<number>\b\d+(?:\.\d+)?\b)|
35+
(?P<variable>(?<!\.)\b[a-z][a-z0-9_]*(?=\.|\[|\s*=))|
36+
(?P<name>\b[a-z0-9_]+(?=\s*\()|(?<=\||\|\s)[a-z0-9_]+\b)|
37+
(?P<operator>(?:\*\*|\.\.|==|!=|>=|<=|\/\/|\?:|[+\-~\*\/%\.=><\|\(\)\[\]\{\}\?:,]))|
38+
(?P<keyword>\b(?:if|and|or|b-and|b-xor|b-or|in|matches|starts with|ends with|is|not|as|import|with|true|false|null|none)\b)
39+
)/xi'
40+
;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function highlight($code, $from = 1, $to = -1, $line = -1)
47+
{
48+
$oldCode = htmlspecialchars(str_replace(array("\r\n", "\r"), "\n", $code), ENT_NOQUOTES);
49+
$regex = $this->regex;
50+
$code = preg_replace_callback($this->regexTags, function ($matches) use ($regex) {
51+
if ($matches[1] == '{#') {
52+
return '<span class="comment">' . $matches[0] . '</span>';
53+
}
54+
55+
$matches[2] = preg_replace_callback($regex, function ($match) {
56+
$keys = array_keys($match);
57+
58+
return sprintf('<span class="%s">%s</span>', $keys[count($match) - 2], $match[0]);
59+
}, $matches[2]);
60+
61+
if ($matches[1][1] == '%') {
62+
$matches[2] = preg_replace('/^(\s*)([a-z0-9_]+)/i', '\\1<span class="keyword">\\2</span>', $matches[2]);
63+
}
64+
65+
return '<span class="tag">'.$matches[1].'</span>'.$matches[2].'<span class="tag">'.$matches[3].'</span>';
66+
}, $oldCode);
67+
68+
if ('' == $code) {
69+
$code = $oldCode;
70+
}
71+
72+
return $this->createLines(explode("\n", $code), $from, $to, $line);
73+
}
74+
75+
/**
76+
* {@inheritdoc}
77+
*/
78+
public function supports($file)
79+
{
80+
return 'twig' === pathinfo($file, PATHINFO_EXTENSION);
81+
}
82+
}

src/Symfony/Bridge/Twig/Extension/CodeExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public function getFilters()
5454
new \Twig_SimpleFilter('format_file', array($this, 'formatFile'), array('is_safe' => array('html'))),
5555
new \Twig_SimpleFilter('format_file_from_text', array($this, 'formatFileFromText'), array('is_safe' => array('html'))),
5656
new \Twig_SimpleFilter('file_link', array($this, 'getFileLink')),
57+
new \Twig_SimpleFilter('highlight_code', array($this->htmlUtils, 'highlight'), array('is_safe' => array('html'))),
5758
);
5859
}
5960

src/Symfony/Bundle/DebugBundle/DebugBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\DebugBundle;
1313

1414
use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\AddFlattenExceptionProcessorPass;
15+
use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\AddHighlighterPass;
1516
use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\DumpDataCollectorPass;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -51,5 +52,6 @@ public function build(ContainerBuilder $container)
5152

5253
$container->addCompilerPass(new AddFlattenExceptionProcessorPass());
5354
$container->addCompilerPass(new DumpDataCollectorPass());
55+
$container->addCompilerPass(new AddHighlighterPass());
5456
}
5557
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\DebugBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
use Symfony\Component\DependencyInjection\Reference;
17+
18+
/**
19+
* Registers the code highlighters.
20+
*
21+
* @author Martin Hasoň <[email protected]>
22+
*/
23+
class AddHighlighterPass implements CompilerPassInterface
24+
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function process(ContainerBuilder $container)
29+
{
30+
if (!$container->hasDefinition('debug.html_utils')) {
31+
return;
32+
}
33+
34+
$highlighters = array();
35+
foreach ($container->findTaggedServiceIds('code_highlighter') as $id => $attributes) {
36+
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
37+
$alias = isset($attributes[0]['alias']) ? $attributes[0]['alias'] : null;
38+
39+
if (null === $alias) {
40+
$highlighters[$priority][] = new Reference($id);
41+
} else {
42+
$highlighters[$priority][$alias] = new Reference($id);
43+
}
44+
}
45+
46+
if (empty($highlighters)) {
47+
return;
48+
}
49+
50+
// sort by priority and flatten
51+
krsort($highlighters);
52+
$highlighters = call_user_func_array('array_merge', $highlighters);
53+
54+
$container->getDefinition('debug.html_utils')->replaceArgument(0, $highlighters);
55+
}
56+
}

src/Symfony/Bundle/DebugBundle/Resources/config/services.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
</service>
4646

4747
<service id="debug.html_utils" class="Symfony\Component\Debug\Utils\HtmlUtils">
48+
<argument type="collection" />
49+
</service>
50+
51+
<service id="debug.php_highlighter" class="Symfony\Component\Debug\Highlighter\PHPHighlighter" public="false">
52+
<tag name="code_highlighter" alias="php" />
4853
</service>
4954
</services>
5055

src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,9 @@
181181
<tag name="exception.processor" />
182182
<argument type="service" id="twig" />
183183
</service>
184+
185+
<service id="twig.twig_highlighter" class="Symfony\Bridge\Twig\Debug\TwigHighlighter" public="false">
186+
<tag name="code_highlighter" alias="twig" />
187+
</service>
184188
</services>
185189
</container>

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,20 @@
1616
<img class="toggle" id="icon-{{ prefix ~ '-' ~ i }}-open" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: {{ 0 == i ? 'none' : 'inline' }}" />
1717
</a>
1818
{% endspaceless %}
19+
1920
<div id="trace-{{ prefix ~ '-' ~ i }}" style="display: {{ 0 == i ? 'block' : 'none' }}" class="trace">
20-
{{ trace.file|file_excerpt(trace.line) }}
21+
{% if trace.file is defined and trace.file and trace.line is defined and trace.line %}
22+
<em>{{ trace.file|format_file(trace.line) }}</em>
23+
<div class="trace">
24+
{{ trace.file|highlight_code(trace.line, highlighter='php') }}
25+
</div>
26+
{% endif %}
27+
28+
{% for code in trace.related_codes|default([]) if code.file.path is defined %}
29+
<em>{{ code.file.path|format_file(code.line|default(1), code.file.name|default) }}</em>
30+
<div class="trace">
31+
{{ code.file.path|highlight_code(code.line|default(1), highlighter=code.file.type|default) }}
32+
</div>
33+
{% endfor %}
2134
</div>
2235
{% endif %}

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
padding-bottom: 2px;
1010
}
1111
.sf-reset .traces li {
12-
ccolor: #222;
12+
color: #222;
1313
font-size: 14px;
1414
padding: 5px 0;
1515
list-style-type: decimal;
@@ -31,8 +31,6 @@
3131
background: #FFCC00;
3232
}
3333
.sf-reset .trace {
34-
border: 1px solid #DDD;
35-
background: #FFF;
3634
padding: 10px;
3735
overflow: auto;
3836
margin: 1em 0;
@@ -95,3 +93,70 @@
9593
.sf-reset .toggle {
9694
vertical-align: middle;
9795
}
96+
.sf-reset .linked ul,
97+
.sf-reset .linked li {
98+
display: inline;
99+
}
100+
.sf-reset #output-content {
101+
color: #000;
102+
font-size: 12px;
103+
}
104+
105+
.sf-reset .trace ol {
106+
padding: 0;
107+
}
108+
.sf-reset .trace li {
109+
margin: 0;
110+
padding: 3px 0;
111+
margin-left: 8ex;
112+
list-style-position: outside;
113+
background: #18171B;
114+
word-wrap: break-word;
115+
overflow-wrap: break-word;
116+
}
117+
.sf-reset .trace li.selected {
118+
background-color: #373831;
119+
}
120+
.sf-reset .trace li code {
121+
color: #ccc;
122+
font-size: 1em;
123+
padding: 0;
124+
margin: 0 1ex;
125+
white-space: pre-wrap;
126+
word-spacing: normal;
127+
word-break: normal;
128+
129+
-moz-tab-size: 4;
130+
-o-tab-size: 4;
131+
tab-size: 4;
132+
133+
-webkit-hyphens: none;
134+
-moz-hyphens: none;
135+
-ms-hyphens: none;
136+
hyphens: none;
137+
}
138+
.sf-reset .code .comment {
139+
color: #B729D9;
140+
font-style: italic;
141+
}
142+
.sf-reset .code .operator {
143+
color: #E67700;
144+
}
145+
.sf-reset .code .tag {
146+
color: #A0A0A0;
147+
}
148+
.sf-reset .code .string {
149+
color: #56DB3A;
150+
}
151+
.sf-reset .code .variable {
152+
color: #FFFFFF;
153+
}
154+
.sf-reset .code .keyword {
155+
color: #FF8400;
156+
}
157+
.sf-reset .code .name {
158+
color: #FFFFFF;
159+
}
160+
.sf-reset .code .number {
161+
color: #1299da;
162+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Debug\Highlighter;
13+
14+
/**
15+
* The base class for syntax highlighters
16+
*
17+
* @author Martin Hasoň <[email protected]>
18+
*/
19+
abstract class Highlighter
20+
{
21+
/**
22+
* Highlights a code
23+
*
24+
* @param string $code The code
25+
* @param int $line The selected line number
26+
* @param int $count The number of lines above and below the selected line
27+
*
28+
* @return string The highlighted code
29+
*/
30+
abstract public function highlight($code, $from = 1, $to = -1, $line = -1);
31+
32+
/**
33+
* Returns true if this class is able to highlight the given file name.
34+
*
35+
* @param string $name The file name
36+
*
37+
* @return bool true if this class supports the given file name, false otherwise
38+
*/
39+
abstract public function supports($file);
40+
41+
protected function createLines($lines, $from, $to, $line = null)
42+
{
43+
$code = '';
44+
$lastOpenSpan = '';
45+
$to = -1 === $to ? count($lines) : $to;
46+
47+
$slice = array_slice($lines, $from - 1, $to - $from + 1, true);
48+
$key = key($slice);
49+
if (isset($lines[$key - 1]) && 0 !== strpos($lines[$key - 1], '<span')) {
50+
for ($i = $key - 2; $i >= 0; $i--) {
51+
if (isset($lines[$i]) && preg_match('#^.*(</?span[^>]*>)#', $lines[$i], $match) && '/' != $match[1][1]) {
52+
$lastOpenSpan = $match[1];
53+
break;
54+
}
55+
}
56+
}
57+
58+
--$line;
59+
foreach ($slice as $number => $content) {
60+
$code .= '<li'.($number === $line ? ' class="selected"' : '').'><code>'.($lastOpenSpan.$content);
61+
if (preg_match('#^.*(</?span[^>]*>)#', $content, $match)) {
62+
$lastOpenSpan = '/' != $match[1][1] ? $match[1] : '';
63+
}
64+
65+
if ($lastOpenSpan) {
66+
$code .= '</span>';
67+
}
68+
69+
$code .= "</code></li>\n";
70+
}
71+
72+
return '<ol class="code" start="'.($key+1).'">'.$code.'</ol>';
73+
}
74+
}

0 commit comments

Comments
 (0)