aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils/outputformatter.cpp
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2020-03-18 12:56:35 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2020-03-19 09:31:02 +0000
commitef6af1b7df7374eab6bb85a2fa80dd3a5e12e2a2 (patch)
tree4c35e12818c55e44d5af23e3a99107bdc2156f68 /src/libs/utils/outputformatter.cpp
parentc8a2ea54333b4d0582afb62b4120558f8c6945e7 (diff)
OutputFormatter: Do the newline handling centrally
All output formatters are line-based, and they all did their own line splitting and, if they didn't entirely ignore it, handling of partial lines. Instead, we now do all the book-keeping in the base class, and the subclasses always work with complete lines. Change-Id: I0b0df7951d0e4f6601f4d912230071784c87b3d3 Reviewed-by: hjk <hjk@qt.io>
Diffstat (limited to 'src/libs/utils/outputformatter.cpp')
-rw-r--r--src/libs/utils/outputformatter.cpp60
1 files changed, 59 insertions, 1 deletions
diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp
index 9c3cee4879..ea40ddadee 100644
--- a/src/libs/utils/outputformatter.cpp
+++ b/src/libs/utils/outputformatter.cpp
@@ -28,6 +28,7 @@
#include "synchronousprocess.h"
#include "theme/theme.h"
+#include <QPair>
#include <QPlainTextEdit>
#include <QTextCursor>
@@ -42,6 +43,7 @@ public:
QTextCharFormat formats[NumberOfFormats];
QTextCursor cursor;
AnsiEscapeCodeHandler escapeCodeHandler;
+ QPair<QString, OutputFormat> incompleteLine;
bool boldFontEnabled = true;
bool prependCarriageReturn = false;
};
@@ -118,6 +120,9 @@ QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat,
void OutputFormatter::clearLastLine()
{
+ // Note that this approach will fail if the text edit is not read-only and users
+ // have messed with the last line between programmatic inputs.
+ // We live with this risk, as all the alternatives are worse.
if (!d->cursor.atEnd())
d->cursor.movePosition(QTextCursor::End);
d->cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
@@ -139,6 +144,20 @@ void OutputFormatter::initFormats()
setBoldFontEnabled(d->boldFontEnabled);
}
+void OutputFormatter::flushIncompleteLine()
+{
+ clearLastLine();
+ doAppendMessage(d->incompleteLine.first, d->incompleteLine.second);
+ d->incompleteLine.first.clear();
+}
+
+void OutputFormatter::dumpIncompleteLine(const QString &line, OutputFormat format)
+{
+ append(line, charFormat(format));
+ d->incompleteLine.first.append(line);
+ d->incompleteLine.second = format;
+}
+
void OutputFormatter::handleLink(const QString &href)
{
Q_UNUSED(href)
@@ -147,7 +166,9 @@ void OutputFormatter::handleLink(const QString &href)
void OutputFormatter::clear()
{
d->prependCarriageReturn = false;
+ d->incompleteLine.first.clear();
plainTextEdit()->clear();
+ reset();
}
void OutputFormatter::setBoldFontEnabled(bool enabled)
@@ -160,11 +181,20 @@ void OutputFormatter::setBoldFontEnabled(bool enabled)
void OutputFormatter::flush()
{
+ if (!d->incompleteLine.first.isEmpty())
+ flushIncompleteLine();
d->escapeCodeHandler.endFormatScope();
+ reset();
}
void OutputFormatter::appendMessage(const QString &text, OutputFormat format)
{
+ // If we have an existing incomplete line and its format is different from this one,
+ // then we consider the two messages unrelated. We re-insert the previous incomplete line,
+ // possibly formatted now, and start from scratch with the new input.
+ if (!d->incompleteLine.first.isEmpty() && d->incompleteLine.second != format)
+ flushIncompleteLine();
+
QString out = text;
if (d->prependCarriageReturn) {
d->prependCarriageReturn = false;
@@ -175,7 +205,35 @@ void OutputFormatter::appendMessage(const QString &text, OutputFormat format)
d->prependCarriageReturn = true;
out.chop(1);
}
- doAppendMessage(out, format);
+
+ // If the input is a single incomplete line, we do not forward it to the specialized
+ // formatting code, but simply dump it as-is. Once it becomes complete or it needs to
+ // be flushed for other reasons, we remove the unformatted part and re-insert it, this
+ // time with proper formatting.
+ if (!out.contains('\n')) {
+ dumpIncompleteLine(out, format);
+ return;
+ }
+
+ // We have at least one complete line, so let's remove the previously dumped
+ // incomplete line and prepend it to the first line of our new input.
+ if (!d->incompleteLine.first.isEmpty()) {
+ clearLastLine();
+ out.prepend(d->incompleteLine.first);
+ d->incompleteLine.first.clear();
+ }
+
+ // Forward all complete lines to the specialized formatting code, and handle a
+ // potential trailing incomplete line the same way as above.
+ for (int startPos = 0; ;) {
+ const int eolPos = out.indexOf('\n', startPos);
+ if (eolPos == -1) {
+ dumpIncompleteLine(out.mid(startPos), format);
+ break;
+ }
+ doAppendMessage(out.mid(startPos, eolPos - startPos + 1), format);
+ startPos = eolPos + 1;
+ }
}
} // namespace Utils