Skip to content

Commit df474f6

Browse files
nunomaduroStyleCIBottaylorotwell
authored
[9.x] Improves dd clickable link on multiple editors and docker environments (#44406)
* Adds better `app.editor` support * Apply fixes from StyleCI * Adds missing class * Fixes tests * Uses `name` instead of `href` * Adds most common edtor's href format * formatting Co-authored-by: StyleCI Bot <bot@styleci.io> Co-authored-by: Taylor Otwell <taylor@laravel.com>
1 parent de42f99 commit df474f6

File tree

4 files changed

+144
-26
lines changed

4 files changed

+144
-26
lines changed

src/Illuminate/Foundation/Concerns/ResolvesDumpSource.php

+61
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,33 @@
22

33
namespace Illuminate\Foundation\Concerns;
44

5+
use Throwable;
6+
57
trait ResolvesDumpSource
68
{
9+
/**
10+
* All of the href formats for common editors.
11+
*
12+
* @var array<string, string>
13+
*/
14+
protected $editorHrefs = [
15+
'atom' => 'atom://core/open/file?filename={file}&line={line}',
16+
'emacs' => 'emacs://open?url=file://{file}&line={line}',
17+
'idea' => 'idea://open?file={file}&line={line}',
18+
'macvim' => 'mvim://open/?url=file://{file}&line={line}',
19+
'netbeans' => 'netbeans://open/?f={file}:{line}',
20+
'nova' => 'nova://core/open/file?filename={file}&line={line}',
21+
'phpstorm' => 'phpstorm://open?file={file}&line={line}',
22+
'sublime' => 'subl://open?url=file://{file}&line={line}',
23+
'textmate' => 'txmt://open?url=file://{file}&line={line}',
24+
'vscode' => 'vscode://file/{file}:{line}',
25+
'vscode-insiders' => 'vscode-insiders://file/{file}:{line}',
26+
'vscode-insiders-remote' => 'vscode-insiders://vscode-remote/{file}:{line}',
27+
'vscode-remote' => 'vscode://vscode-remote/{file}:{line}',
28+
'vscodium' => 'vscodium://file/{file}:{line}',
29+
'xdebug' => 'xdebug://{file}@{line}',
30+
];
31+
732
/**
833
* The source resolver.
934
*
@@ -90,6 +115,42 @@ protected function getOriginalFileForCompiledView($file)
90115
return $file;
91116
}
92117

118+
/**
119+
* Resolve the source href, if possible.
120+
*
121+
* @param string $file
122+
* @param int|null $line
123+
* @return string|null
124+
*/
125+
protected function resolveSourceHref($file, $line)
126+
{
127+
try {
128+
$editor = config('app.editor');
129+
} catch (Throwable $e) {
130+
// ..
131+
}
132+
133+
if (! isset($editor)) {
134+
return;
135+
}
136+
137+
$href = is_array($editor) && isset($editor['href'])
138+
? $editor['href']
139+
: ($this->editorHrefs[$editor['name'] ?? $editor] ?? sprintf('%s://open?file={file}&line={line}', $editor['name'] ?? $editor));
140+
141+
if ($basePath = $editor['base_path'] ?? false) {
142+
$file = str_replace($this->basePath, $basePath, $file);
143+
}
144+
145+
$href = str_replace(
146+
['{file}', '{line}'],
147+
[$file, is_null($line) ? 1 : $line],
148+
$href,
149+
);
150+
151+
return $href;
152+
}
153+
93154
/**
94155
* Set the resolver that resolves the source of the dump call.
95156
*

src/Illuminate/Foundation/Console/CliDumper.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ protected function getDumpSourceContent()
114114

115115
[$file, $relativeFile, $line] = $dumpSource;
116116

117+
$href = $this->resolveSourceHref($file, $line);
118+
117119
return sprintf(
118-
' <fg=gray>// <fg=gray;href=file://%s%s>%s%s</></>',
119-
$file,
120-
is_null($line) ? '' : "#L$line",
120+
' <fg=gray>// <fg=gray%s>%s%s</></>',
121+
is_null($href) ? '' : ";href=$href",
121122
$relativeFile,
122123
is_null($line) ? '' : ":$line"
123124
);

src/Illuminate/Foundation/Http/HtmlDumper.php

+2-23
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Symfony\Component\VarDumper\Cloner\VarCloner;
99
use Symfony\Component\VarDumper\Dumper\HtmlDumper as BaseHtmlDumper;
1010
use Symfony\Component\VarDumper\VarDumper;
11-
use Throwable;
1211

1312
class HtmlDumper extends BaseHtmlDumper
1413
{
@@ -132,30 +131,10 @@ protected function getDumpSourceContent()
132131

133132
$source = sprintf('%s%s', $relativeFile, is_null($line) ? '' : ":$line");
134133

135-
if ($editor = $this->editor()) {
136-
$source = sprintf(
137-
'<a href="%s://open?file=%s%s">%s</a>',
138-
$editor,
139-
$file,
140-
is_null($line) ? '' : "&line=$line",
141-
$source,
142-
);
134+
if ($href = $this->resolveSourceHref($file, $line)) {
135+
$source = sprintf('<a href="%s">%s</a>', $href, $source);
143136
}
144137

145138
return sprintf('<span style="color: #A0A0A0;"> // %s</span>', $source);
146139
}
147-
148-
/**
149-
* Get the application editor, if applicable.
150-
*
151-
* @return string|null
152-
*/
153-
protected function editor()
154-
{
155-
try {
156-
return config('app.editor');
157-
} catch (Throwable $e) {
158-
// ...
159-
}
160-
}
161140
}

tests/Foundation/Http/HtmlDumperTest.php

+77
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Illuminate\Tests\Foundation\Http;
44

5+
use Illuminate\Config\Repository;
6+
use Illuminate\Container\Container;
57
use Illuminate\Foundation\Http\HtmlDumper;
68
use PHPUnit\Framework\TestCase;
79
use ReflectionClass;
@@ -11,6 +13,8 @@
1113

1214
class HtmlDumperTest extends TestCase
1315
{
16+
protected $app;
17+
1418
protected function setUp(): void
1519
{
1620
HtmlDumper::resolveDumpSourceUsing(function () {
@@ -20,6 +24,8 @@ protected function setUp(): void
2024
18,
2125
];
2226
});
27+
28+
$this->app = Container::getInstance();
2329
}
2430

2531
public function testString()
@@ -184,6 +190,76 @@ public function testUnresolvableLine()
184190
$this->assertStringContainsString($expected, $output);
185191
}
186192

193+
public function testHref()
194+
{
195+
$dumper = new HtmlDumper(
196+
'/my-work-directory',
197+
'/my-work-directory/storage/framework/views'
198+
);
199+
200+
// Failure...
201+
$href = (fn () => $this->resolveSourceHref(
202+
'/my-work-directory/app/my-file',
203+
10,
204+
))->call($dumper);
205+
$this->assertNull($href);
206+
207+
$config = new Repository();
208+
$this->app->instance('config', $config);
209+
$resolveSourceHref = fn () => (fn () => $this->resolveSourceHref(
210+
'/my-work-directory/app/my-file',
211+
10,
212+
))->call($dumper);
213+
214+
// Empty...
215+
$this->assertNull($resolveSourceHref());
216+
217+
// When editor name is provided...
218+
$config->set('app.editor', 'phpstorm');
219+
$this->assertSame(
220+
'phpstorm://open?file=/my-work-directory/app/my-file&line=10', $resolveSourceHref()
221+
);
222+
223+
// When editor name is provided on array format...
224+
$config->set('app.editor', ['name' => 'phpstorm']);
225+
$this->assertSame(
226+
'phpstorm://open?file=/my-work-directory/app/my-file&line=10', $resolveSourceHref()
227+
);
228+
229+
// When editor name and base path is provided on array format...
230+
$config->set('app.editor', ['name' => 'phpstorm', 'base_path' => '/my-docker-work-directory']);
231+
$this->assertSame(
232+
'phpstorm://open?file=/my-docker-work-directory/app/my-file&line=10', $resolveSourceHref());
233+
234+
// When href is provided on array format...
235+
$config->set('app.editor', ['href' => 'vscode://open?file={file}&line={line}']);
236+
$this->assertSame(
237+
'vscode://open?file=/my-work-directory/app/my-file&line=10', $resolveSourceHref()
238+
);
239+
240+
// When href and base path is provided on array format...
241+
$config->set('app.editor', ['href' => 'vscode://open?file={file}&line={line}', 'base_path' => '/my-docker-work-directory']);
242+
$this->assertSame(
243+
'vscode://open?file=/my-docker-work-directory/app/my-file&line=10', $resolveSourceHref()
244+
);
245+
246+
// When editor name is provided...
247+
$config->set('app.editor', 'sublime');
248+
$this->assertSame('subl://open?url=file:///my-work-directory/app/my-file&line=10', $resolveSourceHref());
249+
250+
// Missing line
251+
$config->set('app.editor', ['name' => 'vscode', 'base_path' => '/my-docker-work-directory']);
252+
253+
$href = (fn () => $this->resolveSourceHref(
254+
'/my-work-directory/app/my-file',
255+
null,
256+
))->call($dumper);
257+
$this->assertSame(
258+
'vscode://file//my-docker-work-directory/app/my-file:1',
259+
$href,
260+
);
261+
}
262+
187263
protected function dump($value)
188264
{
189265
$outputFile = stream_get_meta_data(tmpfile())['uri'];
@@ -205,5 +281,6 @@ protected function dump($value)
205281
protected function tearDown(): void
206282
{
207283
HtmlDumper::resolveDumpSourceUsing(null);
284+
Container::setInstance(null);
208285
}
209286
}

0 commit comments

Comments
 (0)