Skip to content

Commit

Permalink
feature #19973 Added a default ide file link web view (jeremyFreeAgent)
Browse files Browse the repository at this point in the history
This PR was merged into the 3.2-dev branch.

Discussion
----------

Added a default ide file link web view

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

When having no `framework.ide` configured or `framework.ide = symfony` the file link open the source in a web view (eg `_profiler/open?file=/src/AppBundle/Controller/DefaultController.php&line=50#line50`).

![](https://cl.ly/2Z0W2J020p43/feature_ide.png)

Commits
-------

ba6bcca Added a default ide file link web view
  • Loading branch information
fabpot committed Oct 25, 2016
2 parents fdb7834 + ba6bcca commit f2768dc
Show file tree
Hide file tree
Showing 19 changed files with 360 additions and 107 deletions.
49 changes: 24 additions & 25 deletions src/Symfony/Bridge/Twig/Extension/CodeExtension.php
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Bridge\Twig\Extension;

use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;

/**
* Twig extension relate to PHP code and used by the profiler and the default exception templates.
*
Expand All @@ -25,18 +27,13 @@ class CodeExtension extends \Twig_Extension
/**
* Constructor.
*
* @param string|array $fileLinkFormat The format for links to source files
* @param string $rootDir The project root directory
* @param string $charset The charset
* @param string|FileLinkFormatter $fileLinkFormat The format for links to source files
* @param string $rootDir The project root directory
* @param string $charset The charset
*/
public function __construct($fileLinkFormat, $rootDir, $charset)
{
$fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
if ($fileLinkFormat && !is_array($fileLinkFormat)) {
$i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: strlen($f);
$fileLinkFormat = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE);
}
$this->fileLinkFormat = $fileLinkFormat;
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
$this->rootDir = str_replace('/', DIRECTORY_SEPARATOR, dirname($rootDir)).DIRECTORY_SEPARATOR;
$this->charset = $charset;
}
Expand Down Expand Up @@ -128,27 +125,36 @@ public function formatArgsAsText($args)
/**
* Returns an excerpt of a code file around the given line number.
*
* @param string $file A file path
* @param int $line The selected line number
* @param string $file A file path
* @param int $line The selected line number
* @param int $srcContext The number of displayed lines around or -1 for the whole file
*
* @return string An HTML string
*/
public function fileExcerpt($file, $line)
public function fileExcerpt($file, $line, $srcContext = 3)
{
if (is_readable($file)) {
// highlight_file could throw warnings
// see https://bugs.php.net/bug.php?id=25725
$code = @highlight_file($file, true);
// remove main code/span tags
$code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code);
$content = preg_split('#<br />#', $code);
// split multiline spans
$code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', function ($m) {
return "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>';
}, $code);
$content = explode('<br />', $code);

$lines = array();
for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; ++$i) {
$lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>';
if (0 > $srcContext) {
$srcContext = count($content);
}

for ($i = max($line - $srcContext, 1), $max = min($line + $srcContext, count($content)); $i <= $max; ++$i) {
$lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><div class="anchor" id="line'.$i.'"></div><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>';
}

return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
return '<ol start="'.max($line - $srcContext, 1).'">'.implode("\n", $lines).'</ol>';
}
}

Expand Down Expand Up @@ -195,15 +201,8 @@ public function formatFile($file, $line, $text = null)
*/
public function getFileLink($file, $line)
{
if ($this->fileLinkFormat && file_exists($file)) {
for ($i = 1; isset($this->fileLinkFormat[$i]); ++$i) {
if (0 === strpos($file, $k = $this->fileLinkFormat[$i++])) {
$file = substr_replace($file, $this->fileLinkFormat[$i], 0, strlen($k));
break;
}
}

return strtr($this->fileLinkFormat[0], array('%f' => $file, '%l' => $line));
if ($fmt = $this->fileLinkFormat) {
return is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line);
}

return false;
Expand Down
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Bridge\Twig\Tests\Extension;

use Symfony\Bridge\Twig\Extension\CodeExtension;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;

class CodeExtensionTest extends \PHPUnit_Framework_TestCase
{
Expand Down Expand Up @@ -64,6 +65,6 @@ public function testGetName()

protected function getExtension()
{
return new CodeExtension('proto://%f#&line=%l&'.substr(__FILE__, 0, 5).'>foobar', '/root', 'UTF-8');
return new CodeExtension(new FileLinkFormatter('proto://%f#&line=%l&'.substr(__FILE__, 0, 5).'>foobar'), '/root', 'UTF-8');
}
}
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/Twig/composer.json
Expand Up @@ -23,7 +23,7 @@
"symfony/asset": "~2.8|~3.0",
"symfony/finder": "~2.8|~3.0",
"symfony/form": "~3.0.4",
"symfony/http-kernel": "~2.8|~3.0",
"symfony/http-kernel": "~3.2",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/routing": "~2.8|~3.0",
"symfony/templating": "~2.8|~3.0",
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/DebugBundle/Resources/config/services.xml
Expand Up @@ -14,7 +14,7 @@
<service id="data_collector.dump" class="Symfony\Component\HttpKernel\DataCollector\DumpDataCollector">
<tag name="data_collector" id="dump" template="@Debug/Profiler/dump.html.twig" priority="240" />
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
<argument>%debug.file_link_format%</argument>
<argument type="service" id="debug.file_link_formatter"></argument>
<argument>%kernel.charset%</argument>
<argument type="service" id="request_stack" />
<argument>null</argument><!-- var_dumper.cli_dumper when debug.dump_destination is set -->
Expand All @@ -38,7 +38,7 @@
<argument>0</argument> <!-- flags -->
<call method="setDisplayOptions">
<argument type="collection">
<argument key="fileLinkFormat">%debug.file_link_format%</argument>
<argument key="fileLinkFormat" type="service" id="debug.file_link_formatter"></argument>
</argument>
</call>
</service>
Expand Down
Expand Up @@ -17,10 +17,14 @@
<argument>-1</argument><!-- Log levels map for enabled error levels -->
<argument>%debug.error_handler.throw_at%</argument>
<argument>true</argument>
<argument>%debug.file_link_format%</argument>
<argument type="service" id="debug.file_link_formatter"></argument>
<argument>true</argument>
</service>

<service id="debug.stopwatch" class="Symfony\Component\Stopwatch\Stopwatch" />

<service id="debug.file_link_formatter" class="Symfony\Component\HttpKernel\Debug\FileLinkFormatter" public="false">
<argument>%debug.file_link_format%</argument>
</service>
</services>
</container>
Expand Up @@ -44,7 +44,7 @@

<service id="templating.helper.code" class="Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper">
<tag name="templating.helper" alias="code" />
<argument>%debug.file_link_format%</argument>
<argument type="service" id="debug.file_link_formatter"></argument>
<argument>%kernel.root_dir%</argument>
<argument>%kernel.charset%</argument>
</service>
Expand Down
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;

use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\Templating\Helper\Helper;

/**
Expand All @@ -27,18 +28,13 @@ class CodeHelper extends Helper
/**
* Constructor.
*
* @param string|array $fileLinkFormat The format for links to source files
* @param string $rootDir The project root directory
* @param string $charset The charset
* @param string|FileLinkFormatter $fileLinkFormat The format for links to source files
* @param string $rootDir The project root directory
* @param string $charset The charset
*/
public function __construct($fileLinkFormat, $rootDir, $charset)
{
$fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
if ($fileLinkFormat && !is_array($fileLinkFormat)) {
$i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: strlen($f);
$fileLinkFormat = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE);
}
$this->fileLinkFormat = $fileLinkFormat;
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
$this->rootDir = str_replace('\\', '/', $rootDir).'/';
$this->charset = $charset;
}
Expand Down Expand Up @@ -190,15 +186,8 @@ public function formatFile($file, $line, $text = null)
*/
public function getFileLink($file, $line)
{
if ($this->fileLinkFormat && is_file($file)) {
for ($i = 1; isset($this->fileLinkFormat[$i]); ++$i) {
if (0 === strpos($file, $k = $this->fileLinkFormat[$i++])) {
$file = substr_replace($path, $this->fileLinkFormat[$i], 0, strlen($k));
break;
}
}

return strtr($this->fileLinkFormat[0], array('%f' => $file, '%l' => $line));
if ($fmt = $this->fileLinkFormat) {
return is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line);
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml
Expand Up @@ -87,7 +87,7 @@

<service id="twig.extension.code" class="Symfony\Bridge\Twig\Extension\CodeExtension" public="false">
<tag name="twig.extension" />
<argument>%debug.file_link_format%</argument>
<argument type="service" id="debug.file_link_formatter"></argument>
<argument>%kernel.root_dir%</argument>
<argument>%kernel.charset%</argument>
</service>
Expand Down
Expand Up @@ -35,6 +35,7 @@ class ProfilerController
private $templates;
private $toolbarPosition;
private $cspHandler;
private $baseDir;

/**
* Constructor.
Expand All @@ -44,15 +45,17 @@ class ProfilerController
* @param \Twig_Environment $twig The twig environment
* @param array $templates The templates
* @param string $toolbarPosition The toolbar position (top, bottom, normal, or null -- use the configuration)
* @param string $baseDir The project root directory
*/
public function __construct(UrlGeneratorInterface $generator, Profiler $profiler = null, \Twig_Environment $twig, array $templates, $toolbarPosition = 'normal', ContentSecurityPolicyHandler $cspHandler = null)
public function __construct(UrlGeneratorInterface $generator, Profiler $profiler = null, \Twig_Environment $twig, array $templates, $toolbarPosition = 'normal', ContentSecurityPolicyHandler $cspHandler = null, $baseDir = null)
{
$this->generator = $generator;
$this->profiler = $profiler;
$this->twig = $twig;
$this->templates = $templates;
$this->toolbarPosition = $toolbarPosition;
$this->cspHandler = $cspHandler;
$this->baseDir = $baseDir;
}

/**
Expand Down Expand Up @@ -394,6 +397,39 @@ public function phpinfoAction()
return new Response($phpinfo, 200, array('Content-Type' => 'text/html'));
}

/**
* Displays the source of a file.
*
* @return Response A Response instance
*
* @throws NotFoundHttpException
*/
public function openAction(Request $request)
{
if (null === $this->baseDir) {
throw new NotFoundHttpException('The base dir should be set.');
}

if ($this->profiler) {
$this->profiler->disable();
}

$file = $request->query->get('file');
$line = $request->query->get('line');

$filename = $this->baseDir.DIRECTORY_SEPARATOR.$file;

if (preg_match("'(^|[/\\\\])\.\.?([/\\\\]|$)'", $file) || !is_readable($filename)) {
throw new NotFoundHttpException(sprintf('The file "%s" cannot be opened.', $file));
}

return new Response($this->twig->render('@WebProfiler/Profiler/open.html.twig', array(
'filename' => $filename,
'file' => $file,
'line' => $line,
)), 200, array('Content-Type' => 'text/html'));
}

/**
* Gets the Template Manager.
*
Expand Down
Expand Up @@ -52,6 +52,24 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('web_profiler.debug_toolbar.intercept_redirects', $config['intercept_redirects']);
$container->setParameter('web_profiler.debug_toolbar.mode', $config['toolbar'] ? WebDebugToolbarListener::ENABLED : WebDebugToolbarListener::DISABLED);
}

$baseDir = array();
$rootDir = $container->getParameter('kernel.root_dir');
$rootDir = explode(DIRECTORY_SEPARATOR, realpath($rootDir) ?: $rootDir);
$bundleDir = explode(DIRECTORY_SEPARATOR, __DIR__);
for ($i = 0; isset($rootDir[$i], $bundleDir[$i]); ++$i) {
if ($rootDir[$i] !== $bundleDir[$i]) {
break;
}
$baseDir[] = $rootDir[$i];
}
$baseDir = implode(DIRECTORY_SEPARATOR, $baseDir);

$profilerController = $container->getDefinition('web_profiler.controller.profiler');
$profilerController->replaceArgument(6, $baseDir);

$fileLinkFormatter = $container->getDefinition('debug.file_link_formatter');
$fileLinkFormatter->replaceArgument(2, $baseDir);
}

/**
Expand Down
Expand Up @@ -12,6 +12,7 @@
<argument>%data_collector.templates%</argument>
<argument>%web_profiler.debug_toolbar.position%</argument>
<argument type="service" id="web_profiler.csp.handler" />
<argument>null</argument>
</service>

<service id="web_profiler.controller.router" class="Symfony\Bundle\WebProfilerBundle\Controller\RouterController">
Expand Down Expand Up @@ -41,11 +42,18 @@
<argument type="constant">Symfony\Component\VarDumper\Dumper\HtmlDumper::DUMP_LIGHT_ARRAY</argument>
<call method="setDisplayOptions">
<argument type="collection">
<argument key="fileLinkFormat">%debug.file_link_format%</argument>
<argument key="fileLinkFormat" type="service" id="debug.file_link_formatter"></argument>
</argument>
</call>
</service>
</argument>
</service>

<service id="debug.file_link_formatter" class="Symfony\Component\HttpKernel\Debug\FileLinkFormatter" public="false">
<argument>%debug.file_link_format%</argument>
<argument type="service" id="request_stack" on-invalid="ignore" />
<argument>null</argument>
<argument>/_profiler/open?file=%%f&amp;line=%%l#line%%l</argument>
</service>
</services>
</container>
Expand Up @@ -28,6 +28,10 @@
<default key="_controller">web_profiler.controller.profiler:searchResultsAction</default>
</route>

<route id="_profiler_open_file" path="/open">
<default key="_controller">web_profiler.controller.profiler:openAction</default>
</route>

<route id="_profiler" path="/{token}">
<default key="_controller">web_profiler.controller.profiler:panelAction</default>
</route>
Expand Down

0 comments on commit f2768dc

Please sign in to comment.