diff options
author | Christian Stenger <christian.stenger@qt.io> | 2019-11-14 14:11:34 +0100 |
---|---|---|
committer | Christian Stenger <christian.stenger@qt.io> | 2020-01-08 14:10:26 +0000 |
commit | f188622704741a9de68c26b6ddf5274197706213 (patch) | |
tree | 7898c82da1592d77d8d211380746ca27df19645f /src/plugins/autotest | |
parent | c893fdffb64a6c253b3f55b61bd46ef9aa0984e9 (diff) |
AutoTest: Re-use established code
Instead of "parsing", modifying and highlighting the text
after it has been added use existing code to get formatted
code beforehand.
Change-Id: I33be2950f9b9a6910ad80b10b132089f4e5f0b1c
Reviewed-by: David Schulz <david.schulz@qt.io>
Diffstat (limited to 'src/plugins/autotest')
-rw-r--r-- | src/plugins/autotest/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/autotest/autotest.pro | 2 | ||||
-rw-r--r-- | src/plugins/autotest/autotest.qbs | 2 | ||||
-rw-r--r-- | src/plugins/autotest/outputhighlighter.cpp | 170 | ||||
-rw-r--r-- | src/plugins/autotest/outputhighlighter.h | 49 | ||||
-rw-r--r-- | src/plugins/autotest/testresultspane.cpp | 64 | ||||
-rw-r--r-- | src/plugins/autotest/testresultspane.h | 7 |
7 files changed, 56 insertions, 239 deletions
diff --git a/src/plugins/autotest/CMakeLists.txt b/src/plugins/autotest/CMakeLists.txt index 3cd7471812d..2c2051647f2 100644 --- a/src/plugins/autotest/CMakeLists.txt +++ b/src/plugins/autotest/CMakeLists.txt @@ -36,7 +36,6 @@ add_qtc_plugin(AutoTest itestframework.h itestparser.cpp itestparser.h itestsettingspage.h - outputhighlighter.cpp outputhighlighter.h projectsettingswidget.cpp projectsettingswidget.h qtest/qttest_utils.cpp qtest/qttest_utils.h qtest/qttestconfiguration.cpp qtest/qttestconfiguration.h diff --git a/src/plugins/autotest/autotest.pro b/src/plugins/autotest/autotest.pro index f0f516e0178..c849d8c4d47 100644 --- a/src/plugins/autotest/autotest.pro +++ b/src/plugins/autotest/autotest.pro @@ -8,7 +8,6 @@ DEFINES += AUTOTEST_LIBRARY SOURCES += \ autotestplugin.cpp \ itestparser.cpp \ - outputhighlighter.cpp \ projectsettingswidget.cpp \ testcodeparser.cpp \ testconfiguration.cpp \ @@ -73,7 +72,6 @@ HEADERS += \ itestframework.h \ itestparser.h \ itestsettingspage.h \ - outputhighlighter.h \ projectsettingswidget.h \ testcodeparser.h \ testconfiguration.h \ diff --git a/src/plugins/autotest/autotest.qbs b/src/plugins/autotest/autotest.qbs index e78ad344c46..0336ec75eb5 100644 --- a/src/plugins/autotest/autotest.qbs +++ b/src/plugins/autotest/autotest.qbs @@ -37,8 +37,6 @@ QtcPlugin { "autotestconstants.h", "autotestplugin.cpp", "autotestplugin.h", - "outputhighlighter.cpp", - "outputhighlighter.h", "projectsettingswidget.cpp", "projectsettingswidget.h", "testcodeparser.cpp", diff --git a/src/plugins/autotest/outputhighlighter.cpp b/src/plugins/autotest/outputhighlighter.cpp deleted file mode 100644 index 727d2d6267b..00000000000 --- a/src/plugins/autotest/outputhighlighter.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "outputhighlighter.h" -#include "testresultspane.h" - -#include <utils/algorithm.h> -#include <utils/theme/theme.h> - -#include <QRegularExpression> -#include <QTextDocument> - -namespace Autotest { -namespace Internal { - -class OutputHighlighterBlockData : public QTextBlockUserData -{ -public: - OutputHighlighterBlockData(const QTextCharFormat &format, OutputChannel channel) - : lastCharFormat(format), outputChannel(channel) {} - - QTextCharFormat lastCharFormat; - OutputChannel outputChannel; -}; - -OutputHighlighter::OutputHighlighter(QTextDocument *parent) - : QSyntaxHighlighter(parent) -{ -} - -static QColor translateCommandlineColor(const QString &cmdlineEscapeString) -{ - const QColor defaultColor = Utils::creatorTheme()->color(Utils::Theme::PaletteWindowText); - if (cmdlineEscapeString.isEmpty()) - return defaultColor; - const QRegularExpression regex("^\\d+(;\\d+)*$"); - QRegularExpressionMatch matcher = regex.match(cmdlineEscapeString); - if (!matcher.hasMatch()) - return defaultColor; - - QList<int> values = Utils::transform(matcher.captured().split(';'), - [](const QString &str) { return str.toInt(); }); - Utils::sort(values); - - bool isBright = false; - for (int value : values) { - if (value < 10) { // special formats (bright/bold, underline,..) - isBright = value == 1; - continue; - } - switch (value) { // so far only foreground - case 30: return isBright ? QColor(76, 76, 76) : QColor(0, 0, 0); - case 31: return isBright ? QColor(205, 0, 0) : QColor(180, 0, 0); - case 32: return isBright ? QColor(0, 205, 0) : QColor(0, 180, 0); - case 33: return isBright ? QColor(205, 205, 0) : QColor(180, 180, 0); - case 34: return isBright ? QColor(30, 140, 255) : QColor(70, 130, 180); - case 35: return isBright ? QColor(205, 0, 205) : QColor(180, 0, 180); - case 36: return isBright ? QColor(0, 205, 205) : QColor(0, 180, 180); - case 37: return isBright ? QColor(255, 255, 255) : QColor(230, 230, 230); - case 39: return defaultColor; // use default color - default: continue; // ignore others like background - } - } - return defaultColor; -} - -void OutputHighlighter::highlightBlock(const QString &text) -{ - struct PositionsAndColors - { - PositionsAndColors(const QRegularExpressionMatch &match) - : start(match.capturedStart()) - , end(match.capturedEnd()) - , length(match.capturedLength()) - , color(translateCommandlineColor(match.captured(1))) - {} - int start; // start of the escape sequence - int end; // end of the escape sequence - int length; // length of the escape sequence - QColor color; // color to be used - }; - - if (text.isEmpty() || currentBlock().userData()) - return; - - auto resultsPane = TestResultsPane::instance(); - OutputChannel channel = resultsPane->channelForBlockNumber(currentBlock().blockNumber()); - - const QRegularExpression pattern("\u001B\\[(.*?)m"); - QTextCursor cursor(currentBlock()); - - QList<PositionsAndColors> escapeSequences; - QRegularExpressionMatchIterator it = pattern.globalMatch(text); - while (it.hasNext()) - escapeSequences.append(PositionsAndColors(it.next())); - - int removed = 0; - const int blockPosition = currentBlock().position(); - - QTextCharFormat modified = startCharFormat(); - cursor.select(QTextCursor::BlockUnderCursor); - cursor.setCharFormat(modified); - - for (int i = 0, max = escapeSequences.length(); i < max; ++i) { - auto seq = escapeSequences.at(i); - - cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); - if (seq.length > 0) { - // delete color escape sequence - cursor.setPosition(blockPosition + seq.start - removed); - cursor.setPosition(blockPosition + seq.end - removed, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - removed += seq.length; - } - - // highlight it until next sequence starts or EOL - if (i < max - 1) - cursor.setPosition(blockPosition + escapeSequences.at(i + 1).start - removed, QTextCursor::KeepAnchor); - else - cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - modified.setForeground(seq.color); - cursor.setCharFormat(modified); - } - - currentBlock().setUserData(new OutputHighlighterBlockData(modified, channel)); -} - -QTextCharFormat OutputHighlighter::startCharFormat() const -{ - QTextBlock current = currentBlock(); - OutputChannel channel = TestResultsPane::instance()->channelForBlockNumber( - current.blockNumber()); - - do { - if (auto data = static_cast<OutputHighlighterBlockData *>(current.userData())) { - if (data->outputChannel == channel) - return data->lastCharFormat; - } - current = current.previous(); - } while (current.isValid()); - - QTextCharFormat format = currentBlock().charFormat(); - format.setForeground(Utils::creatorTheme()->color(Utils::Theme::PaletteWindowText)); - return format; -} - -} // namespace Internal -} // namespace Autotest diff --git a/src/plugins/autotest/outputhighlighter.h b/src/plugins/autotest/outputhighlighter.h deleted file mode 100644 index b0b1f5b8c68..00000000000 --- a/src/plugins/autotest/outputhighlighter.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include <QSyntaxHighlighter> - -#pragma once - -namespace Autotest { - -enum class OutputChannel; - -namespace Internal { - -class OutputHighlighter : public QSyntaxHighlighter -{ -public: - explicit OutputHighlighter(QTextDocument *parent = nullptr); - -protected: - void highlightBlock(const QString &text) override; - -private: - QTextCharFormat startCharFormat() const; -}; - -} // namespace Internal -} // namespace Autotest diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp index baac198a376..daa290601bf 100644 --- a/src/plugins/autotest/testresultspane.cpp +++ b/src/plugins/autotest/testresultspane.cpp @@ -34,7 +34,6 @@ #include "testcodeparser.h" #include "testeditormark.h" #include "testoutputreader.h" -#include "outputhighlighter.h" #include <aggregation/aggregate.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -50,6 +49,7 @@ #include <texteditor/texteditor.h> #include <texteditor/texteditorsettings.h> #include <utils/qtcassert.h> +#include <utils/stylehelper.h> #include <utils/theme/theme.h> #include <utils/utilsicons.h> @@ -136,7 +136,6 @@ TestResultsPane::TestResultsPane(QObject *parent) : m_textOutput->setFont(TextEditor::TextEditorSettings::fontSettings().font()); m_textOutput->setWordWrapMode(QTextOption::WordWrap); m_textOutput->setReadOnly(true); - new OutputHighlighter(m_textOutput->document()); m_outputWidget->addWidget(m_textOutput); auto agg = new Aggregation::Aggregate; @@ -241,6 +240,34 @@ void TestResultsPane::addTestResult(const TestResultPtr &result) navigateStateChanged(); } +static void checkAndFineTuneColors(QTextCharFormat *format) +{ + QTC_ASSERT(format, return); + const QColor bgColor = format->background().color(); + QColor fgColor = format->foreground().color(); + + if (Utils::StyleHelper::isReadableOn(bgColor, fgColor)) + return; + + int h, s, v; + fgColor.getHsv(&h, &s, &v); + // adjust the color value to ensure better readability + if (Utils::StyleHelper::luminance(bgColor) < .5) + v = v + 64; + else + v = v - 64; + + fgColor.setHsv(h, s, v); + if (!Utils::StyleHelper::isReadableOn(bgColor, fgColor)) { + s = (s + 128) % 255; // adjust the saturation to ensure better readability + fgColor.setHsv(h, s, v); + if (!Utils::StyleHelper::isReadableOn(bgColor, fgColor)) + return; + } + + format->setForeground(fgColor); +} + void TestResultsPane::addOutputLine(const QByteArray &outputLine, OutputChannel channel) { if (!QTC_GUARD(!outputLine.contains('\n'))) { @@ -248,8 +275,21 @@ void TestResultsPane::addOutputLine(const QByteArray &outputLine, OutputChannel addOutputLine(line, channel); return; } - m_outputChannels.append(channel); - m_textOutput->appendPlainText(QString::fromUtf8(outputLine)); + + const Utils::FormattedText formattedText + = Utils::FormattedText{QString::fromUtf8(outputLine), m_defaultFormat}; + QList<Utils::FormattedText> formatted = channel == OutputChannel::StdOut + ? m_stdOutHandler.parseText(formattedText) + : m_stdErrHandler.parseText(formattedText); + + QTextCursor cursor = m_textOutput->textCursor(); + cursor.beginEditBlock(); + for (auto formattedText : formatted) { + checkAndFineTuneColors(&formattedText.format); + cursor.insertText(formattedText.text, formattedText.format); + } + cursor.insertText("\n"); + cursor.endEditBlock(); } QWidget *TestResultsPane::outputWidget(QWidget *parent) @@ -289,8 +329,15 @@ void TestResultsPane::clearContents() m_autoScroll = AutotestPlugin::settings()->autoScroll; connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection); - m_outputChannels.clear(); m_textOutput->clear(); + m_defaultFormat.setBackground(Utils::creatorTheme()->palette().color( + m_textOutput->backgroundRole())); + m_defaultFormat.setForeground(Utils::creatorTheme()->palette().color( + m_textOutput->foregroundRole())); + + // in case they had been forgotten to reset + m_stdErrHandler.endFormatScope(); + m_stdOutHandler.endFormatScope(); clearMarks(); } @@ -709,12 +756,5 @@ void TestResultsPane::showTestResult(const QModelIndex &index) } } -OutputChannel TestResultsPane::channelForBlockNumber(int blockNumber) const -{ - QTC_ASSERT(blockNumber > -1 && blockNumber < m_outputChannels.size(), - return OutputChannel::StdOut); - return m_outputChannels.at(blockNumber); -} - } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/testresultspane.h b/src/plugins/autotest/testresultspane.h index 3372cee06bd..ac835fd5082 100644 --- a/src/plugins/autotest/testresultspane.h +++ b/src/plugins/autotest/testresultspane.h @@ -29,6 +29,7 @@ #include <coreplugin/ioutputpane.h> +#include <utils/ansiescapecodehandler.h> #include <utils/itemviews.h> QT_BEGIN_NAMESPACE @@ -98,8 +99,6 @@ public: void addOutputLine(const QByteArray &outputLine, OutputChannel channel); void showTestResult(const QModelIndex &index); - OutputChannel channelForBlockNumber(int blockNumber) const; - private: explicit TestResultsPane(QObject *parent = nullptr); @@ -147,7 +146,9 @@ private: bool m_atEnd = false; bool m_testRunning = false; QVector<TestEditorMark *> m_marks; - QList<OutputChannel> m_outputChannels; + QTextCharFormat m_defaultFormat; + Utils::AnsiEscapeCodeHandler m_stdErrHandler; + Utils::AnsiEscapeCodeHandler m_stdOutHandler; }; } // namespace Internal |