forked from qt-creator/qt-creator
Remove the limitation that output formatters have to be exclusive
Introduce an aggregating output formatter that forwards its input to a sub-formatter that feels responsible for it, or otherwise lets the base class handle it. Our output panes now use such an aggregating formatter. In particular, this means that in the future, we won't have to stuff all run control output formatting into the Qt output formatter anymore. Change-Id: I5498f200a61db10ccff3ec8974c6825da7f7072d Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -25,8 +25,10 @@
|
|||||||
|
|
||||||
#include "ansiescapecodehandler.h"
|
#include "ansiescapecodehandler.h"
|
||||||
#include "outputformatter.h"
|
#include "outputformatter.h"
|
||||||
|
#include "qtcassert.h"
|
||||||
#include "synchronousprocess.h"
|
#include "synchronousprocess.h"
|
||||||
#include "theme/theme.h"
|
#include "theme/theme.h"
|
||||||
|
#include "utils/optional.h"
|
||||||
|
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
@@ -44,6 +46,7 @@ public:
|
|||||||
QTextCursor cursor;
|
QTextCursor cursor;
|
||||||
AnsiEscapeCodeHandler escapeCodeHandler;
|
AnsiEscapeCodeHandler escapeCodeHandler;
|
||||||
QPair<QString, OutputFormat> incompleteLine;
|
QPair<QString, OutputFormat> incompleteLine;
|
||||||
|
optional<QTextCharFormat> formatOverride;
|
||||||
bool boldFontEnabled = true;
|
bool boldFontEnabled = true;
|
||||||
bool prependCarriageReturn = false;
|
bool prependCarriageReturn = false;
|
||||||
};
|
};
|
||||||
@@ -75,9 +78,15 @@ void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText)
|
|||||||
|
|
||||||
void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
|
void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
|
||||||
{
|
{
|
||||||
if (!d->cursor.atEnd() && text.startsWith('\n'))
|
if (handleMessage(text, format) == Status::NotHandled)
|
||||||
d->cursor.movePosition(QTextCursor::End);
|
appendMessageDefault(text, format);
|
||||||
doAppendMessage(text, d->formats[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)
|
void OutputFormatter::doAppendMessage(const QString &text, const QTextCharFormat &format)
|
||||||
@@ -118,6 +127,16 @@ QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat,
|
|||||||
return result;
|
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()
|
void OutputFormatter::clearLastLine()
|
||||||
{
|
{
|
||||||
// Note that this approach will fail if the text edit is not read-only and users
|
// 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;
|
d->incompleteLine.second = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputFormatter::handleLink(const QString &href)
|
bool OutputFormatter::handleLink(const QString &href)
|
||||||
{
|
{
|
||||||
Q_UNUSED(href)
|
Q_UNUSED(href)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputFormatter::clear()
|
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
|
} // namespace Utils
|
||||||
|
|||||||
@@ -42,8 +42,11 @@ class FormattedText;
|
|||||||
|
|
||||||
namespace Internal { class OutputFormatterPrivate; }
|
namespace Internal { class OutputFormatterPrivate; }
|
||||||
|
|
||||||
|
class QTCREATOR_UTILS_EXPORT AggregatingOutputFormatter;
|
||||||
|
|
||||||
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
|
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
|
||||||
{
|
{
|
||||||
|
friend class AggregatingOutputFormatter;
|
||||||
public:
|
public:
|
||||||
OutputFormatter();
|
OutputFormatter();
|
||||||
~OutputFormatter() override;
|
~OutputFormatter() override;
|
||||||
@@ -55,26 +58,32 @@ public:
|
|||||||
|
|
||||||
void appendMessage(const QString &text, OutputFormat format);
|
void appendMessage(const QString &text, OutputFormat format);
|
||||||
|
|
||||||
virtual void handleLink(const QString &href);
|
virtual bool handleLink(const QString &href);
|
||||||
void clear();
|
void clear();
|
||||||
void setBoldFontEnabled(bool enabled);
|
void setBoldFontEnabled(bool enabled);
|
||||||
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href);
|
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href);
|
||||||
|
|
||||||
|
// For unit testing only
|
||||||
|
void overrideTextCharFormat(const QTextCharFormat &fmt);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// text contains at most one line feed character, and if it does occur, it's the last character.
|
enum class Status { Done, InProgress, NotHandled };
|
||||||
// Either way, the input is to be considered "complete" for formatting purposes.
|
|
||||||
virtual void doAppendMessage(const QString &text, OutputFormat format);
|
|
||||||
|
|
||||||
virtual void clearLastLine();
|
|
||||||
|
|
||||||
|
void appendMessageDefault(const QString &text, OutputFormat format);
|
||||||
|
void clearLastLine();
|
||||||
QTextCharFormat charFormat(OutputFormat format) const;
|
QTextCharFormat charFormat(OutputFormat format) const;
|
||||||
QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format);
|
QList<FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format);
|
||||||
QTextCursor &cursor() const;
|
QTextCursor &cursor() const;
|
||||||
|
|
||||||
private:
|
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() {}
|
virtual void reset() {}
|
||||||
|
|
||||||
|
void doAppendMessage(const QString &text, const QTextCharFormat &format);
|
||||||
void append(const QString &text, const QTextCharFormat &format);
|
void append(const QString &text, const QTextCharFormat &format);
|
||||||
void initFormats();
|
void initFormats();
|
||||||
void flushIncompleteLine();
|
void flushIncompleteLine();
|
||||||
@@ -83,4 +92,20 @@ private:
|
|||||||
Internal::OutputFormatterPrivate *d;
|
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
|
} // namespace Utils
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ private slots:
|
|||||||
// Locator:
|
// Locator:
|
||||||
void test_basefilefilter();
|
void test_basefilefilter();
|
||||||
void test_basefilefilter_data();
|
void test_basefilefilter_data();
|
||||||
|
|
||||||
|
void testOutputFormatter();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "actionmanager/actionmanager.h"
|
#include "actionmanager/actionmanager.h"
|
||||||
#include "coreconstants.h"
|
#include "coreconstants.h"
|
||||||
|
#include "coreplugin.h"
|
||||||
#include "icore.h"
|
#include "icore.h"
|
||||||
|
|
||||||
#include <utils/outputformatter.h>
|
#include <utils/outputformatter.h>
|
||||||
@@ -39,6 +40,10 @@
|
|||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include <QtTest>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -60,9 +65,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
IContext *outputWindowContext = nullptr;
|
IContext *outputWindowContext = nullptr;
|
||||||
QPointer<Utils::OutputFormatter> formatter;
|
|
||||||
QString settingsKey;
|
QString settingsKey;
|
||||||
OutputFormatter defaultFormatter;
|
AggregatingOutputFormatter formatter;
|
||||||
|
|
||||||
bool scrollToBottom = true;
|
bool scrollToBottom = true;
|
||||||
bool linksActive = true;
|
bool linksActive = true;
|
||||||
@@ -91,7 +95,7 @@ OutputWindow::OutputWindow(Context context, const QString &settingsKey, QWidget
|
|||||||
setFrameShape(QFrame::NoFrame);
|
setFrameShape(QFrame::NoFrame);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
setUndoRedoEnabled(false);
|
setUndoRedoEnabled(false);
|
||||||
setFormatter(&d->defaultFormatter);
|
d->formatter.setPlainTextEdit(this);
|
||||||
|
|
||||||
d->settingsKey = settingsKey;
|
d->settingsKey = settingsKey;
|
||||||
|
|
||||||
@@ -168,7 +172,7 @@ void OutputWindow::mouseReleaseEvent(QMouseEvent *e)
|
|||||||
{
|
{
|
||||||
if (d->linksActive && d->mouseButtonPressed == Qt::LeftButton) {
|
if (d->linksActive && d->mouseButtonPressed == Qt::LeftButton) {
|
||||||
const QString href = anchorAt(e->pos());
|
const QString href = anchorAt(e->pos());
|
||||||
d->formatter->handleLink(href);
|
d->formatter.handleLink(href);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse was released, activate links again
|
// Mouse was released, activate links again
|
||||||
@@ -212,13 +216,9 @@ void OutputWindow::keyPressEvent(QKeyEvent *ev)
|
|||||||
verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
|
verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputWindow::setFormatter(OutputFormatter *formatter)
|
void OutputWindow::setFormatters(const QList<OutputFormatter *> &formatters)
|
||||||
{
|
{
|
||||||
d->formatter = formatter;
|
d->formatter.setFormatters(formatters);
|
||||||
if (d->formatter)
|
|
||||||
d->formatter->setPlainTextEdit(this);
|
|
||||||
else
|
|
||||||
d->formatter = &d->defaultFormatter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputWindow::showEvent(QShowEvent *e)
|
void OutputWindow::showEvent(QShowEvent *e)
|
||||||
@@ -396,7 +396,7 @@ void OutputWindow::appendMessage(const QString &output, OutputFormat format)
|
|||||||
|
|
||||||
const bool atBottom = isScrollbarAtBottom() || m_scrollTimer.isActive();
|
const bool atBottom = isScrollbarAtBottom() || m_scrollTimer.isActive();
|
||||||
d->scrollToBottom = true;
|
d->scrollToBottom = true;
|
||||||
d->formatter->appendMessage(out, format);
|
d->formatter.appendMessage(out, format);
|
||||||
|
|
||||||
if (atBottom) {
|
if (atBottom) {
|
||||||
if (m_lastMessage.elapsed() < 5) {
|
if (m_lastMessage.elapsed() < 5) {
|
||||||
@@ -445,7 +445,12 @@ QMimeData *OutputWindow::createMimeDataFromSelection() const
|
|||||||
|
|
||||||
void OutputWindow::clear()
|
void OutputWindow::clear()
|
||||||
{
|
{
|
||||||
d->formatter->clear();
|
d->formatter.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputWindow::flush()
|
||||||
|
{
|
||||||
|
d->formatter.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputWindow::scrollToBottom()
|
void OutputWindow::scrollToBottom()
|
||||||
@@ -494,4 +499,89 @@ void OutputWindow::setWordWrapEnabled(bool wrap)
|
|||||||
setWordWrapMode(QTextOption::NoWrap);
|
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
|
} // namespace Core
|
||||||
|
|||||||
@@ -56,12 +56,13 @@ public:
|
|||||||
OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr);
|
OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr);
|
||||||
~OutputWindow() override;
|
~OutputWindow() override;
|
||||||
|
|
||||||
void setFormatter(Utils::OutputFormatter *formatter);
|
void setFormatters(const QList<Utils::OutputFormatter *> &formatters);
|
||||||
|
|
||||||
void appendMessage(const QString &out, Utils::OutputFormat format);
|
void appendMessage(const QString &out, Utils::OutputFormat format);
|
||||||
|
|
||||||
void grayOutOldContent();
|
void grayOutOldContent();
|
||||||
void clear();
|
void clear();
|
||||||
|
void flush();
|
||||||
|
|
||||||
void scrollToBottom();
|
void scrollToBottom();
|
||||||
|
|
||||||
|
|||||||
@@ -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->setFormatter(runControl->outputFormatter());
|
w->setFormatters(runControl->outputFormatters());
|
||||||
}
|
}
|
||||||
|
|
||||||
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->setFormatter(rc->outputFormatter());
|
tab.window->setFormatters(rc->outputFormatters());
|
||||||
|
|
||||||
handleOldOutput(tab.window);
|
handleOldOutput(tab.window);
|
||||||
|
|
||||||
@@ -743,8 +743,12 @@ void AppOutputPane::slotRunControlFinished()
|
|||||||
{
|
{
|
||||||
auto *rc = qobject_cast<RunControl *>(sender());
|
auto *rc = qobject_cast<RunControl *>(sender());
|
||||||
QTimer::singleShot(0, this, [this, rc]() { slotRunControlFinished2(rc); });
|
QTimer::singleShot(0, this, [this, rc]() { slotRunControlFinished2(rc); });
|
||||||
if (rc->outputFormatter())
|
for (const RunControlTab &t : m_runControlTabs) {
|
||||||
rc->outputFormatter()->flush();
|
if (t.runControl == rc) {
|
||||||
|
t.window->flush();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppOutputPane::slotRunControlFinished2(RunControl *sender)
|
void AppOutputPane::slotRunControlFinished2(RunControl *sender)
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) :
|
|||||||
m_outputWindow->setReadOnly(true);
|
m_outputWindow->setReadOnly(true);
|
||||||
m_outputWindow->setUndoRedoEnabled(false);
|
m_outputWindow->setUndoRedoEnabled(false);
|
||||||
m_outputWindow->setMaxCharCount(Core::Constants::DEFAULT_MAX_CHAR_COUNT);
|
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,
|
// Let selected text be colored as if the text edit was editable,
|
||||||
// otherwise the highlight for searching is too light
|
// otherwise the highlight for searching is too light
|
||||||
|
|||||||
@@ -279,7 +279,6 @@ public:
|
|||||||
: q(parent), runMode(mode)
|
: q(parent), runMode(mode)
|
||||||
{
|
{
|
||||||
icon = Icons::RUN_SMALL_TOOLBAR;
|
icon = Icons::RUN_SMALL_TOOLBAR;
|
||||||
outputFormatter = new OutputFormatter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~RunControlPrivate() override
|
~RunControlPrivate() override
|
||||||
@@ -289,7 +288,7 @@ public:
|
|||||||
q = nullptr;
|
q = nullptr;
|
||||||
qDeleteAll(m_workers);
|
qDeleteAll(m_workers);
|
||||||
m_workers.clear();
|
m_workers.clear();
|
||||||
delete outputFormatter;
|
qDeleteAll(outputFormatters);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ENUM(RunControlState)
|
Q_ENUM(RunControlState)
|
||||||
@@ -334,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.
|
||||||
QPointer<Utils::OutputFormatter> outputFormatter = nullptr;
|
QList<Utils::OutputFormatter *> outputFormatters;
|
||||||
std::function<bool(bool*)> promptToStop;
|
std::function<bool(bool*)> promptToStop;
|
||||||
std::vector<RunWorkerFactory> m_factories;
|
std::vector<RunWorkerFactory> m_factories;
|
||||||
|
|
||||||
@@ -385,10 +384,8 @@ void RunControl::setTarget(Target *target)
|
|||||||
d->buildEnvironment = bc->environment();
|
d->buildEnvironment = bc->environment();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete d->outputFormatter;
|
QTC_CHECK(d->outputFormatters.isEmpty());
|
||||||
d->outputFormatter = OutputFormatterFactory::createFormatter(target);
|
d->outputFormatters = OutputFormatterFactory::createFormatters(target);
|
||||||
if (!d->outputFormatter)
|
|
||||||
d->outputFormatter = new OutputFormatter();
|
|
||||||
|
|
||||||
setKit(target->kit());
|
setKit(target->kit());
|
||||||
d->project = target->project();
|
d->project = target->project();
|
||||||
@@ -831,9 +828,9 @@ void RunControlPrivate::showError(const QString &msg)
|
|||||||
q->appendMessage(msg + '\n', ErrorMessageFormat);
|
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
|
Core::Id RunControl::runMode() const
|
||||||
@@ -1601,11 +1598,7 @@ static QList<OutputFormatterFactory *> g_outputFormatterFactories;
|
|||||||
|
|
||||||
OutputFormatterFactory::OutputFormatterFactory()
|
OutputFormatterFactory::OutputFormatterFactory()
|
||||||
{
|
{
|
||||||
// This is a bit cheating: We know that only two formatters exist right now,
|
g_outputFormatterFactories.append(this);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputFormatterFactory::~OutputFormatterFactory()
|
OutputFormatterFactory::~OutputFormatterFactory()
|
||||||
@@ -1613,13 +1606,14 @@ OutputFormatterFactory::~OutputFormatterFactory()
|
|||||||
g_outputFormatterFactories.removeOne(this);
|
g_outputFormatterFactories.removeOne(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputFormatter *OutputFormatterFactory::createFormatter(Target *target)
|
QList<OutputFormatter *> OutputFormatterFactory::createFormatters(Target *target)
|
||||||
{
|
{
|
||||||
|
QList<OutputFormatter *> 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))
|
||||||
return formatter;
|
formatters << formatter;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return formatters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputFormatterFactory::setFormatterCreator
|
void OutputFormatterFactory::setFormatterCreator
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ public:
|
|||||||
Utils::FilePath targetFilePath() const;
|
Utils::FilePath targetFilePath() const;
|
||||||
Utils::FilePath projectFilePath() const;
|
Utils::FilePath projectFilePath() const;
|
||||||
|
|
||||||
Utils::OutputFormatter *outputFormatter() const;
|
QList<Utils::OutputFormatter *> outputFormatters() const;
|
||||||
Core::Id runMode() const;
|
Core::Id runMode() const;
|
||||||
|
|
||||||
const Runnable &runnable() const;
|
const Runnable &runnable() const;
|
||||||
@@ -309,7 +309,7 @@ protected:
|
|||||||
public:
|
public:
|
||||||
virtual ~OutputFormatterFactory();
|
virtual ~OutputFormatterFactory();
|
||||||
|
|
||||||
static Utils::OutputFormatter *createFormatter(Target *target);
|
static QList<Utils::OutputFormatter *> createFormatters(Target *target);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setFormatterCreator(const std::function<Utils::OutputFormatter *(Target *)> &creator);
|
void setFormatterCreator(const std::function<Utils::OutputFormatter *(Target *)> &creator);
|
||||||
|
|||||||
@@ -71,12 +71,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doAppendMessage(const QString &text, OutputFormat format) final
|
Status handleMessage(const QString &text, OutputFormat format) final
|
||||||
{
|
{
|
||||||
if (!m_inTraceBack) {
|
if (!m_inTraceBack) {
|
||||||
m_inTraceBack = format == StdErrFormat
|
m_inTraceBack = format == StdErrFormat
|
||||||
&& text.startsWith("Traceback (most recent call last):");
|
&& 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);
|
const Core::Id category(PythonErrorTaskCategory);
|
||||||
@@ -90,9 +94,10 @@ private:
|
|||||||
const auto fileName = FilePath::fromString(match.captured(3));
|
const auto fileName = FilePath::fromString(match.captured(3));
|
||||||
const int lineNumber = match.capturedRef(4).toInt();
|
const int lineNumber = match.capturedRef(4).toInt();
|
||||||
m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category});
|
m_tasks.append({Task::Warning, QString(), fileName, lineNumber, category});
|
||||||
return;
|
return Status::InProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status status = Status::InProgress;
|
||||||
if (text.startsWith(' ')) {
|
if (text.startsWith(' ')) {
|
||||||
// Neither traceback start, nor file, nor error message line.
|
// Neither traceback start, nor file, nor error message line.
|
||||||
// Not sure if that can actually happen.
|
// Not sure if that can actually happen.
|
||||||
@@ -111,18 +116,21 @@ private:
|
|||||||
TaskHub::addTask(*rit);
|
TaskHub::addTask(*rit);
|
||||||
m_tasks.clear();
|
m_tasks.clear();
|
||||||
m_inTraceBack = false;
|
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);
|
const QRegularExpressionMatch match = filePattern.match(href);
|
||||||
if (!match.hasMatch())
|
if (!match.hasMatch())
|
||||||
return;
|
return false;
|
||||||
const QString fileName = match.captured(3);
|
const QString fileName = match.captured(3);
|
||||||
const int lineNumber = match.capturedRef(4).toInt();
|
const int lineNumber = match.capturedRef(4).toInt();
|
||||||
Core::EditorManager::openEditorAt(fileName, lineNumber);
|
Core::EditorManager::openEditorAt(fileName, lineNumber);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() override
|
void reset() override
|
||||||
|
|||||||
@@ -45,6 +45,8 @@
|
|||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -95,14 +97,13 @@ 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:
|
||||||
void doAppendMessage(const QString &text, Utils::OutputFormat format) override;
|
Status handleMessage(const QString &text, Utils::OutputFormat format) override;
|
||||||
void handleLink(const QString &href) override;
|
bool handleLink(const QString &href) override;
|
||||||
|
|
||||||
void updateProjectFileList();
|
void updateProjectFileList();
|
||||||
LinkResult matchLine(const QString &line) const;
|
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 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;
|
QtOutputFormatterPrivate *d;
|
||||||
friend class QtSupportPlugin; // for testing
|
friend class QtSupportPlugin; // for testing
|
||||||
@@ -160,25 +161,39 @@ LinkResult QtOutputFormatter::matchLine(const QString &line) const
|
|||||||
return lr;
|
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);
|
const QList<FormattedText> ansiTextList = parseAnsi(txt, format);
|
||||||
for (const FormattedText &output : ansiTextList)
|
QList<std::tuple<QString, QTextCharFormat, LinkResult>> parts;
|
||||||
appendMessagePart(output.text, output.format);
|
bool hasMatches = false;
|
||||||
}
|
for (const FormattedText &output : ansiTextList) {
|
||||||
|
const LinkResult lr = matchLine(output.text);
|
||||||
void QtOutputFormatter::doAppendMessage(const QString &txt, OutputFormat format)
|
|
||||||
{
|
|
||||||
doAppendMessage(txt, charFormat(format));
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtOutputFormatter::appendMessagePart(const QString &txt, const QTextCharFormat &fmt)
|
|
||||||
{
|
|
||||||
const LinkResult lr = matchLine(txt);
|
|
||||||
if (!lr.href.isEmpty())
|
if (!lr.href.isEmpty())
|
||||||
appendLine(lr, txt, fmt);
|
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
|
else
|
||||||
cursor().insertText(txt, fmt);
|
cursor().insertText(text, fmt);
|
||||||
|
}
|
||||||
|
return Status::Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
QtOutputFormatter::Status QtOutputFormatter::handleMessage(const QString &txt, OutputFormat format)
|
||||||
|
{
|
||||||
|
return doAppendMessage(txt, charFormat(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtOutputFormatter::appendLine(const LinkResult &lr, const QString &line,
|
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);
|
cursor().insertText(line.mid(lr.end), format);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtOutputFormatter::handleLink(const QString &href)
|
bool QtOutputFormatter::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
|
||||||
@@ -205,7 +220,7 @@ void QtOutputFormatter::handleLink(const QString &href)
|
|||||||
const int line = qmlLineColumnMatch.captured(2).toInt();
|
const int line = qmlLineColumnMatch.captured(2).toInt();
|
||||||
const int column = qmlLineColumnMatch.captured(3).toInt();
|
const int column = qmlLineColumnMatch.captured(3).toInt();
|
||||||
openEditor(getFileToOpen(fileUrl), line, column - 1);
|
openEditor(getFileToOpen(fileUrl), line, column - 1);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QRegularExpression qmlLineLink("^(" QT_QML_URL_REGEXP ")" // url
|
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))));
|
fileUrl = QUrl::fromLocalFile(filePath.mid(int(strlen(scheme))));
|
||||||
const int line = qmlLineMatch.captured(2).toInt();
|
const int line = qmlLineMatch.captured(2).toInt();
|
||||||
openEditor(getFileToOpen(fileUrl), line);
|
openEditor(getFileToOpen(fileUrl), line);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString fileName;
|
QString fileName;
|
||||||
@@ -250,9 +265,10 @@ void QtOutputFormatter::handleLink(const QString &href)
|
|||||||
if (!fileName.isEmpty()) {
|
if (!fileName.isEmpty()) {
|
||||||
fileName = getFileToOpen(QUrl::fromLocalFile(fileName));
|
fileName = getFileToOpen(QUrl::fromLocalFile(fileName));
|
||||||
openEditor(fileName, line);
|
openEditor(fileName, line);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtOutputFormatter::openEditor(const QString &fileName, int line, int column)
|
void QtOutputFormatter::openEditor(const QString &fileName, int line, int column)
|
||||||
@@ -504,8 +520,13 @@ void QtSupportPlugin::testQtOutputFormatter_appendMessage()
|
|||||||
QFETCH(QString, outputText);
|
QFETCH(QString, outputText);
|
||||||
QFETCH(QTextCharFormat, inputFormat);
|
QFETCH(QTextCharFormat, inputFormat);
|
||||||
QFETCH(QTextCharFormat, outputFormat);
|
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.toPlainText(), outputText);
|
||||||
QCOMPARE(edit.currentCharFormat(), outputFormat);
|
QCOMPARE(edit.currentCharFormat(), outputFormat);
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ void SerialOutputPane::createNewOutputWindow(SerialControl *rc)
|
|||||||
this, fontSettingsChanged);
|
this, fontSettingsChanged);
|
||||||
fontSettingsChanged();
|
fontSettingsChanged();
|
||||||
ow->setWindowTitle(tr("Serial Terminal Window"));
|
ow->setWindowTitle(tr("Serial Terminal Window"));
|
||||||
ow->setFormatter(formatter);
|
ow->setFormatters({formatter});
|
||||||
// TODO: wordwrap, maxLineCount, zoom/wheelZoom (add to settings)
|
// TODO: wordwrap, maxLineCount, zoom/wheelZoom (add to settings)
|
||||||
|
|
||||||
auto controlTab = SerialControlTab(rc, ow);
|
auto controlTab = SerialControlTab(rc, ow);
|
||||||
|
|||||||
@@ -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);
|
QRegularExpressionMatchIterator it = m_regexp.globalMatch(text);
|
||||||
|
if (!it.hasNext())
|
||||||
|
return Status::NotHandled;
|
||||||
int begin = 0;
|
int begin = 0;
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
const QRegularExpressionMatch match = it.next();
|
const QRegularExpressionMatch match = it.next();
|
||||||
const QTextCharFormat normalFormat = charFormat(format);
|
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();
|
QTextCursor tc = plainTextEdit()->textCursor();
|
||||||
QStringView url = match.capturedView();
|
QStringView url = match.capturedView();
|
||||||
begin = match.capturedEnd();
|
begin = match.capturedEnd();
|
||||||
@@ -61,15 +64,17 @@ void VcsOutputFormatter::doAppendMessage(const QString &text, Utils::OutputForma
|
|||||||
tc.insertText(url.toString(), linkFormat(normalFormat, url.toString()));
|
tc.insertText(url.toString(), linkFormat(normalFormat, url.toString()));
|
||||||
tc.movePosition(QTextCursor::End);
|
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://"))
|
if (href.startsWith("http://") || href.startsWith("https://"))
|
||||||
QDesktopServices::openUrl(QUrl(href));
|
QDesktopServices::openUrl(QUrl(href));
|
||||||
else if (!href.isEmpty())
|
else if (!href.isEmpty())
|
||||||
emit referenceClicked(href);
|
emit referenceClicked(href);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VcsOutputFormatter::fillLinkContextMenu(
|
void VcsOutputFormatter::fillLinkContextMenu(
|
||||||
|
|||||||
@@ -37,14 +37,15 @@ class VcsOutputFormatter : public Utils::OutputFormatter
|
|||||||
public:
|
public:
|
||||||
VcsOutputFormatter();
|
VcsOutputFormatter();
|
||||||
~VcsOutputFormatter() override = default;
|
~VcsOutputFormatter() override = default;
|
||||||
void doAppendMessage(const QString &text, Utils::OutputFormat format) override;
|
bool handleLink(const QString &href) override;
|
||||||
void 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:
|
||||||
|
Status handleMessage(const QString &text, Utils::OutputFormat format) override;
|
||||||
|
|
||||||
const QRegularExpression m_regexp;
|
const QRegularExpression m_regexp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ OutputWindowPlainTextEdit::OutputWindowPlainTextEdit(QWidget *parent) :
|
|||||||
setFrameStyle(QFrame::NoFrame);
|
setFrameStyle(QFrame::NoFrame);
|
||||||
m_formatter = new VcsOutputFormatter;
|
m_formatter = new VcsOutputFormatter;
|
||||||
m_formatter->setBoldFontEnabled(false);
|
m_formatter->setBoldFontEnabled(false);
|
||||||
setFormatter(m_formatter);
|
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));
|
||||||
|
|||||||
Reference in New Issue
Block a user