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)
{
d->m_outputParserChain.reset(new AnsiFilterParser);
d->m_outputParserChain->appendOutputParser(parser);
connect(d->m_outputParserChain.get(), &IOutputParser::addTask, this, &AbstractProcessStep::taskAdded);
parser->addFilter(&Internal::filterAnsiEscapeCodes);
d->m_outputParserChain.reset(parser);
connect(d->m_outputParserChain.get(), &IOutputParser::addTask,
this, &AbstractProcessStep::taskAdded);
}
/*!

View File

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

View File

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

View File

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

View File

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