Markdown importer: properly set hyperlinks

The "title" in markdown is the tooltip, not the name attribute of
a link. Also, tell the char format that it's an anchor.

Change-Id: I2978848ec6705fe16376d6fe17f31007cce4b801
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Giuseppe D'Angelo 2020-01-31 14:32:24 +01:00
parent c29fac453f
commit 2d265dce58
4 changed files with 104 additions and 2 deletions

View File

@ -399,8 +399,10 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det)
MD_SPAN_A_DETAIL *detail = static_cast<MD_SPAN_A_DETAIL *>(det);
QString url = QString::fromUtf8(detail->href.text, int(detail->href.size));
QString title = QString::fromUtf8(detail->title.text, int(detail->title.size));
charFmt.setAnchor(true);
charFmt.setAnchorHref(url);
charFmt.setAnchorNames(QStringList(title));
if (!title.isEmpty())
charFmt.setToolTip(title);
charFmt.setForeground(m_palette.link());
qCDebug(lcMD) << "anchor" << url << title;
} break;

View File

@ -56,10 +56,13 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcMDW, "qt.text.markdown.writer")
static const QChar Space = QLatin1Char(' ');
static const QChar Tab = QLatin1Char('\t');
static const QChar Newline = QLatin1Char('\n');
static const QChar CarriageReturn = QLatin1Char('\r');
static const QChar LineBreak = QChar(0x2028);
static const QChar DoubleQuote = QLatin1Char('"');
static const QChar Backtick = QLatin1Char('`');
static const QChar Backslash = QLatin1Char('\\');
static const QChar Period = QLatin1Char('.');
QTextMarkdownWriter::QTextMarkdownWriter(QTextStream &stream, QTextDocument::MarkdownFeatures features)
@ -291,6 +294,72 @@ static void maybeEscapeFirstChar(QString &s)
}
}
struct LineEndPositions {
const QChar *lineEnd;
const QChar *nextLineBegin;
};
static LineEndPositions findLineEnd(const QChar *begin, const QChar *end)
{
LineEndPositions result{ end, end };
while (begin < end) {
if (*begin == Newline) {
result.lineEnd = begin;
result.nextLineBegin = begin + 1;
break;
} else if (*begin == CarriageReturn) {
result.lineEnd = begin;
result.nextLineBegin = begin + 1;
if (((begin + 1) < end) && begin[1] == Newline)
++result.nextLineBegin;
break;
}
++begin;
}
return result;
}
static bool isBlankLine(const QChar *begin, const QChar *end)
{
while (begin < end) {
if (*begin != Space && *begin != Tab)
return false;
++begin;
}
return true;
}
static QString createLinkTitle(const QString &title)
{
QString result;
result.reserve(title.size() + 2);
result += DoubleQuote;
const QChar *data = title.data();
const QChar *end = data + title.size();
while (data < end) {
const auto lineEndPositions = findLineEnd(data, end);
if (!isBlankLine(data, lineEndPositions.lineEnd)) {
while (data < lineEndPositions.nextLineBegin) {
if (*data == DoubleQuote)
result += Backslash;
result += *data;
++data;
}
}
data = lineEndPositions.nextLineBegin;
}
result += DoubleQuote;
return result;
}
int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ignoreFormat, bool ignoreEmpty)
{
if (block.text().isEmpty() && ignoreEmpty)
@ -445,7 +514,12 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign
col += s.length();
} else if (fmt.hasProperty(QTextFormat::AnchorHref)) {
QString s = QLatin1Char('[') + fragmentText + QLatin1String("](") +
fmt.property(QTextFormat::AnchorHref).toString() + QLatin1Char(')');
fmt.property(QTextFormat::AnchorHref).toString();
if (fmt.hasProperty(QTextFormat::TextToolTip)) {
s += Space;
s += createLinkTitle(fmt.property(QTextFormat::TextToolTip).toString());
}
s += QLatin1Char(')');
if (wrap && col + s.length() > ColumnLimit) {
m_stream << Newline << wrapIndentString;
col = m_wrappedLineIndent;

View File

@ -0,0 +1,25 @@
A series of links.
[link](/uri)
[link]()
[link](/uri "title")
[link](/uri "àbcdè")
[link](/uri "title title \" title title")
[link](/url "title \"&quot;")
[link](/url "title
title
title title
\"title\" title \"
title")
* [link](/url "title")
* [link](/url)
* [link](/url "title
title title")
* nonlink

View File

@ -368,6 +368,7 @@ void tst_QTextMarkdownWriter::rewriteDocument_data()
QTest::newRow("example") << "example.md";
QTest::newRow("list items after headings") << "headingsAndLists.md";
QTest::newRow("word wrap") << "wordWrap.md";
QTest::newRow("links") << "links.md";
}
void tst_QTextMarkdownWriter::rewriteDocument()