diff --git a/kdevplatform/outputview/outputfilteringstrategies.h b/kdevplatform/outputview/outputfilteringstrategies.h --- a/kdevplatform/outputview/outputfilteringstrategies.h +++ b/kdevplatform/outputview/outputfilteringstrategies.h @@ -81,12 +81,13 @@ { public: - ScriptErrorFilterStrategy(); + ScriptErrorFilterStrategy(const QString& scriptdir = QString()); FilteredItem errorInLine(const QString& line) override; FilteredItem actionInLine(const QString& line) override; - +private: + QString scriptDir; }; /** diff --git a/kdevplatform/outputview/outputfilteringstrategies.cpp b/kdevplatform/outputview/outputfilteringstrategies.cpp --- a/kdevplatform/outputview/outputfilteringstrategies.cpp +++ b/kdevplatform/outputview/outputfilteringstrategies.cpp @@ -24,6 +24,7 @@ #include #include +#include #include @@ -352,7 +353,8 @@ /// --- Script error filter strategy --- -ScriptErrorFilterStrategy::ScriptErrorFilterStrategy() +ScriptErrorFilterStrategy::ScriptErrorFilterStrategy(const QString &scriptdir) + : scriptDir(scriptdir) { } @@ -365,12 +367,86 @@ { // A list of filters for possible Python and PHP errors static const ErrorFormat SCRIPT_ERROR_FILTERS[] = { + // php trigger_error message + ErrorFormat( QStringLiteral("^PHP\\s+(.*?):.* in\\s+(/.*) on line ([0-9]+)$"), 2, 3, 1, QStringLiteral("PHP") ), + // php trigger_error stack trace + // PHP 2. trigger_error() /home/proyect/yii:6 + ErrorFormat( QStringLiteral("^(PHP\\s+)[0-9]+\\.\\s+.*\\s+(/.*):([0-9]+)$"), 2, 3, 1, QStringLiteral("PHP") ), + // PHP Uncaught Error + ErrorFormat( QStringLiteral("^PHP.*(error):.* in (/.*):([0-9]+)$"), 2, 3, 1, QStringLiteral("PHP") ), + // PHP Uncaught Error stack trace + // #0 /home/proyect/yii(6): bad() + ErrorFormat( QStringLiteral("^\\#[0-9]+\\s+(/.*)\\(([0-9]+)\\):.*$"), 1, 2, -1, QStringLiteral("PHP") ), + // PHP Uncaught Error stack trace, last thrown + ErrorFormat( QStringLiteral("^(thrown) in\\s+(/.*) on line ([0-9]+)$"), 2, 3, 1, QStringLiteral("PHP") ), + // ungreedy with no trailing text, usually the output of the php function trigger_error() + ErrorFormat( QStringLiteral("^.*\\.\\s+(/.*?):([0-9]+)$"), 1, 2, 0 ), + // Another stack trace format (seen with CodeCeption) + // #0 /home/proyect/yii:6 + ErrorFormat( QStringLiteral("^\\#[0-9]+\\s+(/.*):([0-9]+)$"), 1, 2, -1, QStringLiteral("PHP") ), + // Codeception scenario steps + ErrorFormat( QStringLiteral("^.*at\\s+(.*):([0-9]+)$"), 1, 2, 0, QStringLiteral("PHP") ), + // Codeception summary failed tests + ErrorFormat( QStringLiteral("^\\s*Test\\s+(.*):.*$"), 1, -1, -1, QStringLiteral("CodeCeption") ), + // Codeception functional tests response + ErrorFormat( QStringLiteral("^\\s*(Response:)\\s+(/.*)$"), 2, -1, 1), ErrorFormat( QStringLiteral("^ File \"(.*)\", line ([0-9]+)(.*$|, in(.*)$)"), 1, 2, -1 ), ErrorFormat( QStringLiteral("^.*(/.*):([0-9]+).*$"), 1, 2, -1 ), - ErrorFormat( QStringLiteral("^.* in (/.*) on line ([0-9]+).*$"), 1, 2, -1 ) + ErrorFormat( QStringLiteral("^.* in (/.*) on line ([0-9]+).*$"), 1, 2, -1 ), + + }; + using Indicator = QPair; + static const Indicator INDICATORS[] = { + // PHP + Indicator(QStringLiteral("Warning"), FilteredItem::WarningItem), +// Indicator(QStringLiteral("Notice:"), FilteredItem::InformationItem), +// Indicator(QStringLiteral("thrown:"), FilteredItem::InformationItem), + Indicator(QStringLiteral("error"), FilteredItem::ErrorItem), + Indicator(QStringLiteral("DEPRECATION"), FilteredItem::WarningItem), +// Indicator(QStringLiteral("Response:"), FilteredItem::InformationItem), +// Indicator(QStringLiteral("PHP "), FilteredItem::InformationItem), }; + FilteredItem item(line); + item.type = FilteredItem::InformationItem; + for (const auto& curErrFilter : SCRIPT_ERROR_FILTERS) { + const auto match = curErrFilter.expression.match(line); + if( match.hasMatch() ) + { + QString url; + if(curErrFilter.fileGroup > 0) { + url = match.captured( curErrFilter.fileGroup ); + QFileInfo fi(url); + if( fi.isRelative() ) { + url = scriptDir + QDir::separator() + url; + } + } + item.url = Path(url).toUrl(); + initializeFilteredItem(item, curErrFilter, match); + const QString txt = match.captured(curErrFilter.textGroup); - return match(SCRIPT_ERROR_FILTERS, line); + // Find the indicator which happens most early. + int earliestIndicatorIdx = txt.length(); + for (const auto& curIndicator : INDICATORS) { + int curIndicatorIdx = txt.indexOf(curIndicator.first, 0, Qt::CaseInsensitive); + if((curIndicatorIdx >= 0) && (earliestIndicatorIdx > curIndicatorIdx)) { + earliestIndicatorIdx = curIndicatorIdx; + item.type = curIndicator.second; + } + } + + // Make the item clickable if it comes with the necessary file information + if (item.url.isValid()) { + item.isActivatable = true; + if(item.type == FilteredItem::InvalidItem) { + // Okay so we couldn't find anything to indicate an error, but we have file and lineGroup + // Lets keep this item clickable and indicate this to the user. + item.type = FilteredItem::InformationItem; + } + } + break; + } + } + return item; } /// --- Native application error filter strategy --- diff --git a/kdevplatform/outputview/outputmodel.cpp b/kdevplatform/outputview/outputmodel.cpp --- a/kdevplatform/outputview/outputmodel.cpp +++ b/kdevplatform/outputview/outputmodel.cpp @@ -411,7 +411,7 @@ filter = new CompilerFilterStrategy( d->m_buildDir ); break; case ScriptErrorFilter: - filter = new ScriptErrorFilterStrategy; + filter = new ScriptErrorFilterStrategy( d->m_buildDir.toString() ); break; case NativeAppErrorFilter: filter = new NativeAppErrorFilterStrategy; diff --git a/plugins/executescript/scriptappjob.cpp b/plugins/executescript/scriptappjob.cpp --- a/plugins/executescript/scriptappjob.cpp +++ b/plugins/executescript/scriptappjob.cpp @@ -129,9 +129,17 @@ auto currentFilterMode = static_cast( iface->outputFilterModeId( cfg ) ); + QUrl wc = iface->workingDirectory( cfg ); + if( !wc.isValid() || wc.isEmpty() ) + { + wc = QUrl::fromLocalFile( QFileInfo( script.toLocalFile() ).absolutePath() ); + } + QUrl localwc( ICore::self()->runtimeController()->currentRuntime()->pathInRuntime(KDevelop::Path(wc)).toLocalFile() ); + + setStandardToolView(KDevelop::IOutputView::RunView); setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll); - auto* m = new KDevelop::OutputModel; + auto* m = new KDevelop::OutputModel(localwc); m->setFilteringStrategy(currentFilterMode); setModel( m ); setDelegate( new KDevelop::OutputDelegate ); @@ -143,12 +151,7 @@ // Now setup the process parameters proc->setEnvironment(environmentProfiles.createEnvironment(envProfileName, proc->systemEnvironment())); - QUrl wc = iface->workingDirectory( cfg ); - if( !wc.isValid() || wc.isEmpty() ) - { - wc = QUrl::fromLocalFile( QFileInfo( script.toLocalFile() ).absolutePath() ); - } - proc->setWorkingDirectory( ICore::self()->runtimeController()->currentRuntime()->pathInRuntime(KDevelop::Path(wc)).toLocalFile() ); + proc->setWorkingDirectory( localwc.toString() ); proc->setProperty( "executable", interpreter.first() ); QStringList program;