Utils: Split up OutputFormatter class

An OutputFormatter takes some string and prints it into a text edit.
In addition, it can ask any number of registered OutputLineParsers
whether they think any special formatting should be applied to the
current line.
This mechanism is now properly modeled by our class design, rather than
being hidden in a monolithic class where everything had the same type,
no matter what its purpose was.
Prospective contributors can now simply be pointed to the
OutputLineParser class and will see at one glance what they have to do.

Change-Id: I9844499f062c94fb038ce73fd6f26576910148c2
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-04-14 15:28:44 +02:00
parent 70bddbcab4
commit c0c2df203d
12 changed files with 173 additions and 139 deletions

View File

@@ -48,14 +48,18 @@ public:
AnsiEscapeCodeHandler escapeCodeHandler; AnsiEscapeCodeHandler escapeCodeHandler;
QPair<QString, OutputFormat> incompleteLine; QPair<QString, OutputFormat> incompleteLine;
optional<QTextCharFormat> formatOverride; optional<QTextCharFormat> formatOverride;
QList<OutputFormatter *> formatters; QList<OutputLineParser *> lineParsers;
OutputFormatter *nextFormatter = nullptr; OutputLineParser *nextParser = nullptr;
bool boldFontEnabled = true; bool boldFontEnabled = true;
bool prependCarriageReturn = false; bool prependCarriageReturn = false;
}; };
} // namespace Internal } // namespace Internal
OutputLineParser::~OutputLineParser()
{
}
OutputFormatter::OutputFormatter() OutputFormatter::OutputFormatter()
: d(new Internal::OutputFormatterPrivate) : d(new Internal::OutputFormatterPrivate)
{ {
@@ -79,12 +83,10 @@ void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText)
initFormats(); initFormats();
} }
void OutputFormatter::setFormatters(const QList<OutputFormatter *> &formatters) void OutputFormatter::setLineParsers(const QList<OutputLineParser *> &parsers)
{ {
for (OutputFormatter * const f : formatters) d->lineParsers = parsers;
f->setPlainTextEdit(plainTextEdit()); d->nextParser = nullptr;
d->formatters = formatters;
d->nextFormatter = nullptr;
} }
void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format) void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
@@ -93,7 +95,7 @@ void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
const QList<FormattedText> formattedText = parseAnsi(text, charFmt); const QList<FormattedText> formattedText = parseAnsi(text, charFmt);
const QString cleanLine = std::accumulate(formattedText.begin(), formattedText.end(), QString(), const QString cleanLine = std::accumulate(formattedText.begin(), formattedText.end(), QString(),
[](const FormattedText &t1, const FormattedText &t2) { return t1.text + t2.text; }); [](const FormattedText &t1, const FormattedText &t2) { return t1.text + t2.text; });
const Result res = handleMessage(cleanLine, format); const OutputLineParser::Result res = handleMessage(cleanLine, format);
if (res.newContent) { if (res.newContent) {
append(res.newContent.value(), charFmt); append(res.newContent.value(), charFmt);
return; return;
@@ -102,36 +104,42 @@ void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
append(output.text, output.format); append(output.text, output.format);
} }
OutputFormatter::Result OutputFormatter::handleMessage(const QString &text, OutputFormat format) OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, OutputFormat format)
{ {
if (d->nextFormatter) { if (d->nextParser) {
const Result res = d->nextFormatter->handleMessage(text, format); const OutputLineParser::Result res = d->nextParser->handleLine(text, format);
switch (res.status) { switch (res.status) {
case Status::Done: case OutputLineParser::Status::Done:
d->nextFormatter = nullptr; d->nextParser = nullptr;
return res; return res;
case Status::InProgress: case OutputLineParser::Status::InProgress:
return res; return res;
case Status::NotHandled: case OutputLineParser::Status::NotHandled:
QTC_CHECK(false); // TODO: This case will be legal after the merge QTC_CHECK(false); // TODO: This case will be legal after the merge
d->nextFormatter = nullptr; d->nextParser = nullptr;
return res; return res;
} }
} }
QTC_CHECK(!d->nextFormatter); QTC_CHECK(!d->nextParser);
for (OutputFormatter * const formatter : qAsConst(d->formatters)) { for (OutputLineParser * const parser : qAsConst(d->lineParsers)) {
const Result res = formatter->handleMessage(text, format); const OutputLineParser::Result res = parser->handleLine(text, format);
switch (res.status) { switch (res.status) {
case Status::Done: case OutputLineParser::Status::Done:
return res; return res;
case Status::InProgress: case OutputLineParser::Status::InProgress:
d->nextFormatter = formatter; d->nextParser = parser;
return res; return res;
case Status::NotHandled: case OutputLineParser::Status::NotHandled:
break; break;
} }
} }
return Status::NotHandled; return OutputLineParser::Status::NotHandled;
}
void OutputFormatter::reset()
{
for (OutputLineParser * const p : d->lineParsers)
p->reset();
} }
QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const
@@ -145,7 +153,7 @@ QList<FormattedText> OutputFormatter::parseAnsi(const QString &text, const QText
} }
const QList<FormattedText> OutputFormatter::linkifiedText( const QList<FormattedText> OutputFormatter::linkifiedText(
const QList<FormattedText> &text, const OutputFormatter::LinkSpecs &linkSpecs) const QList<FormattedText> &text, const OutputLineParser::LinkSpecs &linkSpecs)
{ {
if (linkSpecs.isEmpty()) if (linkSpecs.isEmpty())
return text; return text;
@@ -171,7 +179,7 @@ const QList<FormattedText> OutputFormatter::linkifiedText(
break; break;
} }
const LinkSpec &linkSpec = linkSpecs.at(nextLinkSpecIndex); const OutputLineParser::LinkSpec &linkSpec = linkSpecs.at(nextLinkSpecIndex);
const int localLinkStartPos = linkSpec.startPos - totalTextLengthSoFar; const int localLinkStartPos = linkSpec.startPos - totalTextLengthSoFar;
++nextLinkSpecIndex; ++nextLinkSpecIndex;
@@ -221,10 +229,12 @@ QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat,
return result; return result;
} }
#ifdef WITH_TESTS
void OutputFormatter::overrideTextCharFormat(const QTextCharFormat &fmt) void OutputFormatter::overrideTextCharFormat(const QTextCharFormat &fmt)
{ {
d->formatOverride = fmt; d->formatOverride = fmt;
} }
#endif // WITH_TESTS
void OutputFormatter::clearLastLine() void OutputFormatter::clearLastLine()
{ {
@@ -266,13 +276,12 @@ void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat forma
d->incompleteLine.second = format; d->incompleteLine.second = format;
} }
bool OutputFormatter::handleLink(const QString &href) void OutputFormatter::handleLink(const QString &href)
{ {
for (OutputFormatter * const f : qAsConst(d->formatters)) { for (OutputLineParser * const f : qAsConst(d->lineParsers)) {
if (f->handleLink(href)) if (f->handleLink(href))
return true; return;
} }
return false;
} }
void OutputFormatter::clear() void OutputFormatter::clear()

View File

@@ -41,33 +41,10 @@ namespace Utils {
class FormattedText; class FormattedText;
namespace Internal { class OutputFormatterPrivate; } class QTCREATOR_UTILS_EXPORT OutputLineParser
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
{ {
public: public:
OutputFormatter(); virtual ~OutputLineParser();
~OutputFormatter() override;
QPlainTextEdit *plainTextEdit() const;
void setPlainTextEdit(QPlainTextEdit *plainText);
void setFormatters(const QList<OutputFormatter *> &formatters);
void flush();
void appendMessage(const QString &text, OutputFormat format);
virtual bool handleLink(const QString &href);
void clear();
void setBoldFontEnabled(bool enabled);
// For unit testing only
void overrideTextCharFormat(const QTextCharFormat &fmt);
protected:
QTextCharFormat charFormat(OutputFormat format) const;
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href);
enum class Status { Done, InProgress, NotHandled }; enum class Status { Done, InProgress, NotHandled };
class LinkSpec { class LinkSpec {
@@ -88,13 +65,51 @@ protected:
optional<QString> newContent; // Hard content override. Only to be used in extreme cases. optional<QString> newContent; // Hard content override. Only to be used in extreme cases.
}; };
// line contains at most one line feed character, and if it does occur, it's the last character.
// Either way, the input is to be considered "complete" for parsing purposes.
virtual Result handleLine(const QString &line, OutputFormat format) = 0;
virtual bool handleLink(const QString &href) { Q_UNUSED(href); return false; }
virtual void reset() {}
};
namespace Internal { class OutputFormatterPrivate; }
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
{
public:
OutputFormatter();
~OutputFormatter() override;
QPlainTextEdit *plainTextEdit() const;
void setPlainTextEdit(QPlainTextEdit *plainText);
void setLineParsers(const QList<OutputLineParser *> &parsers);
void appendMessage(const QString &text, OutputFormat format);
void flush();
void handleLink(const QString &href);
void clear();
void setBoldFontEnabled(bool enabled);
#ifdef WITH_TESTS
void overrideTextCharFormat(const QTextCharFormat &fmt);
#endif
#ifndef WITH_TESTS
private:
#endif
QTextCharFormat charFormat(OutputFormat format) const;
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href);
private: private:
// text contains at most one line feed character, and if it does occur, it's the last character.
// Either way, the input is to be considered "complete" for formatting purposes.
void doAppendMessage(const QString &text, OutputFormat format); void doAppendMessage(const QString &text, OutputFormat format);
virtual Result handleMessage(const QString &text, OutputFormat format); OutputLineParser::Result handleMessage(const QString &text, OutputFormat format);
virtual void reset() {} void reset();
void append(const QString &text, const QTextCharFormat &format); void append(const QString &text, const QTextCharFormat &format);
void initFormats(); void initFormats();
@@ -103,9 +118,10 @@ private:
void clearLastLine(); void clearLastLine();
QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format); QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format);
const QList<Utils::FormattedText> linkifiedText(const QList<FormattedText> &text, const QList<Utils::FormattedText> linkifiedText(const QList<FormattedText> &text,
const LinkSpecs &linkSpecs); const OutputLineParser::LinkSpecs &linkSpecs);
Internal::OutputFormatterPrivate *d; Internal::OutputFormatterPrivate *d;
}; };
} // namespace Utils } // namespace Utils

View File

@@ -216,9 +216,9 @@ void OutputWindow::keyPressEvent(QKeyEvent *ev)
verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
} }
void OutputWindow::setFormatters(const QList<OutputFormatter *> &formatters) void OutputWindow::setLineParsers(const QList<OutputLineParser *> &parsers)
{ {
d->formatter.setFormatters(formatters); d->formatter.setLineParsers(parsers);
} }
void OutputWindow::showEvent(QShowEvent *e) void OutputWindow::showEvent(QShowEvent *e)
@@ -503,10 +503,10 @@ void OutputWindow::setWordWrapEnabled(bool wrap)
// Handles all lines starting with "A" and the following ones up to and including the next // Handles all lines starting with "A" and the following ones up to and including the next
// one starting with "A". // one starting with "A".
class TestFormatterA : public OutputFormatter class TestFormatterA : public OutputLineParser
{ {
private: private:
Result handleMessage(const QString &text, OutputFormat) override Result handleLine(const QString &text, OutputFormat) override
{ {
static const QString replacement = "handled by A\n"; static const QString replacement = "handled by A\n";
if (m_handling) { if (m_handling) {
@@ -529,10 +529,10 @@ private:
}; };
// Handles all lines starting with "B". No continuation logic // Handles all lines starting with "B". No continuation logic
class TestFormatterB : public OutputFormatter class TestFormatterB : public OutputLineParser
{ {
private: private:
Result handleMessage(const QString &text, OutputFormat) override Result handleLine(const QString &text, OutputFormat) override
{ {
if (text.startsWith("B")) if (text.startsWith("B"))
return {Status::Done, {}, QString("handled by B\n")}; return {Status::Done, {}, QString("handled by B\n")};
@@ -571,7 +571,7 @@ void Internal::CorePlugin::testOutputFormatter()
OutputFormatter formatter; OutputFormatter formatter;
QPlainTextEdit textEdit; QPlainTextEdit textEdit;
formatter.setPlainTextEdit(&textEdit); formatter.setPlainTextEdit(&textEdit);
formatter.setFormatters({&formatterB, &formatterA}); formatter.setLineParsers({&formatterB, &formatterA});
// Stress-test the implementation by providing the input in chunks, splitting at all possible // Stress-test the implementation by providing the input in chunks, splitting at all possible
// offsets. // offsets.

View File

@@ -34,7 +34,7 @@
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QTimer> #include <QTimer>
namespace Utils { class OutputFormatter; } namespace Utils { class OutputLineParser; }
namespace Core { namespace Core {
@@ -56,7 +56,7 @@ public:
OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr); OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr);
~OutputWindow() override; ~OutputWindow() override;
void setFormatters(const QList<Utils::OutputFormatter *> &formatters); void setLineParsers(const QList<Utils::OutputLineParser *> &parsers);
void appendMessage(const QString &out, Utils::OutputFormat format); void appendMessage(const QString &out, Utils::OutputFormat format);

View File

@@ -154,7 +154,7 @@ AppOutputPane::RunControlTab::RunControlTab(RunControl *runControl, Core::Output
runControl(runControl), window(w) runControl(runControl), window(w)
{ {
if (runControl && w) if (runControl && w)
w->setFormatters(runControl->outputFormatters()); w->setLineParsers(runControl->outputParsers());
} }
AppOutputPane::AppOutputPane() : AppOutputPane::AppOutputPane() :
@@ -404,7 +404,7 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc)
if (tab.runControl) if (tab.runControl)
tab.runControl->initiateFinish(); tab.runControl->initiateFinish();
tab.runControl = rc; tab.runControl = rc;
tab.window->setFormatters(rc->outputFormatters()); tab.window->setLineParsers(rc->outputParsers());
handleOldOutput(tab.window); handleOldOutput(tab.window);

View File

@@ -288,7 +288,7 @@ public:
q = nullptr; q = nullptr;
qDeleteAll(m_workers); qDeleteAll(m_workers);
m_workers.clear(); m_workers.clear();
qDeleteAll(outputFormatters); qDeleteAll(outputParsers);
} }
Q_ENUM(RunControlState) Q_ENUM(RunControlState)
@@ -333,7 +333,7 @@ public:
Kit *kit = nullptr; // Not owned. Kit *kit = nullptr; // Not owned.
QPointer<Target> target; // Not owned. QPointer<Target> target; // Not owned.
QPointer<Project> project; // Not owned. QPointer<Project> project; // Not owned.
QList<Utils::OutputFormatter *> outputFormatters; QList<Utils::OutputLineParser *> outputParsers;
std::function<bool(bool*)> promptToStop; std::function<bool(bool*)> promptToStop;
std::vector<RunWorkerFactory> m_factories; std::vector<RunWorkerFactory> m_factories;
@@ -384,8 +384,8 @@ void RunControl::setTarget(Target *target)
d->buildEnvironment = bc->environment(); d->buildEnvironment = bc->environment();
} }
QTC_CHECK(d->outputFormatters.isEmpty()); QTC_CHECK(d->outputParsers.isEmpty());
d->outputFormatters = OutputFormatterFactory::createFormatters(target); d->outputParsers = OutputFormatterFactory::createFormatters(target);
setKit(target->kit()); setKit(target->kit());
d->project = target->project(); d->project = target->project();
@@ -828,9 +828,9 @@ void RunControlPrivate::showError(const QString &msg)
q->appendMessage(msg + '\n', ErrorMessageFormat); q->appendMessage(msg + '\n', ErrorMessageFormat);
} }
QList<Utils::OutputFormatter *> RunControl::outputFormatters() const QList<Utils::OutputLineParser *> RunControl::outputParsers() const
{ {
return d->outputFormatters; return d->outputParsers;
} }
Core::Id RunControl::runMode() const Core::Id RunControl::runMode() const
@@ -1606,9 +1606,9 @@ OutputFormatterFactory::~OutputFormatterFactory()
g_outputFormatterFactories.removeOne(this); g_outputFormatterFactories.removeOne(this);
} }
QList<OutputFormatter *> OutputFormatterFactory::createFormatters(Target *target) QList<OutputLineParser *> OutputFormatterFactory::createFormatters(Target *target)
{ {
QList<OutputFormatter *> formatters; QList<OutputLineParser *> formatters;
for (auto factory : qAsConst(g_outputFormatterFactories)) { for (auto factory : qAsConst(g_outputFormatterFactories)) {
if (auto formatter = factory->m_creator(target)) if (auto formatter = factory->m_creator(target))
formatters << formatter; formatters << formatter;
@@ -1617,7 +1617,7 @@ QList<OutputFormatter *> OutputFormatterFactory::createFormatters(Target *target
} }
void OutputFormatterFactory::setFormatterCreator void OutputFormatterFactory::setFormatterCreator
(const std::function<OutputFormatter *(Target *)> &creator) (const std::function<OutputLineParser *(Target *)> &creator)
{ {
m_creator = creator; m_creator = creator;
} }

View File

@@ -45,7 +45,7 @@
namespace Utils { namespace Utils {
class MacroExpander; class MacroExpander;
class OutputFormatter; class OutputLineParser;
} // Utils } // Utils
namespace ProjectExplorer { namespace ProjectExplorer {
@@ -238,7 +238,7 @@ public:
Utils::FilePath targetFilePath() const; Utils::FilePath targetFilePath() const;
Utils::FilePath projectFilePath() const; Utils::FilePath projectFilePath() const;
QList<Utils::OutputFormatter *> outputFormatters() const; QList<Utils::OutputLineParser *> outputParsers() const;
Core::Id runMode() const; Core::Id runMode() const;
const Runnable &runnable() const; const Runnable &runnable() const;
@@ -309,13 +309,13 @@ protected:
public: public:
virtual ~OutputFormatterFactory(); virtual ~OutputFormatterFactory();
static QList<Utils::OutputFormatter *> createFormatters(Target *target); static QList<Utils::OutputLineParser *> createFormatters(Target *target);
protected: protected:
void setFormatterCreator(const std::function<Utils::OutputFormatter *(Target *)> &creator); void setFormatterCreator(const std::function<Utils::OutputLineParser *(Target *)> &creator);
private: private:
std::function<Utils::OutputFormatter *(Target *)> m_creator; std::function<Utils::OutputLineParser *(Target *)> m_creator;
}; };
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -60,10 +60,10 @@ using namespace Utils;
namespace Python { namespace Python {
namespace Internal { namespace Internal {
class PythonOutputFormatter : public OutputFormatter class PythonOutputLineParser : public OutputLineParser
{ {
public: public:
PythonOutputFormatter() PythonOutputLineParser()
// Note that moc dislikes raw string literals. // Note that moc dislikes raw string literals.
: filePattern("^(\\s*)(File \"([^\"]+)\", line (\\d+), .*$)") : filePattern("^(\\s*)(File \"([^\"]+)\", line (\\d+), .*$)")
{ {
@@ -71,7 +71,7 @@ public:
} }
private: private:
Result handleMessage(const QString &text, OutputFormat format) final Result handleLine(const QString &text, OutputFormat format) final
{ {
if (!m_inTraceBack) { if (!m_inTraceBack) {
m_inTraceBack = format == StdErrFormat m_inTraceBack = format == StdErrFormat
@@ -337,9 +337,9 @@ PythonRunConfigurationFactory::PythonRunConfigurationFactory()
PythonOutputFormatterFactory::PythonOutputFormatterFactory() PythonOutputFormatterFactory::PythonOutputFormatterFactory()
{ {
setFormatterCreator([](Target *t) -> OutputFormatter * { setFormatterCreator([](Target *t) -> OutputLineParser * {
if (t->project()->mimeType() == Constants::C_PY_MIMETYPE) if (t->project()->mimeType() == Constants::C_PY_MIMETYPE)
return new PythonOutputFormatter; return new PythonOutputLineParser;
return nullptr; return nullptr;
}); });
} }

View File

@@ -79,17 +79,17 @@ public:
FileInProjectFinder projectFinder; FileInProjectFinder projectFinder;
}; };
class QtOutputFormatter : public OutputFormatter class QtOutputLineParser : public QObject, public OutputLineParser
{ {
public: public:
explicit QtOutputFormatter(Target *target); explicit QtOutputLineParser(Target *target);
~QtOutputFormatter() override; ~QtOutputLineParser() override;
protected: protected:
virtual void openEditor(const QString &fileName, int line, int column = -1); virtual void openEditor(const QString &fileName, int line, int column = -1);
private: private:
Result handleMessage(const QString &text, Utils::OutputFormat format) override; Result handleLine(const QString &text, Utils::OutputFormat format) override;
bool handleLink(const QString &href) override; bool handleLink(const QString &href) override;
void updateProjectFileList(); void updateProjectFileList();
@@ -99,7 +99,7 @@ private:
friend class QtSupportPlugin; // for testing friend class QtSupportPlugin; // for testing
}; };
QtOutputFormatter::QtOutputFormatter(Target *target) QtOutputLineParser::QtOutputLineParser(Target *target)
: d(new QtOutputFormatterPrivate) : d(new QtOutputFormatterPrivate)
{ {
d->project = target ? target->project() : nullptr; d->project = target ? target->project() : nullptr;
@@ -110,17 +110,17 @@ QtOutputFormatter::QtOutputFormatter(Target *target)
connect(d->project, connect(d->project,
&Project::fileListChanged, &Project::fileListChanged,
this, this,
&QtOutputFormatter::updateProjectFileList, &QtOutputLineParser::updateProjectFileList,
Qt::QueuedConnection); Qt::QueuedConnection);
} }
} }
QtOutputFormatter::~QtOutputFormatter() QtOutputLineParser::~QtOutputLineParser()
{ {
delete d; delete d;
} }
OutputFormatter::LinkSpec QtOutputFormatter::matchLine(const QString &line) const OutputLineParser::LinkSpec QtOutputLineParser::matchLine(const QString &line) const
{ {
LinkSpec lr; LinkSpec lr;
@@ -151,7 +151,7 @@ OutputFormatter::LinkSpec QtOutputFormatter::matchLine(const QString &line) cons
return lr; return lr;
} }
OutputFormatter::Result QtOutputFormatter::handleMessage(const QString &txt, OutputFormat format) OutputLineParser::Result QtOutputLineParser::handleLine(const QString &txt, OutputFormat format)
{ {
Q_UNUSED(format); Q_UNUSED(format);
const LinkSpec lr = matchLine(txt); const LinkSpec lr = matchLine(txt);
@@ -160,7 +160,7 @@ OutputFormatter::Result QtOutputFormatter::handleMessage(const QString &txt, Out
return Status::NotHandled; return Status::NotHandled;
} }
bool QtOutputFormatter::handleLink(const QString &href) bool QtOutputLineParser::handleLink(const QString &href)
{ {
if (!href.isEmpty()) { if (!href.isEmpty()) {
static const QRegularExpression qmlLineColumnLink("^(" QT_QML_URL_REGEXP ")" // url static const QRegularExpression qmlLineColumnLink("^(" QT_QML_URL_REGEXP ")" // url
@@ -227,12 +227,12 @@ bool QtOutputFormatter::handleLink(const QString &href)
return false; return false;
} }
void QtOutputFormatter::openEditor(const QString &fileName, int line, int column) void QtOutputLineParser::openEditor(const QString &fileName, int line, int column)
{ {
Core::EditorManager::openEditorAt(fileName, line, column); Core::EditorManager::openEditorAt(fileName, line, column);
} }
void QtOutputFormatter::updateProjectFileList() void QtOutputLineParser::updateProjectFileList()
{ {
if (d->project) if (d->project)
d->projectFinder.setProjectFiles(d->project->files(Project::SourceFiles)); d->projectFinder.setProjectFiles(d->project->files(Project::SourceFiles));
@@ -242,9 +242,9 @@ void QtOutputFormatter::updateProjectFileList()
QtOutputFormatterFactory::QtOutputFormatterFactory() QtOutputFormatterFactory::QtOutputFormatterFactory()
{ {
setFormatterCreator([](Target *t) -> OutputFormatter * { setFormatterCreator([](Target *t) -> OutputLineParser * {
BaseQtVersion *qt = QtKitAspect::qtVersion(t->kit()); BaseQtVersion *qt = QtKitAspect::qtVersion(t->kit());
return qt ? new QtOutputFormatter(t) : nullptr; return qt ? new QtOutputLineParser(t) : nullptr;
}); });
} }
@@ -265,11 +265,11 @@ namespace QtSupport {
using namespace QtSupport::Internal; using namespace QtSupport::Internal;
class TestQtOutputFormatter : public QtOutputFormatter class TestQtOutputLineParser : public QtOutputLineParser
{ {
public: public:
TestQtOutputFormatter() : TestQtOutputLineParser() :
QtOutputFormatter(nullptr) QtOutputLineParser(nullptr)
{ {
} }
@@ -286,6 +286,20 @@ public:
int column = -1; int column = -1;
}; };
class TestQtOutputFormatter : public OutputFormatter
{
public:
TestQtOutputFormatter() : m_parser(new TestQtOutputLineParser)
{
setLineParsers({m_parser});
}
~TestQtOutputFormatter() { delete m_parser; }
private:
OutputLineParser * const m_parser;
};
void QtSupportPlugin::testQtOutputFormatter_data() void QtSupportPlugin::testQtOutputFormatter_data()
{ {
@@ -409,9 +423,9 @@ void QtSupportPlugin::testQtOutputFormatter()
QFETCH(int, line); QFETCH(int, line);
QFETCH(int, column); QFETCH(int, column);
TestQtOutputFormatter formatter; TestQtOutputLineParser formatter;
QtOutputFormatter::LinkSpec result = formatter.matchLine(input); QtOutputLineParser::LinkSpec result = formatter.matchLine(input);
formatter.handleLink(result.target); formatter.handleLink(result.target);
QCOMPARE(result.startPos, linkStart); QCOMPARE(result.startPos, linkStart);
@@ -453,7 +467,7 @@ void QtSupportPlugin::testQtOutputFormatter_appendMessage_data()
<< "Object::Test in test.cpp:123" << "Object::Test in test.cpp:123"
<< "Object::Test in test.cpp:123" << "Object::Test in test.cpp:123"
<< QTextCharFormat() << QTextCharFormat()
<< QtOutputFormatter::linkFormat(QTextCharFormat(), "test.cpp:123"); << OutputFormatter::linkFormat(QTextCharFormat(), "test.cpp:123");
QTest::newRow("colored") QTest::newRow("colored")
<< "blue da ba dee" << "blue da ba dee"
<< "blue da ba dee" << "blue da ba dee"
@@ -491,6 +505,7 @@ void QtSupportPlugin::testQtOutputFormatter_appendMessage()
void QtSupportPlugin::testQtOutputFormatter_appendMixedAssertAndAnsi() void QtSupportPlugin::testQtOutputFormatter_appendMixedAssertAndAnsi()
{ {
QPlainTextEdit edit; QPlainTextEdit edit;
TestQtOutputFormatter formatter; TestQtOutputFormatter formatter;
formatter.setPlainTextEdit(&edit); formatter.setPlainTextEdit(&edit);
@@ -512,7 +527,8 @@ void QtSupportPlugin::testQtOutputFormatter_appendMixedAssertAndAnsi()
edit.moveCursor(QTextCursor::WordRight); edit.moveCursor(QTextCursor::WordRight);
edit.moveCursor(QTextCursor::Right); edit.moveCursor(QTextCursor::Right);
QCOMPARE(edit.currentCharFormat(), QtOutputFormatter::linkFormat(QTextCharFormat(), "file://test.cpp:123")); QCOMPARE(edit.currentCharFormat(),
OutputFormatter::linkFormat(QTextCharFormat(), "file://test.cpp:123"));
edit.moveCursor(QTextCursor::End); edit.moveCursor(QTextCursor::End);
QCOMPARE(edit.currentCharFormat(), blueFormat()); QCOMPARE(edit.currentCharFormat(), blueFormat());

View File

@@ -34,7 +34,7 @@
namespace VcsBase { namespace VcsBase {
VcsOutputFormatter::VcsOutputFormatter() : VcsOutputLineParser::VcsOutputLineParser() :
m_regexp( m_regexp(
"(https?://\\S*)" // https://codereview.org/c/1234 "(https?://\\S*)" // https://codereview.org/c/1234
"|(v[0-9]+\\.[0-9]+\\.[0-9]+[\\-A-Za-z0-9]*)" // v0.1.2-beta3 "|(v[0-9]+\\.[0-9]+\\.[0-9]+[\\-A-Za-z0-9]*)" // v0.1.2-beta3
@@ -43,8 +43,8 @@ VcsOutputFormatter::VcsOutputFormatter() :
{ {
} }
Utils::OutputFormatter::Result VcsOutputFormatter::handleMessage(const QString &text, Utils::OutputLineParser::Result VcsOutputLineParser::handleLine(const QString &text,
Utils::OutputFormat format) Utils::OutputFormat format)
{ {
Q_UNUSED(format); Q_UNUSED(format);
QRegularExpressionMatchIterator it = m_regexp.globalMatch(text); QRegularExpressionMatchIterator it = m_regexp.globalMatch(text);
@@ -62,7 +62,7 @@ Utils::OutputFormatter::Result VcsOutputFormatter::handleMessage(const QString &
return {Status::Done, linkSpecs}; return {Status::Done, linkSpecs};
} }
bool VcsOutputFormatter::handleLink(const QString &href) bool VcsOutputLineParser::handleLink(const QString &href)
{ {
if (href.startsWith("http://") || href.startsWith("https://")) if (href.startsWith("http://") || href.startsWith("https://"))
QDesktopServices::openUrl(QUrl(href)); QDesktopServices::openUrl(QUrl(href));
@@ -71,7 +71,7 @@ bool VcsOutputFormatter::handleLink(const QString &href)
return true; return true;
} }
void VcsOutputFormatter::fillLinkContextMenu( void VcsOutputLineParser::fillLinkContextMenu(
QMenu *menu, const QString &workingDirectory, const QString &href) QMenu *menu, const QString &workingDirectory, const QString &href)
{ {
if (href.isEmpty() || href.startsWith("http://") || href.startsWith("https://")) { if (href.isEmpty() || href.startsWith("http://") || href.startsWith("https://")) {

View File

@@ -31,20 +31,19 @@ QT_FORWARD_DECLARE_CLASS(QMenu)
namespace VcsBase { namespace VcsBase {
class VcsOutputFormatter : public Utils::OutputFormatter class VcsOutputLineParser : public QObject, public Utils::OutputLineParser
{ {
Q_OBJECT Q_OBJECT
public: public:
VcsOutputFormatter(); VcsOutputLineParser();
~VcsOutputFormatter() override = default;
bool handleLink(const QString &href) override;
void fillLinkContextMenu(QMenu *menu, const QString &workingDirectory, const QString &href); void fillLinkContextMenu(QMenu *menu, const QString &workingDirectory, const QString &href);
signals: signals:
void referenceClicked(const QString &reference); void referenceClicked(const QString &reference);
private: private:
Result handleMessage(const QString &text, Utils::OutputFormat format) override; Result handleLine(const QString &text, Utils::OutputFormat format) override;
bool handleLink(const QString &href) override;
const QRegularExpression m_regexp; const QRegularExpression m_regexp;
}; };

View File

@@ -97,12 +97,11 @@ class OutputWindowPlainTextEdit : public Core::OutputWindow
{ {
public: public:
explicit OutputWindowPlainTextEdit(QWidget *parent = nullptr); explicit OutputWindowPlainTextEdit(QWidget *parent = nullptr);
~OutputWindowPlainTextEdit() override;
void appendLines(const QString &s, const QString &repository = QString()); void appendLines(const QString &s, const QString &repository = QString());
void appendLinesWithStyle(const QString &s, VcsOutputWindow::MessageStyle style, void appendLinesWithStyle(const QString &s, VcsOutputWindow::MessageStyle style,
const QString &repository = QString()); const QString &repository = QString());
VcsOutputFormatter *formatter(); VcsOutputLineParser *parser();
protected: protected:
void contextMenuEvent(QContextMenuEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override;
@@ -112,7 +111,8 @@ private:
QString identifierUnderCursor(const QPoint &pos, QString *repository = nullptr) const; QString identifierUnderCursor(const QPoint &pos, QString *repository = nullptr) const;
Utils::OutputFormat m_format; Utils::OutputFormat m_format;
VcsOutputFormatter *m_formatter = nullptr; Utils::OutputFormatter m_formatter;
VcsOutputLineParser m_parser;
}; };
OutputWindowPlainTextEdit::OutputWindowPlainTextEdit(QWidget *parent) : OutputWindowPlainTextEdit::OutputWindowPlainTextEdit(QWidget *parent) :
@@ -121,19 +121,13 @@ OutputWindowPlainTextEdit::OutputWindowPlainTextEdit(QWidget *parent) :
setReadOnly(true); setReadOnly(true);
setUndoRedoEnabled(false); setUndoRedoEnabled(false);
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
m_formatter = new VcsOutputFormatter; m_formatter.setBoldFontEnabled(false);
m_formatter->setBoldFontEnabled(false); setLineParsers({&m_parser});
setFormatters({m_formatter});
auto agg = new Aggregation::Aggregate; auto agg = new Aggregation::Aggregate;
agg->add(this); agg->add(this);
agg->add(new Core::BaseTextFind(this)); agg->add(new Core::BaseTextFind(this));
} }
OutputWindowPlainTextEdit::~OutputWindowPlainTextEdit()
{
delete m_formatter;
}
// Search back for beginning of word // Search back for beginning of word
static inline int firstWordCharacter(const QString &s, int startPos) static inline int firstWordCharacter(const QString &s, int startPos)
{ {
@@ -181,9 +175,9 @@ void OutputWindowPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
QString repository; QString repository;
const QString token = identifierUnderCursor(event->pos(), &repository); const QString token = identifierUnderCursor(event->pos(), &repository);
if (!repository.isEmpty()) { if (!repository.isEmpty()) {
if (VcsOutputFormatter *f = formatter()) { if (VcsOutputLineParser * const p = parser()) {
if (!href.isEmpty()) if (!href.isEmpty())
f->fillLinkContextMenu(menu, repository, href); p->fillLinkContextMenu(menu, repository, href);
} }
} }
QAction *openAction = nullptr; QAction *openAction = nullptr;
@@ -228,7 +222,7 @@ void OutputWindowPlainTextEdit::appendLines(const QString &s, const QString &rep
const int previousLineCount = document()->lineCount(); const int previousLineCount = document()->lineCount();
m_formatter->appendMessage(s, m_format); m_formatter.appendMessage(s, m_format);
// Scroll down // Scroll down
moveCursor(QTextCursor::End); moveCursor(QTextCursor::End);
@@ -255,14 +249,14 @@ void OutputWindowPlainTextEdit::appendLinesWithStyle(const QString &s,
} }
} }
VcsOutputFormatter *OutputWindowPlainTextEdit::formatter() VcsOutputLineParser *OutputWindowPlainTextEdit::parser()
{ {
return m_formatter; return &m_parser;
} }
void OutputWindowPlainTextEdit::setFormat(VcsOutputWindow::MessageStyle style) void OutputWindowPlainTextEdit::setFormat(VcsOutputWindow::MessageStyle style)
{ {
m_formatter->setBoldFontEnabled(style == VcsOutputWindow::Command); m_formatter.setBoldFontEnabled(style == VcsOutputWindow::Command);
switch (style) { switch (style) {
case VcsOutputWindow::Warning: case VcsOutputWindow::Warning:
@@ -318,7 +312,7 @@ VcsOutputWindow::VcsOutputWindow()
connect(this, &IOutputPane::resetZoom, &d->widget, &Core::OutputWindow::resetZoom); connect(this, &IOutputPane::resetZoom, &d->widget, &Core::OutputWindow::resetZoom);
connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged,
this, updateBehaviorSettings); this, updateBehaviorSettings);
connect(d->widget.formatter(), &VcsOutputFormatter::referenceClicked, connect(d->widget.parser(), &VcsOutputLineParser::referenceClicked,
VcsOutputWindow::instance(), &VcsOutputWindow::referenceClicked); VcsOutputWindow::instance(), &VcsOutputWindow::referenceClicked);
} }