diff --git a/.gitattributes b/.gitattributes index e1bccc4bf..433a2de9a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ .gitignore export-ignore ncs.* export-ignore phpstan*.neon export-ignore +src/**/*.latte export-ignore tests/ export-ignore *.php* diff=php diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index af3408d91..27ca2fbfa 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -14,7 +14,7 @@ jobs: coverage: none - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress - - run: php temp/code-checker/code-checker --strict-types --no-progress -i tests/Tracy/fixtures -i examples/assets + - run: php temp/code-checker/code-checker --strict-types --no-progress -i tests/Tracy/fixtures -i examples/assets -i *.latte nette_cs: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c5f98677b..2b287d79b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,6 +37,7 @@ jobs: code_coverage: name: Code Coverage runs-on: ubuntu-latest + continue-on-error: true steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 diff --git a/composer.json b/composer.json index 97d54a97f..32b3d654c 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,9 @@ "nette/tester": "^2.6", "latte/latte": "^2.5 || ^3.0", "psr/log": "^1.0 || ^2.0 || ^3.0", - "phpstan/phpstan": "^2.0@stable" + "phpstan/phpstan": "^2.1@stable", + "phpstan/extension-installer": "^1.4@stable", + "nette/phpstan-rules": "^1.0" }, "conflict": { "nette/di": "<3.0" @@ -46,7 +48,12 @@ }, "extra": { "branch-alias": { - "dev-master": "2.11-dev" + "dev-master": "3.0-dev" + } + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true } } } diff --git a/examples/lazy-panels.php b/examples/lazy-panels.php new file mode 100644 index 000000000..8d0f8398e --- /dev/null +++ b/examples/lazy-panels.php @@ -0,0 +1,143 @@ +⚡ Normal'; + } + + public function getPanel(): string + { + return '
This panel was rendered during the request (eager).
' + . 'Time: ' . date('H:i:s') . '
' + . 'This panel was rendered after the response (lazy).
' + . 'It simulates a 500ms expensive computation.
' + . 'Time: ' . date('H:i:s') . '
' + . '| Key | Value |
|---|---|
| PHP Version | ' . PHP_VERSION . ' |
| Memory Peak | ' . number_format(memory_get_peak_usage() / 1024 / 1024, 2) . ' MB |
| Extensions | ' . count(get_loaded_extensions()) . ' loaded |
Simulated database queries — rendered lazily after the response was sent.
' + . '| # | Query | Time |
|---|---|---|
| ' . ($i + 1) . ' | ' . htmlspecialchars($query) . ' | ' . $time . ' |
This demo shows the lazy: true parameter for Debugger::getBar()->addPanel().
getPanel() is called during the request.// Register a lazy panel — getPanel() is NOT called during the request
+Debugger::getBar()->addPanel(new MyExpensivePanel, 'my-panel', lazy: true);
+
+
+Lazy panels have their getTab() called normally (so the tab is always visible),
+but getPanel() is deferred to a shutdown function. The content is stored in the session
+and fetched via AJAX when you click or hover over the panel tab.
This is useful for panels that perform expensive operations like database profiling, +API call logging, or heavy data analysis — they won't slow down your page response time.
+ +For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.'; +} diff --git a/phpstan.neon b/phpstan.neon index 94b109077..cdecbdc15 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,33 +1,51 @@ parameters: - level: 6 + level: 8 paths: - src - checkMissingCallableSignature: true + fileExtensions: + - php + - phtml ignoreErrors: # Template variables used in required .phtml files via variable scope - identifier: closure.unusedUse path: src/Tracy/Bar/Bar.php + - + identifier: closure.unusedUse + path: src/Tracy/BlueScreen/dist/markdown.phtml # Tracy doesn't need generic type parameters for Fiber, ArrayObject, DOMNodeList, etc. - identifier: missingType.generics - # Private methods called from .phtml template files - - - identifier: method.unused - path: src/Tracy/BlueScreen/BlueScreen.php - # Runtime validation of callable-string and Closure types - identifier: function.alreadyNarrowedType + paths: + - src/Tracy/Bar/dist/loader.phtml + - src/Tracy/BlueScreen/BlueScreen.php + - src/Tracy/Helpers.php # Tracy uses dynamic properties on exceptions and panels - identifier: property.notFound + paths: + - src/Tracy/Bar/dist/info.panel.phtml + - src/Tracy/Bar/dist/info.tab.phtml + - src/Tracy/Bar/panels/info.panel.php + - src/Tracy/Debugger/DevelopmentStrategy.php + - src/Tracy/Helpers.php + + # Private methods called from .phtml template files + - + identifier: method.unused + path: src/Tracy/BlueScreen/BlueScreen.php + - + identifier: method.private + path: src/Tracy/BlueScreen/dist # PHPStan doesn't track reference assignments to snapshot array - @@ -74,3 +92,29 @@ parameters: - identifier: missingType.return path: src/Tracy/Logger/ILogger.php + + # Arrow function callback receives class names from get_declared_classes() etc. + - + identifier: argument.type + message: '#class\-string#' + path: src/Tracy/Bar/panels/info.panel.php + + # getPanel() returns ?IBarPanel but panel is always registered; dynamic props correct by design + - + identifier: property.nonObject + path: src/Tracy/Debugger/DevelopmentStrategy.php + + # Value::$id and $value are always non-null when used as array keys (snapshot/above maps) + - + identifier: offsetAccess.invalidOffset + paths: + - src/Tracy/Dumper/Describer.php + - src/Tracy/Dumper/Renderer.php + + # Generated phtml templates use is_bool() as a runtime type guard; PHPStan sees it as always-false for string-typed vars + - + identifier: function.impossibleType + paths: + - src/Tracy/Bar/dist + - src/Tracy/BlueScreen/dist + - src/Tracy/Debugger/dist diff --git a/src/Bridges/Nette/Bridge.php b/src/Bridges/Nette/Bridge.php index f5272479c..2ad596922 100644 --- a/src/Bridges/Nette/Bridge.php +++ b/src/Bridges/Nette/Bridge.php @@ -46,16 +46,12 @@ public static function renderMemberAccessException(?\Throwable $e): ?array $loc = Tracy\Debugger::mapSource($loc['file'], $loc['line']) ?? $loc; if (preg_match('#Cannot (?:read|write to) an undeclared property .+::\$(\w+), did you mean \$(\w+)\?#A', $e->getMessage(), $m)) { - return [ - 'link' => Helpers::editorUri($loc['file'], $loc['line'], 'fix', '->' . $m[1], '->' . $m[2]), - 'label' => 'fix it', - ]; + $link = Helpers::editorUri($loc['file'], $loc['line'], 'fix', '->' . $m[1], '->' . $m[2]); + return $link !== null ? ['link' => $link, 'label' => 'fix it'] : null; } elseif (preg_match('#Call to undefined (static )?method .+::(\w+)\(\), did you mean (\w+)\(\)?#A', $e->getMessage(), $m)) { $operator = $m[1] ? '::' : '->'; - return [ - 'link' => Helpers::editorUri($loc['file'], $loc['line'], 'fix', $operator . $m[2] . '(', $operator . $m[3] . '('), - 'label' => 'fix it', - ]; + $link = Helpers::editorUri($loc['file'], $loc['line'], 'fix', $operator . $m[2] . '(', $operator . $m[3] . '('); + return $link !== null ? ['link' => $link, 'label' => 'fix it'] : null; } return null; diff --git a/src/Bridges/Nette/TracyExtension.php b/src/Bridges/Nette/TracyExtension.php index c966812f2..1342bbd50 100644 --- a/src/Bridges/Nette/TracyExtension.php +++ b/src/Bridges/Nette/TracyExtension.php @@ -131,14 +131,14 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class): void } $initialize->addBody($builder->formatPhp('if ($logger instanceof Tracy\Logger) $logger->mailer = ?;', [ - [new Statement(Tracy\Bridges\Nette\MailSender::class, $params), 'send'], + [new Statement(Tracy\Bridges\Nette\MailSender::class, $params), 'send'], // TODO: nette/di must be able to create closures ])); } if ($this->debugMode) { foreach ($config->bar as $item) { if (is_string($item) && str_starts_with($item, '@')) { - $item = new Statement(['@' . $builder::THIS_CONTAINER, 'getService'], [substr($item, 1)]); + $item = new Statement(['@' . $builder::ThisContainer, 'getService'], [substr($item, 1)]); } elseif (is_string($item)) { $item = new Statement($item); } @@ -182,7 +182,8 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class): void private function parseErrorSeverity(string|array $value): int { $value = implode('|', (array) $value); - $res = (int) @parse_ini_string('e = ' . $value)['e']; // @ may fail + $ini = @parse_ini_string('e = ' . $value); // @ may fail + $res = (int) ($ini['e'] ?? 0); if (!$res) { throw new Nette\InvalidStateException("Syntax error in expression '$value'"); } diff --git a/src/Tracy/Bar/Bar.php b/src/Tracy/Bar/Bar.php index cd69a351e..c47a957a4 100644 --- a/src/Tracy/Bar/Bar.php +++ b/src/Tracy/Bar/Bar.php @@ -19,13 +19,19 @@ class Bar { /** @var IBarPanel[] */ private array $panels = []; + + /** @var arrayLoading panel content\u2026
Lazy panel content not available. The panel may have expired from the session.
Failed to load lazy panel content.
| = Tracy\Helpers::escapeHtml($key) ?> + = Tracy\Helpers::escapeHtml($val) ?> + | += Tracy\Helpers::escapeHtml($key) ?> + | = Tracy\Helpers::escapeHtml($val) ?> + | +|
| = Tracy\Helpers::escapeHtml($package->name) ?> + | += Tracy\Helpers::escapeHtml($package->version) ?> += Tracy\Helpers::escapeHtml(strpos($package->version, 'dev') !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : '') ?> + | +
| = Tracy\Helpers::escapeHtml($package->name) ?> + | += Tracy\Helpers::escapeHtml($package->version) ?> += Tracy\Helpers::escapeHtml(strpos($package->version, 'dev') !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : '') ?> + | +
| = Tracy\Helpers::escapeHtml($count ? $count . '×' : '') ?> + | += Tracy\Helpers::escapeHtml($message) ?> + in = Tracy\Helpers::editorLink($file, (int) $line) ?> + |
+
| {$key} {$val} | + {else} +{$key} | {$val} | + {/if} +|
| {$package->name} | +{$package->version}{strpos($package->version, dev) !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : ''} | +
| {$package->name} | +{$package->version}{strpos($package->version, dev) !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : ''} | +
| = Helpers::escapeHtml($key) ?> = Helpers::escapeHtml($val) ?> | - -= Helpers::escapeHtml($key) ?> | = Helpers::escapeHtml($val) ?> | - -|
| = Helpers::escapeHtml($package->name) ?> | = Helpers::escapeHtml($package->version . (strpos($package->version, 'dev') !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : '')) ?> |
| = Helpers::escapeHtml($package->name) ?> | = Helpers::escapeHtml($package->version . (strpos($package->version, 'dev') !== false && $package->hash ? ' #' . substr($package->hash, 0, 4) : '')) ?> |
| {$count ? $count . '×' : ''} | +{$message} in {Tracy\Helpers::editorLink($file, (int) $line)} |
+
| = $count ? "$count\xC3\x97" : '' ?> | -= Helpers::escapeHtml($message), ' in ', Helpers::editorLink($file, (int) $line) ?> |
-
'; - if ($editor = Helpers::editorUri($file, $line)) { + if ($editor = Helpers::editorUri($file, line: $line, column: $column)) { $source = substr_replace($source, ' title="Ctrl-Click to open in editor" data-tracy-href="' . Helpers::escapeHtml($editor) . '"', 4, 0); } diff --git a/src/Tracy/BlueScreen/assets/content.latte b/src/Tracy/BlueScreen/assets/content.latte new file mode 100644 index 000000000..38de0d9bc --- /dev/null +++ b/src/Tracy/BlueScreen/assets/content.latte @@ -0,0 +1,76 @@ +{* + * @var Throwable $exception + * @var array{link: string, label: string, external?: bool}[] $actions + * @var string[] $info + * @var string $source + * @var ?array{type: int, message: string, file: string, line: int} $lastError + * @var array' . CodeHighlighter::highlightLine(htmlspecialchars($source, ENT_IGNORE, 'UTF-8'), $line, $column) . '
php{$tmp[1]}
+ {/if}
+
+ {if isset($_SERVER[argv])}
+ | {$k} | +{$dump($v, $k)} | +
|---|
php= Helpers::escapeHtml($tmp[1]) ?>- - - -
| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
| {$k} | +{$dump($v, $k)} | +
|---|
| {$k} | +{if $k === __NF}Nette Session{else}{$dump($v, $k)}{/if} | +
|---|
| {$k} | +{$dump($v, $k)} | +
|---|
| {$k} | +{$dump($v, $k)} | +
|---|
| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
| = Helpers::escapeHtml($k) ?> | = $k === '__NF' ? 'Nette Session' : $dump($v, $k) ?> |
|---|
| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
{$title}{$code}
{/if} + += Helpers::escapeHtml($title . $code) ?>
- - -| {$k} | +{$dump($v, $k)} | +
|---|
empty
+ {else} +| {$k} | +{$dump($v, $k)} | +
|---|
empty
+ {else} +| {$k} | +{$dump($v, $k)} | +
|---|
empty
+ {else} +| {$k} | +{$dump($v, $k)} | +
|---|
| {$s[0]} | +{$dump(trim($s[1]), $s[0])} | +
|---|
no headers
+ {/if} + + {if $headersSent && $headersFile && $headersLine !== null && @is_file($headersFile)} +Headers have been sent, output started at {Helpers::editorLink($headersFile, $headersLine)} source
+ +Headers have been sent
+ {else} +Headers were not sent at the time the exception was thrown
+ {/if} +| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
empty
- -| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
empty
- -| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
empty
- -| = Helpers::escapeHtml($k) ?> | = $dump($v, $k) ?> |
|---|
| = Helpers::escapeHtml($s[0]) ?> | = $dump(trim($s[1]), $s[0]) ?> |
|---|
no headers
- - - - -Headers have been sent, output started at = Helpers::editorLink($headersFile, $headersLine) ?> source
-Headers have been sent
- -Headers were not sent at the time the exception was thrown
- -Note: the last muted error may have nothing to do with the thrown exception.
+ + {if @is_file($lastError[file])} +{Helpers::editorLink($lastError[file], $lastError[line])}
+ +inner-code:{$lastError[line]}
+ {/if} +Note: the last muted error may have nothing to do with the thrown exception.
- - -= Helpers::editorLink($lastError['file'], $lastError['line']) ?>
-inner-code
- -| {is_string($argName) ? '$' : '#'}{$argName} | +{$dump($v, (string) $argName)} | +
|---|
File: {Helpers::editorLink(...$sourceOriginal)}
+ {BlueScreen::highlightFile(...$sourceOriginal)} +File: {Helpers::editorLink($sourceMapped[file], $sourceMapped[line])}
+ {BlueScreen::highlightFile($sourceMapped[file], line: $sourceMapped[line], column: $sourceMapped[column], php: false)} +File: {Helpers::editorLink($file, $line)}
+ {if $sourceOriginal}{BlueScreen::highlightFile(...$sourceOriginal)}{/if} + {/if} +File: = Helpers::editorLink(...$sourceOriginal) ?>
- = BlueScreen::highlightFile(...$sourceOriginal) ?> -File: = Helpers::editorLink($sourceMapped['file'], $sourceMapped['line']) ?>
- = BlueScreen::highlightFile($sourceMapped['file'], line: $sourceMapped['line'] ?? 0, column: $sourceMapped['column'] ?? 0, php: false) ?> -File: = Helpers::editorLink($file, $line) ?>
- - - -php= Tracy\Helpers::escapeHtml($tmp[1]) ?> ++ +
| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
| = Tracy\Helpers::escapeHtml($k) ?> + | ++Nette Session += $dump($v, $k) ?> + + | +
|---|
| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
= Tracy\Helpers::escapeHtml($title) ?> += Tracy\Helpers::escapeHtml($code) ?> +
+ + +| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
empty
+| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
empty
+| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
empty
+| = Tracy\Helpers::escapeHtml($k) ?> + | += $dump($v, $k) ?> + | +
|---|
| = Tracy\Helpers::escapeHtml($s[0]) ?> + | += $dump(trim($s[1]), $s[0]) ?> + | +
|---|
no headers
+ +Headers have been sent, output started at = Helpers::editorLink($headersFile, $headersLine) ?> + source
+ +Headers have been sent
+Headers were not sent at the time the exception was thrown
+Note: the last muted error may have nothing to do with the thrown exception.
+ += Helpers::editorLink($lastError['file'], $lastError['line']) ?> +
+ +inner-code:= Tracy\Helpers::escapeHtml($lastError['line']) ?> +
+File: = Helpers::editorLink(...$sourceOriginal) ?> +
+ = BlueScreen::highlightFile(...$sourceOriginal) ?> + +File: = Helpers::editorLink($sourceMapped['file'], $sourceMapped['line']) ?> +
+ = BlueScreen::highlightFile($sourceMapped['file'], line: $sourceMapped['line'], column: $sourceMapped['column'], php: false) ?> + +File: = Helpers::editorLink($file, $line) ?> +
+ + = BlueScreen::highlightFile(...$sourceOriginal) ?> + + +We're sorry! The server encountered an internal error and + was unable to complete your request. Please try again later.
+ +error 500 | {date('j. n. Y H:i')}{if !$logged}
Tracy is unable to log error.{/if}
We're sorry! The server encountered an internal error and was unable to complete your request. Please try again later.
-error 500 |
Tracy is unable to log error.
error 500 | = Tracy\Helpers::escapeHtml(date('j. n. Y H:i')) ?>
+
+
Tracy is unable to log error.
+
barDump('value') 📍'value'+
| 1%a% | -Notice: Only variables should be assigned by reference in %a%:%d% |
-
| 1%a% | -Warning: hex2bin(): Hexadecimal input string must have an even length in %a%:%d% |
-
| 1%a% | -Compile Warning: Unsupported declare 'foo' in %a%:%d% |
-
Notice: Only variables should be assigned by reference in %a%:%d%
Warning: hex2bin(): Hexadecimal input string must have an even length in %a%:%d%
Compile Warning: Unsupported declare 'foo' in %a%:%d%
User Error
-File: %a% : eval()'d code:1
- +File: %a% : eval()'d code:1
| $message | + |
+ ||||
|---|---|---|---|---|---|
| $error%a% | +256
+ |
+
+ +
+ +
| $user | +'root'
+ |
+
|---|---|
| $pass | ++ | +
- -
- -
| $user | 'root'
- |
|---|---|
| $pass | - |
%A%+ +
Headers were not sent at the time the exception was thrown
+Headers were not sent at the time the exception was thrown
+Exception #123
-Exception #123
-Exception #123
-File: %a%
--%d%: +File: %a%Debugger.exception.html.phpt:%d%
+%d%: %d%: %d%: function second($arg1, $arg2) %d%: { @@ -51,23 +53,25 @@- Call stack
++ Call stack +
-- - - -- third (...) -- --%d%: ++ + +- - -+ +third (...) + ++ ++- - - --%d%: %d%: %d%: function first($arg1, $arg2) %d%: { @@ -84,22 +88,26 @@ %d%: function third($arg1)-
-- $arg1 - - second (...) -- ---+ + +19: ++
++ +$arg1 ++ ++ +second (...) + ++ ++- - - --%d%: %d%: %d%: Debugger::$productionMode = false; %d%: setHtmlMode(); @@ -116,24 +124,31 @@ %d%: function second($arg1, $arg2)-
-- $arg1 true-- $arg2 false-- first (...) -- --+ + +%d%: ++
++ +$arg1 ++ true++ +$arg2 ++ false++ +first (...) + ++ ++-%d%: %d%: %d%: function third($arg1) %d%: { @@ -146,133 +161,126 @@ %d%: first(10, 'any string');-
+- $arg1 10-- $arg2 'any string'-+
++ +$arg1 ++ 10++ +$arg2 ++ 'any string'+- - -Last muted error
++ Last muted error +
++Warning: hex2bin(): Hexadecimal input string must have an even length
-Warning: hex2bin(): Hexadecimal input string must have an even length
-Note: the last muted error may have nothing to do with the thrown exception.
+Note: the last muted error may have nothing to do with the thrown exception.
-%a%Debugger.exception.html.phpt:%d%
-%A%+%a%Debugger.exception.html.phpt:%d%
+ +%A%- Environment
++ Environment +
- -- - - ---- - - - - -- %A% -
--- - -- %A% -
--%A%%A% - - - -
+ @@ -16,24 +17,25 @@-- - -++ + ++++ ++ %A% +
++++ %A% +
++%A%+ +++ +- HTTP
++ HTTP +
- -- - -- - + + diff --git a/tests/Tracy/expected/Debugger.exception.in-generator.html.expect b/tests/Tracy/expected/Debugger.exception.in-generator.html.expect index 7d4d5238c..8292b7857 100644 --- a/tests/Tracy/expected/Debugger.exception.in-generator.html.expect +++ b/tests/Tracy/expected/Debugger.exception.in-generator.html.expect @@ -1,4 +1,5 @@- - - - -- ---Code: %d%
--- - -%A%
-Headers were not sent at the time the exception was thrown
++ + ++ + + ++Code: %d%
+ ++++%A% +
+Headers were not sent at the time the exception was thrown
+ + - - - + Exception #123
-The my exception - search► +
+ The my exception + search►
-- Source file
++ Source file +
-File: %a%Debugger.exception.in-generator.html.phpt:%d%
--%d%: +File: %a%Debugger.exception.in-generator.html.phpt:%d%
+%d%: %d%: %d%: Debugger::$productionMode = false; %d%: setHtmlMode(); @@ -51,23 +53,25 @@- Call stack
++ Call stack +
-- - - -- {closure%a?%} () -- --+ @@ -16,204 +17,211 @@%A% + + + +%A% \ No newline at end of file diff --git a/tests/Tracy/expected/Debugger.strict.html.expect b/tests/Tracy/expected/Debugger.strict.html.expect index 6c0d14b74..b5bbcd216 100644 --- a/tests/Tracy/expected/Debugger.strict.html.expect +++ b/tests/Tracy/expected/Debugger.strict.html.expect @@ -1,4 +1,5 @@%d%: setHtmlMode(); ++ + + + ++ - + -+- - +-%d%: setHtmlMode(); %d%: %d%: Debugger::enable(); %d%: @@ -84,18 +88,19 @@ %d%: $fn($generator);--%d%: ++-%d%: %d%: $generator = (function (): iterable { %d%: yield 5; %d%: throw new Exception('The my exception', 123); @@ -108,12 +113,19 @@ %d%: $fn($generator);-
+- #0 - +
++ +#0 ++ + + - - - + Notice
-Only variables should be assigned by reference - search► +
+ Only variables should be assigned by reference + search►
-- -Source file
++ Source file +
- Call stack
++ Call stack +
-- - - --- third (...) -- --- - - -- --
-- $arg1 - - second (...) -- --- - - -- --
-- $arg1 true-- $arg2 false-- first (...) -- ---- --
+- $arg1 %d%-- $arg2 'any string'-+ + ++ +third (...) + ++ +++ + ++ ++
++ +$arg1 ++ ++ +second (...) + ++ +++ + ++ ++
++ +$arg1 ++ true++ +$arg2 ++ false++ +first (...) + ++ +++ ++
++ +$arg1 ++ 10++ +$arg2 ++ 'any string'+- - - - -Exception
++ Exception +
++- Environment
++ Environment +
- -- - - ---- - - - - -- %A% -
--- - -- %A% -
--%A%%A% - - - -
-- - -++ + ++++ ++ %A% +
++++ %A% +
++%A%+ ++%A%+ +- HTTP
++ HTTP +
- -- - -- - + + -%A%%A% \ No newline at end of file +%A%%A% diff --git a/tools/open-in-editor/linux/open-editor.sh b/tools/open-in-editor/linux/open-editor.sh index 69e37be83..8cabf2789 100755 --- a/tools/open-in-editor/linux/open-editor.sh +++ b/tools/open-in-editor/linux/open-editor.sh @@ -6,7 +6,7 @@ declare -A mapping # # Visual Studio Code -#editor='code --goto "$FILE":"$LINE"' +#editor='code --goto "$FILE":"$LINE":"$COLUMN"' # Emacs #editor='emacs +$LINE "$FILE"' # gVim @@ -17,11 +17,9 @@ declare -A mapping #editor='pluma +$LINE "$FILE"' # PHPStorm # To enable PHPStorm command-line interface, folow this guide: https://www.jetbrains.com/help/phpstorm/working-with-the-ide-features-from-command-line.html -#editor='phpstorm --line $LINE "$FILE"' +#editor='phpstorm --line $LINE --column $COLUMN "$FILE"' # VS Codium #editor='codium --goto "$FILE":"$LINE"' -# Visual Studio Code -#editor='code --goto "$FILE":"$LINE"' # # Optionally configure custom mapping here: @@ -56,16 +54,21 @@ action=`echo $url | sed -r "s/$regex/\1/i"` uri_params=`echo $url | sed -r "s/$regex/\2/i"` file=`get_param $uri_params "file"` -line=`get_param $uri_params "line"` +line_param=`get_param $uri_params "line"` search=`get_param $uri_params "search"` replace=`get_param $uri_params "replace"` +# Parse line and column from line parameter (format: "12:5" or just "12") +IFS=':' read -r line column <<< "$line_param" +column="${column:-1}" + # Debug? #echo "action '$action'" #echo "file '$file'" #echo "line '$line'" #echo "search '$search'" #echo "replace '$replace'" +#echo "column '$column'" # Convert URI encoded codes to normal characters (e.g. '%2F' => '/'). printf -v file "${file//%/\\x}" @@ -102,6 +105,7 @@ fi # Format the command according to the selected editor. command="${editor//\$FILE/$file}" command="${command//\$LINE/$line}" +command="${command//\$COLUMN/$column}" # Debug? #echo $command diff --git a/tools/open-in-editor/windows/open-editor.js b/tools/open-in-editor/windows/open-editor.js index ac1093e70..86f699483 100644 --- a/tools/open-in-editor/windows/open-editor.js +++ b/tools/open-in-editor/windows/open-editor.js @@ -1,7 +1,7 @@ var settings = { // PhpStorm - // editor: '"C:\\Program Files\\JetBrains\\PhpStorm 2018.1.2\\bin\\phpstorm64.exe" --line %line% "%file%"', + // editor: '"C:\\Program Files\\JetBrains\\PhpStorm 2018.1.2\\bin\\phpstorm64.exe" --line %line% --column %column% "%file%"', // title: 'PhpStorm', // NetBeans @@ -14,7 +14,7 @@ var settings = { // editor: '"C:\\Program Files\\SciTE\\scite.exe" "-open:%file%" -goto:%line%', // EmEditor - // editor: '"C:\\Program Files\\EmEditor\\EmEditor.exe" "%file%" /l %line%', + // editor: '"C:\\Program Files\\EmEditor\\EmEditor.exe" "%file%" /l %line% /cl %column%', // PSPad Editor // editor: '"C:\\Program Files\\PSPad editor\\PSPad.exe" -%line% "%file%"', @@ -26,7 +26,7 @@ var settings = { // editor: '"C:\\Program Files\\Sublime Text 2\\sublime_text.exe" "%file%:%line%"', // Visual Studio Code / VSCodium - // editor: '"C:\\Program Files\\Microsoft VS Code\\Code.exe" --goto "%file%:%line%"', + // editor: '"C:\\Program Files\\Microsoft VS Code\\Code.exe" --goto "%file%:%line%:%column%"', mappings: { // '/remotepath': '/localpath' @@ -41,7 +41,7 @@ if (!settings.editor) { } var url = WScript.Arguments(0); -var match = /^editor:\/\/(open|create|fix)\/?\?file=([^&]+)&line=(\d+)(?:&search=([^&]*)&replace=([^&]*))?/.exec(url); +var match = /^editor:\/\/(open|create|fix)\/?\?file=([^&]+)&line=(\d+)(?::(\d+))?(?:&search=([^&]*)&replace=([^&]*))?/.exec(url); if (!match) { WScript.Echo('Unexpected URI ' + url); WScript.Quit(); @@ -53,8 +53,9 @@ for (var i in match) { var action = match[1]; var file = match[2]; var line = match[3]; -var search = match[4]; -var replace = match[5]; +var column = match[4] || 1; +var search = match[5]; +var replace = match[6]; var shell = new ActiveXObject('WScript.Shell'); var fileSystem = new ActiveXObject('Scripting.FileSystemObject'); @@ -76,7 +77,7 @@ if (action === 'create' && !fileSystem.FileExists(file)) { fileSystem.OpenTextFile(file, 2).Write(lines.join('\n')); } -var command = settings.editor.replace(/%line%/, line).replace(/%file%/, file); +var command = settings.editor.replace(/%line%/, line).replace(/%column%/, column).replace(/%file%/, file); shell.Exec(command); if (settings.title) {- - - - -- ---Code: %d%
--- - -%A%
-Headers were not sent at the time the exception was thrown
++ + ++ + + ++Code: %d%
+ ++++ %A% +
+Headers were not sent at the time the exception was thrown
+