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());
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
|
||||
foreach (const FormattedText &output,
|
||||
m_escapeCodeHandler->parseText(FormattedText(text, format))) {
|
||||
foreach (const FormattedText &output, parseAnsi(text, format)) {
|
||||
int startPos = 0;
|
||||
int crPos = -1;
|
||||
while ((crPos = output.text.indexOf(QLatin1Char('\r'), startPos)) >= 0) {
|
||||
@@ -92,6 +91,11 @@ QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const
|
||||
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,
|
||||
const QTextCharFormat &format)
|
||||
{
|
||||
|
@@ -47,6 +47,7 @@ QT_END_NAMESPACE
|
||||
namespace Utils {
|
||||
|
||||
class AnsiEscapeCodeHandler;
|
||||
class FormattedText;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject
|
||||
{
|
||||
@@ -71,6 +72,7 @@ protected:
|
||||
void initFormats();
|
||||
virtual void clearLastLine();
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <utils/ansiescapecodehandler.h>
|
||||
#include <utils/theme/theme.h>
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
@@ -100,10 +101,12 @@ LinkResult QtOutputFormatter::matchLine(const QString &line) const
|
||||
|
||||
void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat format)
|
||||
{
|
||||
QTextCursor cursor(plainTextEdit()->document());
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.beginEditBlock();
|
||||
appendMessage(txt, charFormat(format));
|
||||
}
|
||||
|
||||
void QtOutputFormatter::appendMessagePart(QTextCursor &cursor, const QString &txt,
|
||||
const QTextCharFormat &format)
|
||||
{
|
||||
QString deferredText;
|
||||
|
||||
int start = 0;
|
||||
@@ -117,7 +120,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
|
||||
LinkResult lr = matchLine(line);
|
||||
if (!lr.href.isEmpty()) {
|
||||
// Found something && line continuation
|
||||
cursor.insertText(deferredText, charFormat(format));
|
||||
cursor.insertText(deferredText, format);
|
||||
deferredText.clear();
|
||||
clearLastLine();
|
||||
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);
|
||||
LinkResult lr = matchLine(line);
|
||||
if (!lr.href.isEmpty()) {
|
||||
cursor.insertText(deferredText, charFormat(format));
|
||||
cursor.insertText(deferredText, format);
|
||||
deferredText.clear();
|
||||
appendLine(cursor, lr, line, format);
|
||||
} else {
|
||||
@@ -151,7 +154,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
|
||||
LinkResult lr = matchLine(line);
|
||||
if (!lr.href.isEmpty()) {
|
||||
// Found something && line continuation
|
||||
cursor.insertText(deferredText, charFormat(format));
|
||||
cursor.insertText(deferredText, format);
|
||||
deferredText.clear();
|
||||
clearLastLine();
|
||||
appendLine(cursor, lr, line, format);
|
||||
@@ -164,7 +167,7 @@ void QtOutputFormatter::appendMessage(const QString &txt, Utils::OutputFormat fo
|
||||
m_lastLine = txt.mid(start);
|
||||
LinkResult lr = matchLine(m_lastLine);
|
||||
if (!lr.href.isEmpty()) {
|
||||
cursor.insertText(deferredText, charFormat(format));
|
||||
cursor.insertText(deferredText, format);
|
||||
deferredText.clear();
|
||||
appendLine(cursor, lr, m_lastLine, format);
|
||||
} 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();
|
||||
}
|
||||
|
||||
void QtOutputFormatter::appendLine(QTextCursor &cursor, const LinkResult &lr,
|
||||
const QString &line, Utils::OutputFormat format)
|
||||
{
|
||||
const QTextCharFormat normalFormat = charFormat(format);
|
||||
cursor.insertText(line.left(lr.start), normalFormat);
|
||||
appendLine(cursor, lr, line, charFormat(format));
|
||||
}
|
||||
|
||||
QTextCharFormat linkFormat = normalFormat;
|
||||
linkFormat.setForeground(creatorTheme()->color(Theme::QtOutputFormatter_LinkTextColor));
|
||||
linkFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||
linkFormat.setAnchor(true);
|
||||
linkFormat.setAnchorHref(lr.href);
|
||||
cursor.insertText(line.mid(lr.start, lr.end - lr.start), linkFormat);
|
||||
cursor.insertText(line.mid(lr.end), normalFormat);
|
||||
static QTextCharFormat linkFormat(const QTextCharFormat &inputFormat, const QString &href)
|
||||
{
|
||||
QTextCharFormat result = inputFormat;
|
||||
result.setForeground(creatorTheme()->color(Theme::QtOutputFormatter_LinkTextColor));
|
||||
result.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||
result.setAnchor(true);
|
||||
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)
|
||||
@@ -274,6 +298,8 @@ void QtOutputFormatter::updateProjectFileList()
|
||||
|
||||
using namespace QtSupport::Internal;
|
||||
|
||||
Q_DECLARE_METATYPE(QTextCharFormat);
|
||||
|
||||
class TestQtOutputFormatter : public QtOutputFormatter
|
||||
{
|
||||
public:
|
||||
@@ -359,4 +385,87 @@ void QtSupportPlugin::testQtOutputFormatter()
|
||||
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
|
||||
|
@@ -63,8 +63,8 @@ class QTSUPPORT_EXPORT QtOutputFormatter
|
||||
public:
|
||||
explicit QtOutputFormatter(ProjectExplorer::Project *project);
|
||||
|
||||
void appendMessage(const QString &text,
|
||||
Utils::OutputFormat format);
|
||||
void appendMessage(const QString &text, Utils::OutputFormat format);
|
||||
void appendMessage(const QString &text, const QTextCharFormat &format);
|
||||
void handleLink(const QString &href);
|
||||
|
||||
protected:
|
||||
@@ -76,8 +76,11 @@ private slots:
|
||||
|
||||
private:
|
||||
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,
|
||||
Utils::OutputFormat);
|
||||
void appendLine(QTextCursor &cursor, const LinkResult &lr, const QString &line,
|
||||
const QTextCharFormat &format);
|
||||
|
||||
mutable QRegExp m_qmlError;
|
||||
mutable QRegExp m_qtError;
|
||||
|
@@ -54,6 +54,9 @@ private slots:
|
||||
void testQtOutputParser();
|
||||
void testQtOutputFormatter_data();
|
||||
void testQtOutputFormatter();
|
||||
void testQtOutputFormatter_appendMessage_data();
|
||||
void testQtOutputFormatter_appendMessage();
|
||||
void testQtOutputFormatter_appendMixedAssertAndAnsi();
|
||||
#endif
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user