QTextMarkdownWriter: Handle lists in blockquotes correctly
But we do not yet handle a blockquote in a list item. Presumably that's less common anyway. We now also continue block-quote prefixes onto blank lines within a block quote, which looks more normal. Task-number: QTBUG-104997 Change-Id: I2b5642cf3a0c81a94444a33f026a02ad53e7e6bb Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Axel Spoerl <axel.spoerl@qt.io> (cherry picked from commit f3e528b97f6836b497953935e1dd27fee134e68a) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
a1413a6ecd
commit
3b4901d2d4
@ -112,17 +112,22 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
|
||||
// suppress needless blank lines, when there will be a big change in block format
|
||||
bool nextIsDifferent = false;
|
||||
bool ending = false;
|
||||
int blockQuoteIndent = 0;
|
||||
int nextBlockQuoteIndent = 0;
|
||||
{
|
||||
QTextFrame::iterator next = iterator;
|
||||
++next;
|
||||
QTextBlockFormat format = iterator.currentBlock().blockFormat();
|
||||
QTextBlockFormat nextFormat = next.currentBlock().blockFormat();
|
||||
blockQuoteIndent = format.intProperty(QTextFormat::BlockQuoteLevel);
|
||||
nextBlockQuoteIndent = nextFormat.intProperty(QTextFormat::BlockQuoteLevel);
|
||||
if (next.atEnd()) {
|
||||
nextIsDifferent = true;
|
||||
ending = true;
|
||||
} else {
|
||||
QTextBlockFormat format = iterator.currentBlock().blockFormat();
|
||||
QTextBlockFormat nextFormat = next.currentBlock().blockFormat();
|
||||
if (nextFormat.indent() != format.indent() ||
|
||||
nextFormat.property(QTextFormat::BlockCodeLanguage) != format.property(QTextFormat::BlockCodeLanguage))
|
||||
nextFormat.property(QTextFormat::BlockCodeLanguage) !=
|
||||
format.property(QTextFormat::BlockCodeLanguage))
|
||||
nextIsDifferent = true;
|
||||
}
|
||||
}
|
||||
@ -139,8 +144,10 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
|
||||
tableRow = cell.row();
|
||||
}
|
||||
} else if (!block.textList()) {
|
||||
if (lastWasList)
|
||||
if (lastWasList) {
|
||||
m_stream << qtmw_Newline;
|
||||
m_linePrefixWritten = false;
|
||||
}
|
||||
}
|
||||
int endingCol = writeBlock(block, !table, table && tableRow == 0,
|
||||
nextIsDifferent && !block.textList());
|
||||
@ -164,8 +171,16 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame)
|
||||
} else if (endingCol > 0) {
|
||||
if (block.textList() || block.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) {
|
||||
m_stream << qtmw_Newline;
|
||||
if (block.textList()) {
|
||||
m_stream << m_linePrefix;
|
||||
m_linePrefixWritten = true;
|
||||
}
|
||||
} else {
|
||||
m_stream << qtmw_Newline << qtmw_Newline;
|
||||
m_stream << qtmw_Newline;
|
||||
if (nextBlockQuoteIndent < blockQuoteIndent)
|
||||
setLinePrefixForBlockQuote(nextBlockQuoteIndent);
|
||||
m_stream << m_linePrefix;
|
||||
m_stream << qtmw_Newline;
|
||||
m_doubleNewlineWritten = true;
|
||||
}
|
||||
}
|
||||
@ -211,6 +226,16 @@ QTextMarkdownWriter::ListInfo QTextMarkdownWriter::listInfo(QTextList *list)
|
||||
return m_listInfo.value(list);
|
||||
}
|
||||
|
||||
void QTextMarkdownWriter::setLinePrefixForBlockQuote(int level)
|
||||
{
|
||||
m_linePrefix.clear();
|
||||
if (level > 0) {
|
||||
m_linePrefix.reserve(level * 2);
|
||||
for (int i = 0; i < level; ++i)
|
||||
m_linePrefix += u"> ";
|
||||
}
|
||||
}
|
||||
|
||||
static int nearestWordWrapIndex(const QString &s, int before)
|
||||
{
|
||||
before = qMin(before, s.size());
|
||||
@ -336,10 +361,20 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
|
||||
const bool codeBlock = blockFmt.hasProperty(QTextFormat::BlockCodeFence) ||
|
||||
blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).size() > 0 ||
|
||||
blockFmt.nonBreakableLines();
|
||||
const int blockQuoteLevel = blockFmt.intProperty(QTextFormat::BlockQuoteLevel);
|
||||
if (m_fencedCodeBlock && !codeBlock) {
|
||||
m_stream << m_linePrefix << m_codeBlockFence << qtmw_Newline;
|
||||
m_fencedCodeBlock = false;
|
||||
m_codeBlockFence.clear();
|
||||
m_linePrefixWritten = m_linePrefix.size() > 0;
|
||||
}
|
||||
m_linePrefix.clear();
|
||||
if (!blockFmt.headingLevel() && blockQuoteLevel > 0) {
|
||||
setLinePrefixForBlockQuote(blockQuoteLevel);
|
||||
if (!m_linePrefixWritten) {
|
||||
m_stream << m_linePrefix;
|
||||
m_linePrefixWritten = true;
|
||||
}
|
||||
}
|
||||
if (block.textList()) { // it's a list-item
|
||||
auto fmt = block.textList()->format();
|
||||
@ -416,31 +451,25 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
|
||||
if (blockFmt.hasProperty(QTextFormat::BlockIndent))
|
||||
m_codeBlockFence = QString(m_wrappedLineIndent, qtmw_Space) + m_codeBlockFence;
|
||||
// A block quote can contain an indented code block, but not vice-versa.
|
||||
m_stream << m_linePrefix << m_codeBlockFence
|
||||
<< blockFmt.stringProperty(QTextFormat::BlockCodeLanguage) << qtmw_Newline;
|
||||
m_stream << m_codeBlockFence << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage)
|
||||
<< qtmw_Newline << m_linePrefix;
|
||||
m_fencedCodeBlock = true;
|
||||
}
|
||||
wrap = false;
|
||||
} else if (!blockFmt.indent()) {
|
||||
m_wrappedLineIndent = 0;
|
||||
m_linePrefix.clear();
|
||||
if (blockFmt.hasProperty(QTextFormat::BlockQuoteLevel)) {
|
||||
int level = blockFmt.intProperty(QTextFormat::BlockQuoteLevel);
|
||||
QString quoteMarker = QStringLiteral("> ");
|
||||
m_linePrefix.reserve(level * 2);
|
||||
for (int i = 0; i < level; ++i)
|
||||
m_linePrefix += quoteMarker;
|
||||
}
|
||||
if (blockFmt.hasProperty(QTextFormat::BlockCodeLanguage)) {
|
||||
// A block quote can contain an indented code block, but not vice-versa.
|
||||
m_linePrefix += QString(4, qtmw_Space);
|
||||
m_indentedCodeBlock = true;
|
||||
}
|
||||
if (!m_linePrefixWritten) {
|
||||
m_stream << m_linePrefix;
|
||||
m_linePrefixWritten = true;
|
||||
}
|
||||
}
|
||||
if (blockFmt.headingLevel())
|
||||
m_stream << QByteArray(blockFmt.headingLevel(), '#') << ' ';
|
||||
else
|
||||
m_stream << m_linePrefix;
|
||||
|
||||
QString wrapIndentString = m_linePrefix + QString(m_wrappedLineIndent, qtmw_Space);
|
||||
// It would be convenient if QTextStream had a lineCharPos() accessor,
|
||||
@ -584,6 +613,10 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
|
||||
i = j + 1;
|
||||
}
|
||||
} else {
|
||||
if (!m_linePrefixWritten && col == wrapIndentString.size()) {
|
||||
m_stream << m_linePrefix;
|
||||
col += m_linePrefix.size();
|
||||
}
|
||||
m_stream << markers << fragmentText;
|
||||
col += markers.size() + fragmentText.size();
|
||||
}
|
||||
@ -615,6 +648,7 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
|
||||
}
|
||||
if (missedBlankCodeBlockLine)
|
||||
m_stream << qtmw_Newline;
|
||||
m_linePrefixWritten = false;
|
||||
return col;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ private:
|
||||
};
|
||||
|
||||
ListInfo listInfo(QTextList *list);
|
||||
void setLinePrefixForBlockQuote(int level);
|
||||
|
||||
private:
|
||||
QTextStream &m_stream;
|
||||
@ -53,6 +54,7 @@ private:
|
||||
int m_wrappedLineIndent = 0;
|
||||
int m_lastListIndent = 1;
|
||||
bool m_doubleNewlineWritten = false;
|
||||
bool m_linePrefixWritten = false;
|
||||
bool m_indentedCodeBlock = false;
|
||||
bool m_fencedCodeBlock = false;
|
||||
};
|
||||
|
@ -8,17 +8,17 @@ MacFarlane writes:
|
||||
|
||||
> What distinguishes Markdown from many other lightweight markup syntaxes,
|
||||
> which are often easier to write, is its readability. As Gruber writes:
|
||||
|
||||
>
|
||||
> > The overriding design goal for Markdown's formatting syntax is to make it
|
||||
> > as readable as possible. The idea is that a Markdown-formatted document should
|
||||
> > be publishable as-is, as plain text, without looking like it's been marked up
|
||||
> > with tags or formatting instructions. (
|
||||
> > <http://daringfireball.net/projects/markdown/> )
|
||||
|
||||
>
|
||||
> The point can be illustrated by comparing a sample of AsciiDoc with an
|
||||
> equivalent sample of Markdown. Here is a sample of AsciiDoc from the AsciiDoc
|
||||
> manual:
|
||||
|
||||
>
|
||||
> ```AsciiDoc
|
||||
> 1. List item one.
|
||||
> +
|
||||
|
@ -0,0 +1,14 @@
|
||||
What if we have a quotation containing a list?
|
||||
|
||||
> First some quoted text, and then a list:
|
||||
>
|
||||
> - one
|
||||
> - two is longer and has enough words to form a paragraph with text continuing
|
||||
> onto the next line
|
||||
>
|
||||
> enough of that, let's try a numbered list
|
||||
>
|
||||
> 1. List item one
|
||||
> 2. List item two is longer and has enough words to form a paragraph with
|
||||
> text continuing onto the next line.
|
||||
>
|
@ -0,0 +1,6 @@
|
||||
What if we have a list item containing a block quote?
|
||||
|
||||
- one
|
||||
- > two is longer and has enough words to form a paragraph with text continuing
|
||||
> onto the next line
|
||||
|
@ -530,6 +530,8 @@ void tst_QTextMarkdownWriter::rewriteDocument_data()
|
||||
QTest::addColumn<QString>("inputFile");
|
||||
|
||||
QTest::newRow("block quotes") << "blockquotes.md";
|
||||
QTest::newRow("block quotes with lists") << "blockquotesWithLists.md";
|
||||
// QTest::newRow("list item with block quote") << "listItemWithBlockquote.md"; // not supported for now
|
||||
QTest::newRow("example") << "example.md";
|
||||
QTest::newRow("list items after headings") << "headingsAndLists.md";
|
||||
QTest::newRow("word wrap") << "wordWrap.md";
|
||||
@ -629,6 +631,8 @@ void tst_QTextMarkdownWriter::fromHtml()
|
||||
}
|
||||
#endif
|
||||
|
||||
output = output.trimmed();
|
||||
expectedOutput = expectedOutput.trimmed();
|
||||
if (output != expectedOutput && (isMainFontFixed() || isFixedFontProportional()))
|
||||
QEXPECT_FAIL("", "fixed main font or proportional fixed font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expectedOutput);
|
||||
|
Loading…
x
Reference in New Issue
Block a user