diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libs/utils/outputformatter.cpp | 86 | ||||
-rw-r--r-- | src/libs/utils/outputformatter.h | 39 | ||||
-rw-r--r-- | src/plugins/coreplugin/coreplugin.h | 2 | ||||
-rw-r--r-- | src/plugins/coreplugin/outputwindow.cpp | 114 | ||||
-rw-r--r-- | src/plugins/coreplugin/outputwindow.h | 3 | ||||
-rw-r--r-- | src/plugins/projectexplorer/appoutputpane.cpp | 12 | ||||
-rw-r--r-- | src/plugins/projectexplorer/compileoutputwindow.cpp | 2 | ||||
-rw-r--r-- | src/plugins/projectexplorer/runcontrol.cpp | 28 | ||||
-rw-r--r-- | src/plugins/projectexplorer/runcontrol.h | 4 | ||||
-rw-r--r-- | src/plugins/python/pythonrunconfiguration.cpp | 20 | ||||
-rw-r--r-- | src/plugins/qtsupport/qtoutputformatter.cpp | 67 | ||||
-rw-r--r-- | src/plugins/serialterminal/serialoutputpane.cpp | 2 | ||||
-rw-r--r-- | src/plugins/vcsbase/vcsoutputformatter.cpp | 13 | ||||
-rw-r--r-- | src/plugins/vcsbase/vcsoutputformatter.h | 5 | ||||
-rw-r--r-- | src/plugins/vcsbase/vcsoutputwindow.cpp | 2 |
15 files changed, 314 insertions, 85 deletions
diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp index ea40ddadee..a1a98a1fbe 100644 --- a/src/libs/utils/outputformatter.cpp +++ b/src/libs/utils/outputformatter.cpp @@ -25,8 +25,10 @@ #include "ansiescapecodehandler.h" #include "outputformatter.h" +#include "qtcassert.h" #include "synchronousprocess.h" #include "theme/theme.h" +#include "utils/optional.h" #include <QPair> #include <QPlainTextEdit> @@ -44,6 +46,7 @@ public: QTextCursor cursor; AnsiEscapeCodeHandler escapeCodeHandler; QPair<QString, OutputFormat> incompleteLine; + optional<QTextCharFormat> formatOverride; bool boldFontEnabled = true; bool prependCarriageReturn = false; }; @@ -75,9 +78,15 @@ void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText) void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format) { - if (!d->cursor.atEnd() && text.startsWith('\n')) - d->cursor.movePosition(QTextCursor::End); - doAppendMessage(text, d->formats[format]); + if (handleMessage(text, format) == Status::NotHandled) + appendMessageDefault(text, format); +} + +OutputFormatter::Status OutputFormatter::handleMessage(const QString &text, OutputFormat format) +{ + Q_UNUSED(text); + Q_UNUSED(format); + return Status::NotHandled; } void OutputFormatter::doAppendMessage(const QString &text, const QTextCharFormat &format) @@ -118,6 +127,16 @@ QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat, return result; } +void OutputFormatter::overrideTextCharFormat(const QTextCharFormat &fmt) +{ + d->formatOverride = fmt; +} + +void OutputFormatter::appendMessageDefault(const QString &text, OutputFormat format) +{ + doAppendMessage(text, d->formatOverride ? d->formatOverride.value() : d->formats[format]); +} + void OutputFormatter::clearLastLine() { // Note that this approach will fail if the text edit is not read-only and users @@ -158,9 +177,10 @@ void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat forma d->incompleteLine.second = format; } -void OutputFormatter::handleLink(const QString &href) +bool OutputFormatter::handleLink(const QString &href) { Q_UNUSED(href) + return false; } void OutputFormatter::clear() @@ -236,4 +256,62 @@ void OutputFormatter::appendMessage(const QString &text, OutputFormat format) } } +class AggregatingOutputFormatter::Private +{ +public: + QList<OutputFormatter *> formatters; + OutputFormatter *nextFormatter = nullptr; +}; + +AggregatingOutputFormatter::AggregatingOutputFormatter() : d(new Private) {} +AggregatingOutputFormatter::~AggregatingOutputFormatter() { delete d; } + +void AggregatingOutputFormatter::setFormatters(const QList<OutputFormatter *> &formatters) +{ + for (OutputFormatter * const f : formatters) + f->setPlainTextEdit(plainTextEdit()); + d->formatters = formatters; + d->nextFormatter = nullptr; +} + +OutputFormatter::Status AggregatingOutputFormatter::handleMessage(const QString &text, + OutputFormat format) +{ + if (d->nextFormatter) { + switch (d->nextFormatter->handleMessage(text, format)) { + case Status::Done: + d->nextFormatter = nullptr; + return Status::Done; + case Status::InProgress: + return Status::InProgress; + case Status::NotHandled: + QTC_CHECK(false); + d->nextFormatter = nullptr; + return Status::NotHandled; + } + } + QTC_CHECK(!d->nextFormatter); + for (OutputFormatter * const formatter : qAsConst(d->formatters)) { + switch (formatter->handleMessage(text, format)) { + case Status::Done: + return Status::Done; + case Status::InProgress: + d->nextFormatter = formatter; + return Status::InProgress; + case Status::NotHandled: + break; + } + } + return Status::NotHandled; +} + +bool AggregatingOutputFormatter::handleLink(const QString &href) +{ + for (OutputFormatter * const f : qAsConst(d->formatters)) { + if (f->handleLink(href)) + return true; + } + return false; +} + } // namespace Utils diff --git a/src/libs/utils/outputformatter.h b/src/libs/utils/outputformatter.h index a5d9c4b331..0aaf880714 100644 --- a/src/libs/utils/outputformatter.h +++ b/src/libs/utils/outputformatter.h @@ -42,8 +42,11 @@ class FormattedText; namespace Internal { class OutputFormatterPrivate; } +class QTCREATOR_UTILS_EXPORT AggregatingOutputFormatter; + class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject { + friend class AggregatingOutputFormatter; public: OutputFormatter(); ~OutputFormatter() override; @@ -55,26 +58,32 @@ public: void appendMessage(const QString &text, OutputFormat format); - virtual void handleLink(const QString &href); + virtual bool handleLink(const QString &href); void clear(); void setBoldFontEnabled(bool enabled); static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href); -protected: - // 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. - virtual void doAppendMessage(const QString &text, OutputFormat format); + // For unit testing only + void overrideTextCharFormat(const QTextCharFormat &fmt); - virtual void clearLastLine(); +protected: + enum class Status { Done, InProgress, NotHandled }; + void appendMessageDefault(const QString &text, OutputFormat format); + void clearLastLine(); QTextCharFormat charFormat(OutputFormat format) const; QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format); QTextCursor &cursor() const; private: - void doAppendMessage(const QString &text, const QTextCharFormat &format); + // 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); + + virtual Status handleMessage(const QString &text, OutputFormat format); virtual void reset() {} + void doAppendMessage(const QString &text, const QTextCharFormat &format); void append(const QString &text, const QTextCharFormat &format); void initFormats(); void flushIncompleteLine(); @@ -83,4 +92,20 @@ private: Internal::OutputFormatterPrivate *d; }; +class QTCREATOR_UTILS_EXPORT AggregatingOutputFormatter : public OutputFormatter +{ +public: + AggregatingOutputFormatter(); + ~AggregatingOutputFormatter(); + + void setFormatters(const QList<OutputFormatter *> &formatters); + bool handleLink(const QString &href) override; + +private: + Status handleMessage(const QString &text, OutputFormat format) override; + + class Private; + Private * const d; +}; + } // namespace Utils diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h index fee1458eab..04c0b1d117 100644 --- a/src/plugins/coreplugin/coreplugin.h +++ b/src/plugins/coreplugin/coreplugin.h @@ -76,6 +76,8 @@ private slots: // Locator: void test_basefilefilter(); void test_basefilefilter_data(); + + void testOutputFormatter(); #endif private: diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp index fc61bcc750..e6b03b2c7a 100644 --- a/src/plugins/coreplugin/outputwindow.cpp +++ b/src/plugins/coreplugin/outputwindow.cpp @@ -27,6 +27,7 @@ #include "actionmanager/actionmanager.h" #include "coreconstants.h" +#include "coreplugin.h" #include "icore.h" #include <utils/outputformatter.h> @@ -39,6 +40,10 @@ #include <QScrollBar> #include <QTextBlock> +#ifdef WITH_TESTS +#include <QtTest> +#endif + using namespace Utils; namespace Core { @@ -60,9 +65,8 @@ public: } IContext *outputWindowContext = nullptr; - QPointer<Utils::OutputFormatter> formatter; QString settingsKey; - OutputFormatter defaultFormatter; + AggregatingOutputFormatter formatter; bool scrollToBottom = true; bool linksActive = true; @@ -91,7 +95,7 @@ OutputWindow::OutputWindow(Context context, const QString &settingsKey, QWidget setFrameShape(QFrame::NoFrame); setMouseTracking(true); setUndoRedoEnabled(false); - setFormatter(&d->defaultFormatter); + d->formatter.setPlainTextEdit(this); d->settingsKey = settingsKey; @@ -168,7 +172,7 @@ void OutputWindow::mouseReleaseEvent(QMouseEvent *e) { if (d->linksActive && d->mouseButtonPressed == Qt::LeftButton) { const QString href = anchorAt(e->pos()); - d->formatter->handleLink(href); + d->formatter.handleLink(href); } // Mouse was released, activate links again @@ -212,13 +216,9 @@ void OutputWindow::keyPressEvent(QKeyEvent *ev) verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); } -void OutputWindow::setFormatter(OutputFormatter *formatter) +void OutputWindow::setFormatters(const QList<OutputFormatter *> &formatters) { - d->formatter = formatter; - if (d->formatter) - d->formatter->setPlainTextEdit(this); - else - d->formatter = &d->defaultFormatter; + d->formatter.setFormatters(formatters); } void OutputWindow::showEvent(QShowEvent *e) @@ -396,7 +396,7 @@ void OutputWindow::appendMessage(const QString &output, OutputFormat format) const bool atBottom = isScrollbarAtBottom() || m_scrollTimer.isActive(); d->scrollToBottom = true; - d->formatter->appendMessage(out, format); + d->formatter.appendMessage(out, format); if (atBottom) { if (m_lastMessage.elapsed() < 5) { @@ -445,7 +445,12 @@ QMimeData *OutputWindow::createMimeDataFromSelection() const void OutputWindow::clear() { - d->formatter->clear(); + d->formatter.clear(); +} + +void OutputWindow::flush() +{ + d->formatter.flush(); } void OutputWindow::scrollToBottom() @@ -494,4 +499,89 @@ void OutputWindow::setWordWrapEnabled(bool wrap) setWordWrapMode(QTextOption::NoWrap); } +#ifdef WITH_TESTS + +// Handles all lines starting with "A" and the following ones up to and including the next +// one starting with "A". +class TestFormatterA : public OutputFormatter +{ +private: + Status handleMessage(const QString &text, OutputFormat format) override + { + if (m_handling) { + appendMessageDefault("handled by A\n", format); + if (text.startsWith("A")) { + m_handling = false; + return Status::Done; + } + return Status::InProgress; + } + if (text.startsWith("A")) { + m_handling = true; + appendMessageDefault("handled by A\n", format); + return Status::InProgress; + } + return Status::NotHandled; + } + + void reset() override { m_handling = false; } + + bool m_handling = false; +}; + +// Handles all lines starting with "B". No continuation logic +class TestFormatterB : public OutputFormatter +{ +private: + Status handleMessage(const QString &text, OutputFormat format) override + { + if (text.startsWith("B")) { + appendMessageDefault("handled by B\n", format); + return Status::Done; + } + return Status::NotHandled; + } +}; + +void Internal::CorePlugin::testOutputFormatter() +{ + const QString input = + "B to be handled by B\r\n" + "not to be handled\n" + "A to be handled by A\n" + "continuation for A\r\n" + "B looks like B, but still continuation for A\r\n" + "A end of A\n" + "A next A\n" + "A end of next A\n" + " A trick\r\n" + "B to be handled by B\n"; + const QString output = + "handled by B\n" + "not to be handled\n" + "handled by A\n" + "handled by A\n" + "handled by A\n" + "handled by A\n" + "handled by A\n" + "handled by A\n" + " A trick\n" + "handled by B\n"; + TestFormatterA formatterA; + TestFormatterB formatterB; + AggregatingOutputFormatter formatter; + QPlainTextEdit textEdit; + formatter.setPlainTextEdit(&textEdit); + formatter.setFormatters({&formatterB, &formatterA}); + + // Stress-test the implementation by providing the input in chunks, splitting at all possible + // offsets. + for (int i = 0; i < input.length(); ++i) { + formatter.appendMessage(input.left(i), NormalMessageFormat); + formatter.appendMessage(input.mid(i), NormalMessageFormat); + QCOMPARE(textEdit.toPlainText(), output); + formatter.clear(); + } +} +#endif // WITH_TESTS } // namespace Core diff --git a/src/plugins/coreplugin/outputwindow.h b/src/plugins/coreplugin/outputwindow.h index a90e9ced66..77703f0b9a 100644 --- a/src/plugins/coreplugin/outputwindow.h +++ b/src/plugins/coreplugin/outputwindow.h @@ -56,12 +56,13 @@ public: OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr); ~OutputWindow() override; - void setFormatter(Utils::OutputFormatter *formatter); + void setFormatters(const QList<Utils::OutputFormatter *> &formatters); void appendMessage(const QString &out, Utils::OutputFormat format); void grayOutOldContent(); void clear(); + void flush(); void scrollToBottom(); diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index d9261efa5e..cb24b7c1c2 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -154,7 +154,7 @@ AppOutputPane::RunControlTab::RunControlTab(RunControl *runControl, Core::Output runControl(runControl), window(w) { if (runControl && w) - w->setFormatter(runControl->outputFormatter()); + w->setFormatters(runControl->outputFormatters()); } AppOutputPane::AppOutputPane() : @@ -404,7 +404,7 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) if (tab.runControl) tab.runControl->initiateFinish(); tab.runControl = rc; - tab.window->setFormatter(rc->outputFormatter()); + tab.window->setFormatters(rc->outputFormatters()); handleOldOutput(tab.window); @@ -743,8 +743,12 @@ void AppOutputPane::slotRunControlFinished() { auto *rc = qobject_cast<RunControl *>(sender()); QTimer::singleShot(0, this, [this, rc]() { slotRunControlFinished2(rc); }); - if (rc->outputFormatter()) - rc->outputFormatter()->flush(); + for (const RunControlTab &t : m_runControlTabs) { + if (t.runControl == rc) { + t.window->flush(); + break; + } + } } void AppOutputPane::slotRunControlFinished2(RunControl *sender) diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index 3eeedee281..f259d1f766 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -135,7 +135,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_outputWindow->setReadOnly(true); m_outputWindow->setUndoRedoEnabled(false); m_outputWindow->setMaxCharCount(Core::Constants::DEFAULT_MAX_CHAR_COUNT); - m_outputWindow->setFormatter(m_formatter); + m_outputWindow->setFormatters({m_formatter}); // Let selected text be colored as if the text edit was editable, // otherwise the highlight for searching is too light diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 37bf2f3c49..18cf5a3099 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -279,7 +279,6 @@ public: : q(parent), runMode(mode) { icon = Icons::RUN_SMALL_TOOLBAR; - outputFormatter = new OutputFormatter(); } ~RunControlPrivate() override @@ -289,7 +288,7 @@ public: q = nullptr; qDeleteAll(m_workers); m_workers.clear(); - delete outputFormatter; + qDeleteAll(outputFormatters); } Q_ENUM(RunControlState) @@ -334,7 +333,7 @@ public: Kit *kit = nullptr; // Not owned. QPointer<Target> target; // Not owned. QPointer<Project> project; // Not owned. - QPointer<Utils::OutputFormatter> outputFormatter = nullptr; + QList<Utils::OutputFormatter *> outputFormatters; std::function<bool(bool*)> promptToStop; std::vector<RunWorkerFactory> m_factories; @@ -385,10 +384,8 @@ void RunControl::setTarget(Target *target) d->buildEnvironment = bc->environment(); } - delete d->outputFormatter; - d->outputFormatter = OutputFormatterFactory::createFormatter(target); - if (!d->outputFormatter) - d->outputFormatter = new OutputFormatter(); + QTC_CHECK(d->outputFormatters.isEmpty()); + d->outputFormatters = OutputFormatterFactory::createFormatters(target); setKit(target->kit()); d->project = target->project(); @@ -831,9 +828,9 @@ void RunControlPrivate::showError(const QString &msg) q->appendMessage(msg + '\n', ErrorMessageFormat); } -Utils::OutputFormatter *RunControl::outputFormatter() const +QList<Utils::OutputFormatter *> RunControl::outputFormatters() const { - return d->outputFormatter; + return d->outputFormatters; } Core::Id RunControl::runMode() const @@ -1601,11 +1598,7 @@ static QList<OutputFormatterFactory *> g_outputFormatterFactories; OutputFormatterFactory::OutputFormatterFactory() { - // This is a bit cheating: We know that only two formatters exist right now, - // and this here gives the second (python) implicit more priority. - // For a final solution, probably all matching formatters should be used - // in parallel, so there's no need to invent a fancy priority system here. - g_outputFormatterFactories.prepend(this); + g_outputFormatterFactories.append(this); } OutputFormatterFactory::~OutputFormatterFactory() @@ -1613,13 +1606,14 @@ OutputFormatterFactory::~OutputFormatterFactory() g_outputFormatterFactories.removeOne(this); } -OutputFormatter *OutputFormatterFactory::createFormatter(Target *target) +QList<OutputFormatter *> OutputFormatterFactory::createFormatters(Target *target) { + QList<OutputFormatter *> formatters; for (auto factory : qAsConst(g_outputFormatterFactories)) { if (auto formatter = factory->m_creator(target)) - return formatter; + formatters << formatter; } - return nullptr; + return formatters; } void OutputFormatterFactory::setFormatterCreator diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index cda6d6ab55..99c6a7636c 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -238,7 +238,7 @@ public: Utils::FilePath targetFilePath() const; Utils::FilePath projectFilePath() const; - Utils::OutputFormatter *outputFormatter() const; + QList<Utils::OutputFormatter *> outputFormatters() const; Core::Id runMode() const; const Runnable &runnable() const; @@ -309,7 +309,7 @@ protected: public: virtual ~OutputFormatterFactory(); - static Utils::OutputFormatter *createFormatter(Target *target); + static QList<Utils::OutputFormatter *> createFormatters(Target *target); protected: void setFormatterCreator(const std::function<Utils::OutputFormatter *(Target *)> &creator); diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index 5ea0bf1f0f..a01d08e39a 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -71,12 +71,16 @@ public: } private: - void doAppendMessage(const QString &text, OutputFormat format) final + Status handleMessage(const QString &text, OutputFormat format) final { if (!m_inTraceBack) { m_inTraceBack = format == StdErrFormat && text.startsWith("Traceback (most recent call last):"); - OutputFormatter::doAppendMessage(text, format); + if (m_inTraceBack) { + OutputFormatter::appendMessageDefault(text, format); + return Status::InProgress; + } + return Status::NotHandled; } const Core::Id category(PythonErrorTaskCategory); @@ -90,9 +94,10 @@ private: const auto fileName = FilePath::fromString(match.captured(3)); const int lineNumber = match.capturedRef(4).toInt(); m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category}); - return; + return Status::InProgress; } + Status status = Status::InProgress; if (text.startsWith(' ')) { // Neither traceback start, nor file, nor error message line. // Not sure if that can actually happen. @@ -111,18 +116,21 @@ private: TaskHub::addTask(*rit); m_tasks.clear(); m_inTraceBack = false; + status = Status::Done; } - OutputFormatter::doAppendMessage(text, format); + OutputFormatter::appendMessageDefault(text, format); + return status; } - void handleLink(const QString &href) final + bool handleLink(const QString &href) final { const QRegularExpressionMatch match = filePattern.match(href); if (!match.hasMatch()) - return; + return false; const QString fileName = match.captured(3); const int lineNumber = match.capturedRef(4).toInt(); Core::EditorManager::openEditorAt(fileName, lineNumber); + return true; } void reset() override diff --git a/src/plugins/qtsupport/qtoutputformatter.cpp b/src/plugins/qtsupport/qtoutputformatter.cpp index fdaef6004b..76cb10422b 100644 --- a/src/plugins/qtsupport/qtoutputformatter.cpp +++ b/src/plugins/qtsupport/qtoutputformatter.cpp @@ -45,6 +45,8 @@ #include <QTextCursor> #include <QUrl> +#include <tuple> + using namespace ProjectExplorer; using namespace Utils; @@ -95,14 +97,13 @@ protected: virtual void openEditor(const QString &fileName, int line, int column = -1); private: - void doAppendMessage(const QString &text, Utils::OutputFormat format) override; - void handleLink(const QString &href) override; + Status handleMessage(const QString &text, Utils::OutputFormat format) override; + bool handleLink(const QString &href) override; void updateProjectFileList(); LinkResult matchLine(const QString &line) const; - void appendMessagePart(const QString &txt, const QTextCharFormat &fmt); void appendLine(const LinkResult &lr, const QString &line, const QTextCharFormat &format); - void doAppendMessage(const QString &txt, const QTextCharFormat &format); + Status doAppendMessage(const QString &txt, const QTextCharFormat &format); QtOutputFormatterPrivate *d; friend class QtSupportPlugin; // for testing @@ -160,25 +161,39 @@ LinkResult QtOutputFormatter::matchLine(const QString &line) const return lr; } -void QtOutputFormatter::doAppendMessage(const QString &txt, const QTextCharFormat &format) +OutputFormatter::Status QtOutputFormatter::doAppendMessage(const QString &txt, + const QTextCharFormat &format) { + // FIXME: We'll do the ANSI parsing twice if there is no match. + // Ideally, we'd (optionally) pre-process ANSI escape codes in the + // base class before passing the text here, but then we can no longer + // pass complete lines... const QList<FormattedText> ansiTextList = parseAnsi(txt, format); - for (const FormattedText &output : ansiTextList) - appendMessagePart(output.text, output.format); -} - -void QtOutputFormatter::doAppendMessage(const QString &txt, OutputFormat format) -{ - doAppendMessage(txt, charFormat(format)); + QList<std::tuple<QString, QTextCharFormat, LinkResult>> parts; + bool hasMatches = false; + for (const FormattedText &output : ansiTextList) { + const LinkResult lr = matchLine(output.text); + if (!lr.href.isEmpty()) + hasMatches = true; + parts << std::make_tuple(output.text, output.format, lr); + } + if (!hasMatches) + return Status::NotHandled; + for (const auto &part : parts) { + const LinkResult &lr = std::get<2>(part); + const QString &text = std::get<0>(part); + const QTextCharFormat &fmt = std::get<1>(part); + if (!lr.href.isEmpty()) + appendLine(lr, text, fmt); + else + cursor().insertText(text, fmt); + } + return Status::Done; } -void QtOutputFormatter::appendMessagePart(const QString &txt, const QTextCharFormat &fmt) +QtOutputFormatter::Status QtOutputFormatter::handleMessage(const QString &txt, OutputFormat format) { - const LinkResult lr = matchLine(txt); - if (!lr.href.isEmpty()) - appendLine(lr, txt, fmt); - else - cursor().insertText(txt, fmt); + return doAppendMessage(txt, charFormat(format)); } void QtOutputFormatter::appendLine(const LinkResult &lr, const QString &line, @@ -189,7 +204,7 @@ void QtOutputFormatter::appendLine(const LinkResult &lr, const QString &line, cursor().insertText(line.mid(lr.end), format); } -void QtOutputFormatter::handleLink(const QString &href) +bool QtOutputFormatter::handleLink(const QString &href) { if (!href.isEmpty()) { static const QRegularExpression qmlLineColumnLink("^(" QT_QML_URL_REGEXP ")" // url @@ -205,7 +220,7 @@ void QtOutputFormatter::handleLink(const QString &href) const int line = qmlLineColumnMatch.captured(2).toInt(); const int column = qmlLineColumnMatch.captured(3).toInt(); openEditor(getFileToOpen(fileUrl), line, column - 1); - return; + return true; } static const QRegularExpression qmlLineLink("^(" QT_QML_URL_REGEXP ")" // url @@ -220,7 +235,7 @@ void QtOutputFormatter::handleLink(const QString &href) fileUrl = QUrl::fromLocalFile(filePath.mid(int(strlen(scheme)))); const int line = qmlLineMatch.captured(2).toInt(); openEditor(getFileToOpen(fileUrl), line); - return; + return true; } QString fileName; @@ -250,9 +265,10 @@ void QtOutputFormatter::handleLink(const QString &href) if (!fileName.isEmpty()) { fileName = getFileToOpen(QUrl::fromLocalFile(fileName)); openEditor(fileName, line); - return; + return true; } } + return false; } void QtOutputFormatter::openEditor(const QString &fileName, int line, int column) @@ -504,8 +520,13 @@ void QtSupportPlugin::testQtOutputFormatter_appendMessage() QFETCH(QString, outputText); QFETCH(QTextCharFormat, inputFormat); QFETCH(QTextCharFormat, outputFormat); + if (outputFormat == QTextCharFormat()) + outputFormat = formatter.charFormat(DebugFormat); + if (inputFormat != QTextCharFormat()) + formatter.overrideTextCharFormat(inputFormat); - formatter.doAppendMessage(inputText, inputFormat); + formatter.appendMessage(inputText, DebugFormat); + formatter.flush(); QCOMPARE(edit.toPlainText(), outputText); QCOMPARE(edit.currentCharFormat(), outputFormat); diff --git a/src/plugins/serialterminal/serialoutputpane.cpp b/src/plugins/serialterminal/serialoutputpane.cpp index e9dfb50ce7..eab36c46be 100644 --- a/src/plugins/serialterminal/serialoutputpane.cpp +++ b/src/plugins/serialterminal/serialoutputpane.cpp @@ -315,7 +315,7 @@ void SerialOutputPane::createNewOutputWindow(SerialControl *rc) this, fontSettingsChanged); fontSettingsChanged(); ow->setWindowTitle(tr("Serial Terminal Window")); - ow->setFormatter(formatter); + ow->setFormatters({formatter}); // TODO: wordwrap, maxLineCount, zoom/wheelZoom (add to settings) auto controlTab = SerialControlTab(rc, ow); diff --git a/src/plugins/vcsbase/vcsoutputformatter.cpp b/src/plugins/vcsbase/vcsoutputformatter.cpp index 4915dda403..4145bd0618 100644 --- a/src/plugins/vcsbase/vcsoutputformatter.cpp +++ b/src/plugins/vcsbase/vcsoutputformatter.cpp @@ -42,14 +42,17 @@ VcsOutputFormatter::VcsOutputFormatter() : { } -void VcsOutputFormatter::doAppendMessage(const QString &text, Utils::OutputFormat format) +VcsOutputFormatter::Status VcsOutputFormatter::handleMessage(const QString &text, + Utils::OutputFormat format) { QRegularExpressionMatchIterator it = m_regexp.globalMatch(text); + if (!it.hasNext()) + return Status::NotHandled; int begin = 0; while (it.hasNext()) { const QRegularExpressionMatch match = it.next(); const QTextCharFormat normalFormat = charFormat(format); - OutputFormatter::doAppendMessage(text.mid(begin, match.capturedStart() - begin), format); + appendMessageDefault(text.mid(begin, match.capturedStart() - begin), format); QTextCursor tc = plainTextEdit()->textCursor(); QStringView url = match.capturedView(); begin = match.capturedEnd(); @@ -61,15 +64,17 @@ void VcsOutputFormatter::doAppendMessage(const QString &text, Utils::OutputForma tc.insertText(url.toString(), linkFormat(normalFormat, url.toString())); tc.movePosition(QTextCursor::End); } - OutputFormatter::doAppendMessage(text.mid(begin), format); + appendMessageDefault(text.mid(begin), format); + return Status::Done; } -void VcsOutputFormatter::handleLink(const QString &href) +bool VcsOutputFormatter::handleLink(const QString &href) { if (href.startsWith("http://") || href.startsWith("https://")) QDesktopServices::openUrl(QUrl(href)); else if (!href.isEmpty()) emit referenceClicked(href); + return true; } void VcsOutputFormatter::fillLinkContextMenu( diff --git a/src/plugins/vcsbase/vcsoutputformatter.h b/src/plugins/vcsbase/vcsoutputformatter.h index fe1bae89fd..5074fc3392 100644 --- a/src/plugins/vcsbase/vcsoutputformatter.h +++ b/src/plugins/vcsbase/vcsoutputformatter.h @@ -37,14 +37,15 @@ class VcsOutputFormatter : public Utils::OutputFormatter public: VcsOutputFormatter(); ~VcsOutputFormatter() override = default; - void doAppendMessage(const QString &text, Utils::OutputFormat format) override; - void handleLink(const QString &href) override; + bool handleLink(const QString &href) override; void fillLinkContextMenu(QMenu *menu, const QString &workingDirectory, const QString &href); signals: void referenceClicked(const QString &reference); private: + Status handleMessage(const QString &text, Utils::OutputFormat format) override; + const QRegularExpression m_regexp; }; diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index 2391a1bb03..f1ca4da868 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsoutputwindow.cpp @@ -123,7 +123,7 @@ OutputWindowPlainTextEdit::OutputWindowPlainTextEdit(QWidget *parent) : setFrameStyle(QFrame::NoFrame); m_formatter = new VcsOutputFormatter; m_formatter->setBoldFontEnabled(false); - setFormatter(m_formatter); + setFormatters({m_formatter}); auto agg = new Aggregation::Aggregate; agg->add(this); agg->add(new Core::BaseTextFind(this)); |