QtOutputFormatter: Support ANSI colors

Task-number: QTCREATORBUG-13764
Change-Id: Iac020bbea0eae8bd2b09f836a61da199acec1575
Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Andre Hartmann
2015-01-02 17:02:00 +01:00
committed by André Hartmann
parent 33ae764554
commit f66ee66595
5 changed files with 142 additions and 21 deletions

View File

@@ -73,8 +73,7 @@ void OutputFormatter::appendMessage(const QString &text, const QTextCharFormat &
QTextCursor cursor(m_plainTextEdit->document()); QTextCursor cursor(m_plainTextEdit->document());
cursor.movePosition(QTextCursor::End); cursor.movePosition(QTextCursor::End);
foreach (const FormattedText &output, foreach (const FormattedText &output, parseAnsi(text, format)) {
m_escapeCodeHandler->parseText(FormattedText(text, format))) {
int startPos = 0; int startPos = 0;
int crPos = -1; int crPos = -1;
while ((crPos = output.text.indexOf(QLatin1Char('\r'), startPos)) >= 0) { while ((crPos = output.text.indexOf(QLatin1Char('\r'), startPos)) >= 0) {
@@ -92,6 +91,11 @@ QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const
return m_formats[format]; return m_formats[format];
} }
QList<FormattedText> OutputFormatter::parseAnsi(const QString &text, const QTextCharFormat &format)
{
return m_escapeCodeHandler->parseText(FormattedText(text, format));
}
void OutputFormatter::append(QTextCursor &cursor, const QString &text, void OutputFormatter::append(QTextCursor &cursor, const QString &text,
const QTextCharFormat &format) const QTextCharFormat &format)
{ {

View File

@@ -47,6 +47,7 @@ QT_END_NAMESPACE
namespace Utils { namespace Utils {
class AnsiEscapeCodeHandler; class AnsiEscapeCodeHandler;
class FormattedText;
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
{ {
@@ -71,6 +72,7 @@ protected:
void initFormats(); void initFormats();
virtual void clearLastLine(); virtual void clearLastLine();
QTextCharFormat charFormat(OutputFormat format) const; QTextCharFormat charFormat(OutputFormat format) const;
QList<Utils::FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format);
void append(QTextCursor &cursor, const QString &text, const QTextCharFormat &format); void append(QTextCursor &cursor, const QString &text, const QTextCharFormat &format);
private: private:

View File

@@ -32,6 +32,7 @@
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <utils/ansiescapecodehandler.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
#include <QPlainTextEdit> #include <QPlainTextEdit>
@@ -100,10 +101,12 @@ LinkResult QtOutputFormatter::matchLine(const QString &line) const
void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat format) void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat format)
{ {
QTextCursor cursor(plainTextEdit()->document()); appendMessage(txt, charFormat(format));
cursor.movePosition(QTextCursor::End); }
cursor.beginEditBlock();
void QtOutputFormatter::appendMessagePart(QTextCursor &cursor, const QString &txt,
const QTextCharFormat &format)
{
QString deferredText; QString deferredText;
int start = 0; int start = 0;
@@ -117,7 +120,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
LinkResult lr = matchLine(line); LinkResult lr = matchLine(line);
if (!lr.href.isEmpty()) { if (!lr.href.isEmpty()) {
// Found something && line continuation // Found something && line continuation
cursor.insertText(deferredText, charFormat(format)); cursor.insertText(deferredText, format);
deferredText.clear(); deferredText.clear();
clearLastLine(); clearLastLine();
appendLine(cursor, lr, line, format); appendLine(cursor, lr, line, format);
@@ -131,7 +134,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
const QString line = txt.mid(start, pos - start + 1); const QString line = txt.mid(start, pos - start + 1);
LinkResult lr = matchLine(line); LinkResult lr = matchLine(line);
if (!lr.href.isEmpty()) { if (!lr.href.isEmpty()) {
cursor.insertText(deferredText, charFormat(format)); cursor.insertText(deferredText, format);
deferredText.clear(); deferredText.clear();
appendLine(cursor, lr, line, format); appendLine(cursor, lr, line, format);
} else { } else {
@@ -151,7 +154,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
LinkResult lr = matchLine(line); LinkResult lr = matchLine(line);
if (!lr.href.isEmpty()) { if (!lr.href.isEmpty()) {
// Found something && line continuation // Found something && line continuation
cursor.insertText(deferredText, charFormat(format)); cursor.insertText(deferredText, format);
deferredText.clear(); deferredText.clear();
clearLastLine(); clearLastLine();
appendLine(cursor, lr, line, format); appendLine(cursor, lr, line, format);
@@ -164,7 +167,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
m_lastLine = txt.mid(start); m_lastLine = txt.mid(start);
LinkResult lr = matchLine(m_lastLine); LinkResult lr = matchLine(m_lastLine);
if (!lr.href.isEmpty()) { if (!lr.href.isEmpty()) {
cursor.insertText(deferredText, charFormat(format)); cursor.insertText(deferredText, format);
deferredText.clear(); deferredText.clear();
appendLine(cursor, lr, m_lastLine, format); appendLine(cursor, lr, m_lastLine, format);
} else { } else {
@@ -172,23 +175,44 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
} }
} }
} }
cursor.insertText(deferredText, charFormat(format)); cursor.insertText(deferredText, format);
}
void QtOutputFormatter::appendMessage(const QString &txt, const QTextCharFormat &format)
{
QTextCursor cursor(plainTextEdit()->document());
cursor.movePosition(QTextCursor::End);
cursor.beginEditBlock();
foreach (const FormattedText &output, parseAnsi(txt, format))
appendMessagePart(cursor, output.text, output.format);
cursor.endEditBlock(); cursor.endEditBlock();
} }
void QtOutputFormatter::appendLine(QTextCursor &cursor, const LinkResult &lr, void QtOutputFormatter::appendLine(QTextCursor &cursor, const LinkResult &lr,
const QString &line, Utils::OutputFormat format) const QString &line, Utils::OutputFormat format)
{ {
const QTextCharFormat normalFormat = charFormat(format); appendLine(cursor, lr, line, charFormat(format));
cursor.insertText(line.left(lr.start), normalFormat); }
QTextCharFormat linkFormat = normalFormat; static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href)
linkFormat.setForeground(creatorTheme()->color(Theme::QtOutputFormatter_LinkTextColor)); {
linkFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); QTextCharFormat result = inputFormat;
linkFormat.setAnchor(true); result.setForeground(creatorTheme()->color(Theme::QtOutputFormatter_LinkTextColor));
linkFormat.setAnchorHref(lr.href); result.setUnderlineStyle(QTextCharFormat::SingleUnderline);
cursor.insertText(line.mid(lr.start, lr.end - lr.start), linkFormat); result.setAnchor(true);
cursor.insertText(line.mid(lr.end), normalFormat); result.setAnchorHref(href);
return result;
}
void QtOutputFormatter::appendLine(QTextCursor &cursor, const LinkResult &lr,
const QString &line, const QTextCharFormat &format)
{
cursor.insertText(line.left(lr.start), format);
cursor.insertText(line.mid(lr.start, lr.end - lr.start), linkFormat(format, lr.href));
cursor.insertText(line.mid(lr.end), format);
} }
void QtOutputFormatter::handleLink(const QString &href) void QtOutputFormatter::handleLink(const QString &href)
@@ -274,6 +298,8 @@ void QtOutputFormatter::updateProjectFileList()
using namespace QtSupport::Internal; using namespace QtSupport::Internal;
Q_DECLARE_METATYPE(QTextCharFormat);
class TestQtOutputFormatter : public QtOutputFormatter class TestQtOutputFormatter : public QtOutputFormatter
{ {
public: public:
@@ -359,4 +385,87 @@ void QtSupportPlugin::testQtOutputFormatter()
QCOMPARE(formatter.column, column); QCOMPARE(formatter.column, column);
} }
static QTextCharFormat blueFormat()
{
QTextCharFormat result;
result.setForeground(QColor(0, 0, 127));
return result;
}
void QtSupportPlugin::testQtOutputFormatter_appendMessage_data()
{
QTest::addColumn<QString>("inputText");
QTest::addColumn<QString>("outputText");
QTest::addColumn<QTextCharFormat>("inputFormat");
QTest::addColumn<QTextCharFormat>("outputFormat");
QTest::newRow("pass through")
<< QString::fromLatin1("test\n123")
<< QString::fromLatin1("test\n123")
<< QTextCharFormat()
<< QTextCharFormat();
QTest::newRow("Qt error")
<< QString::fromLatin1("Object::Test in test.cpp:123")
<< QString::fromLatin1("Object::Test in test.cpp:123")
<< QTextCharFormat()
<< linkFormat(QTextCharFormat(), QLatin1String("test.cpp:123"));
QTest::newRow("colored")
<< QString::fromLatin1("blue da ba dee")
<< QString::fromLatin1("blue da ba dee")
<< blueFormat()
<< blueFormat();
QTest::newRow("ANSI color change")
<< QString::fromLatin1("\x1b[38;2;0;0;127mHello")
<< QString::fromLatin1("Hello")
<< QTextCharFormat()
<< blueFormat();
}
void QtSupportPlugin::testQtOutputFormatter_appendMessage()
{
QPlainTextEdit edit;
TestQtOutputFormatter formatter;
formatter.setPlainTextEdit(&edit);
QFETCH(QString, inputText);
QFETCH(QString, outputText);
QFETCH(QTextCharFormat, inputFormat);
QFETCH(QTextCharFormat, outputFormat);
formatter.appendMessage(inputText, inputFormat);
QCOMPARE(edit.toPlainText(), outputText);
QCOMPARE(edit.currentCharFormat(), outputFormat);
}
void QtSupportPlugin::testQtOutputFormatter_appendMixedAssertAndAnsi()
{
QPlainTextEdit edit;
TestQtOutputFormatter formatter;
formatter.setPlainTextEdit(&edit);
const QString inputText = QString::fromLatin1(
"\x1b[38;2;0;0;127mHello\n"
"Object::Test in test.cpp:123\n"
"\x1b[38;2;0;0;127mHello\n");
const QString outputText = QString::fromLatin1(
"Hello\n"
"Object::Test in test.cpp:123\n"
"Hello\n");
formatter.appendMessage(inputText, QTextCharFormat());
QCOMPARE(edit.toPlainText(), outputText);
edit.moveCursor(QTextCursor::Start);
QCOMPARE(edit.currentCharFormat(), blueFormat());
edit.moveCursor(QTextCursor::Down);
edit.moveCursor(QTextCursor::EndOfLine);
QCOMPARE(edit.currentCharFormat(), linkFormat(QTextCharFormat(), QLatin1String("test.cpp:123")));
edit.moveCursor(QTextCursor::End);
QCOMPARE(edit.currentCharFormat(), blueFormat());
}
#endif // WITH_TESTS #endif // WITH_TESTS

View File

@@ -63,8 +63,8 @@ class QTSUPPORT_EXPORT QtOutputFormatter
public: public:
explicit QtOutputFormatter(ProjectExplorer::Project *project); explicit QtOutputFormatter(ProjectExplorer::Project *project);
void appendMessage(const QString &text, void appendMessage(const QString &text, Utils::OutputFormat format);
Utils::OutputFormat format); void appendMessage(const QString &text, const QTextCharFormat &format);
void handleLink(const QString &href); void handleLink(const QString &href);
protected: protected:
@@ -76,8 +76,11 @@ private slots:
private: private:
LinkResult matchLine(const QString &line) const; LinkResult matchLine(const QString &line) const;
void appendMessagePart(QTextCursor &cursor, const QString &txt, const QTextCharFormat &format);
void appendLine(QTextCursor &cursor, const LinkResult &lr, const QString &line, void appendLine(QTextCursor &cursor, const LinkResult &lr, const QString &line,
Utils::OutputFormat); Utils::OutputFormat);
void appendLine(QTextCursor &cursor, const LinkResult &lr, const QString &line,
const QTextCharFormat &format);
mutable QRegExp m_qmlError; mutable QRegExp m_qmlError;
mutable QRegExp m_qtError; mutable QRegExp m_qtError;

View File

@@ -54,6 +54,9 @@ private slots:
void testQtOutputParser(); void testQtOutputParser();
void testQtOutputFormatter_data(); void testQtOutputFormatter_data();
void testQtOutputFormatter(); void testQtOutputFormatter();
void testQtOutputFormatter_appendMessage_data();
void testQtOutputFormatter_appendMessage();
void testQtOutputFormatter_appendMixedAssertAndAnsi();
#endif #endif
}; };