aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils/outputformatter.cpp
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2020-04-09 17:47:01 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2020-04-14 09:46:34 +0000
commit0f163781888a6b3b39c705e5171db5ad0e766f6e (patch)
tree91bfe182c2a929f04f04652fee954bbc315ea06f /src/libs/utils/outputformatter.cpp
parentdeb0eaf7950e4aa4067af730367e61cbd732d178 (diff)
OutputFormatter: Do all formatting centrally
Instead of working directly on the text edit, the specialized OutputFormatter classes now simply ask the base class to do it for them. In practice, the request currently always is "turn this part of the text into a link", but the interface can be extended to other types of formatting, should that ever be required. This is a win/win situation: Derived classes no longer have to fiddle with QTextCursor & friends (nor do they have to call any base class functions), while the base class can make strong assumptions about what the derived class does to the text edit (i.e.: nothing). Change-Id: Icc4bc52d4001b0359247563e39a206fa274833d7 Reviewed-by: hjk <hjk@qt.io>
Diffstat (limited to 'src/libs/utils/outputformatter.cpp')
-rw-r--r--src/libs/utils/outputformatter.cpp110
1 files changed, 79 insertions, 31 deletions
diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp
index 87680cb0fd..938030a87f 100644
--- a/src/libs/utils/outputformatter.cpp
+++ b/src/libs/utils/outputformatter.cpp
@@ -28,12 +28,13 @@
#include "qtcassert.h"
#include "synchronousprocess.h"
#include "theme/theme.h"
-#include "utils/optional.h"
#include <QPair>
#include <QPlainTextEdit>
#include <QTextCursor>
+#include <numeric>
+
namespace Utils {
namespace Internal {
@@ -78,27 +79,29 @@ void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText)
void OutputFormatter::doAppendMessage(const QString &text, OutputFormat format)
{
- if (handleMessage(text, format) == Status::NotHandled)
- appendMessageDefault(text, format);
+ const QTextCharFormat charFmt = charFormat(format);
+ const QList<FormattedText> formattedText = parseAnsi(text, charFmt);
+ const QString cleanLine = std::accumulate(formattedText.begin(), formattedText.end(), QString(),
+ [](const FormattedText &t1, const FormattedText &t2) { return t1.text + t2.text; });
+ const Result res = handleMessage(cleanLine, format);
+ if (res.newContent) {
+ append(res.newContent.value(), charFmt);
+ return;
+ }
+ for (const FormattedText &output : linkifiedText(formattedText, res.linkSpecs))
+ append(output.text, output.format);
}
-OutputFormatter::Status OutputFormatter::handleMessage(const QString &text, OutputFormat format)
+OutputFormatter::Result 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)
-{
- const QList<FormattedText> formattedTextList = parseAnsi(text, format);
- for (const FormattedText &output : formattedTextList)
- append(output.text, output.format);
-}
-
QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const
{
- return d->formats[format];
+ return d->formatOverride ? d->formatOverride.value() : d->formats[format];
}
QList<FormattedText> OutputFormatter::parseAnsi(const QString &text, const QTextCharFormat &format)
@@ -106,6 +109,59 @@ QList<FormattedText> OutputFormatter::parseAnsi(const QString &text, const QText
return d->escapeCodeHandler.parseText(FormattedText(text, format));
}
+const QList<FormattedText> OutputFormatter::linkifiedText(
+ const QList<FormattedText> &text, const OutputFormatter::LinkSpecs &linkSpecs)
+{
+ if (linkSpecs.isEmpty())
+ return text;
+
+ QList<FormattedText> linkified;
+ int totalTextLengthSoFar = 0;
+ int nextLinkSpecIndex = 0;
+
+ for (const FormattedText &t : text) {
+
+ // There is no more linkification work to be done. Just copy the text as-is.
+ if (nextLinkSpecIndex >= linkSpecs.size()) {
+ linkified << t;
+ continue;
+ }
+
+ for (int nextLocalTextPos = 0; nextLocalTextPos < t.text.size(); ) {
+
+ // There are no more links in this part, so copy the rest of the text as-is.
+ if (nextLinkSpecIndex >= linkSpecs.size()) {
+ linkified << FormattedText(t.text.mid(nextLocalTextPos), t.format);
+ totalTextLengthSoFar += t.text.length() - nextLocalTextPos;
+ break;
+ }
+
+ const LinkSpec &linkSpec = linkSpecs.at(nextLinkSpecIndex);
+ const int localLinkStartPos = linkSpec.startPos - totalTextLengthSoFar;
+ ++nextLinkSpecIndex;
+
+ // We ignore links that would cross format boundaries.
+ if (localLinkStartPos < nextLocalTextPos
+ || localLinkStartPos + linkSpec.length > t.text.length()) {
+ linkified << FormattedText(t.text.mid(nextLocalTextPos), t.format);
+ totalTextLengthSoFar += t.text.length() - nextLocalTextPos;
+ break;
+ }
+
+ // Now we know we have a link that is fully inside this part of the text.
+ // Split the text so that the link part gets the appropriate format.
+ const int prefixLength = localLinkStartPos - nextLocalTextPos;
+ const QString textBeforeLink = t.text.mid(nextLocalTextPos, prefixLength);
+ linkified << FormattedText(textBeforeLink, t.format);
+ const QString linkedText = t.text.mid(localLinkStartPos, linkSpec.length);
+ linkified << FormattedText(linkedText, linkFormat(t.format, linkSpec.target));
+ nextLocalTextPos = localLinkStartPos + linkSpec.length;
+ totalTextLengthSoFar += prefixLength + linkSpec.length;
+ }
+ }
+ return linkified;
+}
+
void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
{
int startPos = 0;
@@ -120,11 +176,6 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
d->cursor.insertText(text.mid(startPos), format);
}
-QTextCursor &OutputFormatter::cursor() const
-{
- return d->cursor;
-}
-
QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat, const QString &href)
{
QTextCharFormat result = inputFormat;
@@ -140,11 +191,6 @@ 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()
{
// Note that this approach will fail if the text edit is not read-only and users
@@ -282,30 +328,32 @@ void AggregatingOutputFormatter::setFormatters(const QList<OutputFormatter *> &f
d->nextFormatter = nullptr;
}
-OutputFormatter::Status AggregatingOutputFormatter::handleMessage(const QString &text,
+OutputFormatter::Result AggregatingOutputFormatter::handleMessage(const QString &text,
OutputFormat format)
{
if (d->nextFormatter) {
- switch (d->nextFormatter->handleMessage(text, format)) {
+ const Result res = d->nextFormatter->handleMessage(text, format);
+ switch (res.status) {
case Status::Done:
d->nextFormatter = nullptr;
- return Status::Done;
+ return res;
case Status::InProgress:
- return Status::InProgress;
+ return res;
case Status::NotHandled:
- QTC_CHECK(false);
+ QTC_CHECK(false); // TODO: This case will be legal after the merge
d->nextFormatter = nullptr;
- return Status::NotHandled;
+ return res;
}
}
QTC_CHECK(!d->nextFormatter);
for (OutputFormatter * const formatter : qAsConst(d->formatters)) {
- switch (formatter->handleMessage(text, format)) {
+ const Result res = formatter->handleMessage(text, format);
+ switch (res.status) {
case Status::Done:
- return Status::Done;
+ return res;
case Status::InProgress:
d->nextFormatter = formatter;
- return Status::InProgress;
+ return res;
case Status::NotHandled:
break;
}