mirror of
https://github.com/filp/whoops.git
synced 2026-09-12 18:56:22 +00:00
Allow to filter frames belonging to the application.
When using a framework, many of the frames are generated by the framework and are most of the times not relevant. If you specify which paths are part of the application, tabs will be shown to toggle between only application and all frames.
This commit is contained in:
@@ -26,6 +26,11 @@ class Frame implements Serializable
|
||||
*/
|
||||
protected $comments = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
/**
|
||||
* @param array[]
|
||||
*/
|
||||
@@ -266,4 +271,24 @@ class Frame implements Serializable
|
||||
}
|
||||
return $frame->getFile() === $this->getFile() && $frame->getLine() === $this->getLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this frame belongs to the application or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isApplication()
|
||||
{
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as an frame belonging to the application.
|
||||
*
|
||||
* @param boolean $application
|
||||
*/
|
||||
public function setApplication($application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,18 @@ class FrameCollection implements ArrayAccess, IteratorAggregate, Serializable, C
|
||||
return count($this->frames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the frames that belongs to the application.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countIsApplication()
|
||||
{
|
||||
return count(array_filter($this->frames, function(Frame $f) {
|
||||
return $f->isApplication();
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Serializable::serialize
|
||||
* @return string
|
||||
|
||||
@@ -52,6 +52,11 @@ class PrettyPageHandler extends Handler
|
||||
*/
|
||||
private $pageTitle = "Whoops! There was an error.";
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
*/
|
||||
private $applicationPaths;
|
||||
|
||||
/**
|
||||
* A string identifier for a known IDE/text editor, or a closure
|
||||
* that resolves a string that can be used to open a given file
|
||||
@@ -137,6 +142,19 @@ class PrettyPageHandler extends Handler
|
||||
$code = Misc::translateErrorCode($inspector->getException()->getSeverity());
|
||||
}
|
||||
|
||||
// Detect frames that belong to the application.
|
||||
if ($this->applicationPaths) {
|
||||
/* @var \Whoops\Exception\Frame $frame */
|
||||
foreach ($frames as $frame) {
|
||||
foreach ($this->applicationPaths as $path) {
|
||||
if (substr($frame->getFile(), 0, strlen($path)) === $path) {
|
||||
$frame->setApplication(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List of variables that will be passed to the layout template.
|
||||
$vars = array(
|
||||
"page_title" => $this->getPageTitle(),
|
||||
@@ -163,6 +181,9 @@ class PrettyPageHandler extends Handler
|
||||
"handler" => $this,
|
||||
"handlers" => $this->getRun()->getHandlers(),
|
||||
|
||||
"active_frames_tab" => count($frames) && $frames->offsetGet(0)->isApplication() ? 'application' : 'all',
|
||||
"has_frames_tabs" => !empty($this->getApplicationPaths()),
|
||||
|
||||
"tables" => array(
|
||||
"GET Data" => $_GET,
|
||||
"POST Data" => $_POST,
|
||||
@@ -532,4 +553,24 @@ class PrettyPageHandler extends Handler
|
||||
{
|
||||
$this->addResourcePath($resourcesPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the application paths.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getApplicationPaths()
|
||||
{
|
||||
return $this->applicationPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the application paths.
|
||||
*
|
||||
* @param array $applicationPaths
|
||||
*/
|
||||
public function setApplicationPaths($applicationPaths)
|
||||
{
|
||||
$this->applicationPaths = $applicationPaths;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,27 @@ header {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.frames-description.frames-description-application {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
.frames-container.frames-container-application .frame:not(.frame-application) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.frames-tab {
|
||||
color: #a29d9d;
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
margin: 0 2px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.frames-tab.frames-tab-active {
|
||||
background-color: #2a2a2a;
|
||||
color: #bebebe;
|
||||
}
|
||||
|
||||
.frame {
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
@@ -169,6 +190,11 @@ header {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.frame-application .frame-index {
|
||||
background-color: #2a2a2a;
|
||||
color: #bebebe;
|
||||
}
|
||||
|
||||
.frame-file {
|
||||
font-family: "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
|
||||
word-wrap: break-word;
|
||||
|
||||
@@ -17,6 +17,8 @@ Zepto(function($) {
|
||||
});
|
||||
|
||||
var $frameContainer = $('.frames-container');
|
||||
var $appFramesTab = $('#application-frames-tab');
|
||||
var $allFramesTab = $('#all-frames-tab');
|
||||
var $container = $('.details-container');
|
||||
var $activeLine = $frameContainer.find('.frame.active');
|
||||
var $activeFrame = $container.find('.frame-code.active');
|
||||
@@ -95,23 +97,30 @@ Zepto(function($) {
|
||||
}
|
||||
|
||||
$(document).on('keydown', function(e) {
|
||||
var applicationFrames = $frameContainer.hasClass('frames-container-application'),
|
||||
frameClass = applicationFrames ? '.frame.frame-application' : '.frame';
|
||||
|
||||
if(e.ctrlKey) {
|
||||
// CTRL+Arrow-UP/Arrow-Down support:
|
||||
// 1) select the next/prev element
|
||||
// 2) make sure the newly selected element is within the view-scope
|
||||
// 3) focus the (right) container, so arrow-up/down (without ctrl) scroll the details
|
||||
if (e.which === 38 /* arrow up */) {
|
||||
$activeLine.prev('.frame').click();
|
||||
$activeLine.prev(frameClass).click();
|
||||
$activeLine[0].scrollIntoView();
|
||||
$container.focus();
|
||||
e.preventDefault();
|
||||
} else if (e.which === 40 /* arrow down */) {
|
||||
$activeLine.next('.frame').click();
|
||||
$activeLine.next(frameClass).click();
|
||||
$activeLine[0].scrollIntoView();
|
||||
$container.focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
} else if (e.which == 78 /* n */) {
|
||||
if ($appFramesTab.length) {
|
||||
setActiveFramesTab($('.frames-tab:not(.frames-tab-active)'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Avoid to quit the page with some protocol (e.g. IntelliJ Platform REST API)
|
||||
@@ -125,4 +134,22 @@ Zepto(function($) {
|
||||
.removeClass('sf-dump-expanded')
|
||||
.addClass('sf-dump-compact');
|
||||
$('.sf-dump-toggle span').html('▶');
|
||||
|
||||
// Make the given frames-tab active
|
||||
function setActiveFramesTab($tab) {
|
||||
$tab.addClass('frames-tab-active');
|
||||
|
||||
if ($tab.attr('id') == 'application-frames-tab') {
|
||||
$frameContainer.addClass('frames-container-application');
|
||||
$allFramesTab.removeClass('frames-tab-active');
|
||||
} else {
|
||||
$frameContainer.removeClass('frames-container-application');
|
||||
$appFramesTab.removeClass('frames-tab-active');
|
||||
}
|
||||
}
|
||||
|
||||
$('a.frames-tab').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
setActiveFramesTab($(this));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
clicking these links/buttons will display the code view
|
||||
for that particular frame */ ?>
|
||||
<?php foreach ($frames as $i => $frame): ?>
|
||||
<div class="frame <?php echo ($i == 0 ? 'active' : '') ?>" id="frame-line-<?php echo $i ?>">
|
||||
<div class="frame <?php echo ($i == 0 ? 'active' : '') ?> <?php echo ($frame->isApplication() ? 'frame-application' : '') ?>" id="frame-line-<?php echo $i ?>">
|
||||
<div class="frame-method-info">
|
||||
<span class="frame-index"><?php echo (count($frames) - $i - 1) ?></span>
|
||||
<span class="frame-class"><?php echo $tpl->escape($frame->getClass() ?: '') ?></span>
|
||||
|
||||
@@ -20,11 +20,28 @@
|
||||
<?php $tpl->render($header) ?>
|
||||
</header>
|
||||
|
||||
<div class="frames-description">
|
||||
Stack frames (<?php echo count($frames) ?>):
|
||||
<div class="frames-description <?php echo $has_frames_tabs ? 'frames-description-application' : '' ?>">
|
||||
<?php if ($has_frames_tabs): ?>
|
||||
<?php if ($active_frames_tab == 'application'): ?>
|
||||
<a href="#" id="application-frames-tab" class="frames-tab frames-tab-active">
|
||||
Application frames (<?php echo $frames->countIsApplication() ?>)
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span href="#" id="application-frames-tab" class="frames-tab">
|
||||
Application frames (<?php echo $frames->countIsApplication() ?>)
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<a href="#" id="all-frames-tab" class="frames-tab <?php echo $active_frames_tab == 'all' ? 'frames-tab-active' : '' ?>">
|
||||
All frames (<?php echo count($frames) ?>)
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span>
|
||||
Stack frames (<?php echo count($frames) ?>)
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="frames-container">
|
||||
<div class="frames-container <?php echo $active_frames_tab == 'application' ? 'frames-container-application' : '' ?>">
|
||||
<?php $tpl->render($frame_list) ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user