ProjectExplorer: Dissolve the AnsiFilterParser class

This class was different from all the other output parsers in that its
only responsibility was to pre-process the output before it was passed
to the real parsers. We now make this explicit by introducing the
concept of a filter function and turning the AnsiFilterParser class into
one of those.
This also gets rid of a case where the order of output parsers in the
chain matters, which we want to move away from.

Task-number: QTCREATORBUG-22665
Change-Id: Ica135e54ab43cf2ca8186073dc2487c906d4b38d
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-04-03 17:22:19 +02:00
parent a03a35d3ea
commit c58b1b2ca6
5 changed files with 53 additions and 51 deletions

View File

@@ -136,10 +136,10 @@ AbstractProcessStep::~AbstractProcessStep()
void AbstractProcessStep::setOutputParser(IOutputParser *parser) void AbstractProcessStep::setOutputParser(IOutputParser *parser)
{ {
d->m_outputParserChain.reset(new AnsiFilterParser); parser->addFilter(&Internal::filterAnsiEscapeCodes);
d->m_outputParserChain->appendOutputParser(parser); d->m_outputParserChain.reset(parser);
connect(d->m_outputParserChain.get(), &IOutputParser::addTask,
connect(d->m_outputParserChain.get(), &IOutputParser::addTask, this, &AbstractProcessStep::taskAdded); this, &AbstractProcessStep::taskAdded);
} }
/*! /*!

View File

@@ -25,7 +25,18 @@
#include "ansifilterparser.h" #include "ansifilterparser.h"
namespace { #ifdef WITH_TESTS
#include "projectexplorer.h"
#include "outputparser_test.h"
#include "task.h"
#include <QTest>
#endif // WITH_TESTS
namespace ProjectExplorer {
namespace Internal {
enum AnsiState { enum AnsiState {
PLAIN, PLAIN,
ANSI_START, ANSI_START,
@@ -34,26 +45,8 @@ enum AnsiState {
ANSI_WAITING_FOR_ST, ANSI_WAITING_FOR_ST,
ANSI_ST_STARTED ANSI_ST_STARTED
}; };
} // namespace
using namespace ProjectExplorer; QString filterAnsiEscapeCodes(const QString &line)
AnsiFilterParser::AnsiFilterParser()
{
setObjectName(QLatin1String("AnsiFilterParser"));
}
void AnsiFilterParser::stdOutput(const QString &line)
{
IOutputParser::stdOutput(filterLine(line));
}
void AnsiFilterParser::stdError(const QString &line)
{
IOutputParser::stdError(filterLine(line));
}
QString AnsiFilterParser::filterLine(const QString &line)
{ {
QString result; QString result;
result.reserve(line.count()); result.reserve(line.count());
@@ -104,15 +97,9 @@ QString AnsiFilterParser::filterLine(const QString &line)
} }
return result; return result;
} }
} // namespace Internal
// Unit tests:
#ifdef WITH_TESTS #ifdef WITH_TESTS
# include <QTest>
# include "projectexplorer.h"
# include "outputparser_test.h"
# include "task.h"
void ProjectExplorerPlugin::testAnsiFilterOutputParser_data() void ProjectExplorerPlugin::testAnsiFilterOutputParser_data()
{ {
QTest::addColumn<QString>("input"); QTest::addColumn<QString>("input");
@@ -161,7 +148,7 @@ void ProjectExplorerPlugin::testAnsiFilterOutputParser_data()
void ProjectExplorerPlugin::testAnsiFilterOutputParser() void ProjectExplorerPlugin::testAnsiFilterOutputParser()
{ {
OutputParserTester testbench; OutputParserTester testbench;
testbench.appendOutputParser(new AnsiFilterParser); testbench.addFilter(&Internal::filterAnsiEscapeCodes);
QFETCH(QString, input); QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel); QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(QString, childStdOutLines); QFETCH(QString, childStdOutLines);
@@ -173,3 +160,5 @@ void ProjectExplorerPlugin::testAnsiFilterOutputParser()
} }
#endif #endif
} // namespace ProjectExplorer

View File

@@ -30,18 +30,9 @@
#include "projectexplorer_export.h" #include "projectexplorer_export.h"
namespace ProjectExplorer { namespace ProjectExplorer {
namespace Internal {
class PROJECTEXPLORER_EXPORT AnsiFilterParser : public IOutputParser QString filterAnsiEscapeCodes(const QString &line);
{
Q_OBJECT
public:
AnsiFilterParser();
void stdOutput(const QString &line) override;
void stdError(const QString &line) override;
private:
QString filterLine(const QString &line);
};
} // namespace Internal
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -111,8 +111,9 @@ class OutputChannelState
public: public:
using LineHandler = void (IOutputParser::*)(const QString &line); using LineHandler = void (IOutputParser::*)(const QString &line);
OutputChannelState(IOutputParser *parser, LineHandler lineHandler) OutputChannelState(IOutputParser *parser, LineHandler lineHandler,
: parser(parser), lineHandler(lineHandler) {} QList<IOutputParser::Filter> &filters)
: parser(parser), lineHandler(lineHandler), filters(filters) {}
void handleData(const QString &newData) void handleData(const QString &newData)
{ {
@@ -124,20 +125,30 @@ public:
break; break;
const QString line = pendingData.left(eolPos + 1); const QString line = pendingData.left(eolPos + 1);
pendingData.remove(0, eolPos + 1); pendingData.remove(0, eolPos + 1);
(parser->*lineHandler)(line); (parser->*lineHandler)(filteredLine(line));
} }
} }
void flush() void flush()
{ {
if (!pendingData.isEmpty()) { if (!pendingData.isEmpty()) {
(parser->*lineHandler)(pendingData); (parser->*lineHandler)(filteredLine(pendingData));
pendingData.clear(); pendingData.clear();
} }
} }
QString filteredLine(const QString &line)
{
QString l = line;
for (const IOutputParser::Filter &f : filters)
l = f(l);
return l;
}
IOutputParser * const parser; IOutputParser * const parser;
LineHandler lineHandler; const LineHandler lineHandler;
QList<IOutputParser::Filter> &filters;
QString pendingData; QString pendingData;
}; };
@@ -145,11 +156,12 @@ class IOutputParser::IOutputParserPrivate
{ {
public: public:
IOutputParserPrivate(IOutputParser *parser) IOutputParserPrivate(IOutputParser *parser)
: stdoutState(parser, &IOutputParser::stdOutput), : stdoutState(parser, &IOutputParser::stdOutput, filters),
stderrState(parser, &IOutputParser::stdError) stderrState(parser, &IOutputParser::stdError, filters)
{} {}
IOutputParser *childParser = nullptr; IOutputParser *childParser = nullptr;
QList<Filter> filters;
Utils::FilePath workingDir; Utils::FilePath workingDir;
OutputChannelState stdoutState; OutputChannelState stdoutState;
OutputChannelState stderrState; OutputChannelState stderrState;
@@ -261,4 +273,9 @@ QString IOutputParser::rightTrimmed(const QString &in)
return in.mid(0, pos); return in.mid(0, pos);
} }
void IOutputParser::addFilter(const Filter &filter)
{
d->filters << filter;
}
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -30,6 +30,8 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <functional>
namespace ProjectExplorer { namespace ProjectExplorer {
class Task; class Task;
@@ -51,6 +53,9 @@ public:
virtual bool hasFatalErrors() const; virtual bool hasFatalErrors() const;
using Filter = std::function<QString(const QString &)>;
void addFilter(const Filter &filter);
void setWorkingDirectory(const Utils::FilePath &fn); void setWorkingDirectory(const Utils::FilePath &fn);
void flush(); // flush pending tasks & output void flush(); // flush pending tasks & output