forked from qt-creator/qt-creator
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:
committed by
André Hartmann
parent
33ae764554
commit
f66ee66595
@@ -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)
|
||||||
{
|
{
|
||||||
|
@@ -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:
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user