From d377d1f3a921488525f6e75c850632f559707747 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 21 Oct 2019 15:14:53 +0200 Subject: Enforce QTextDocument::MarkdownFeature compatibility at compile time We use md4c for parsing markdown. It provides flags to control the feature set that will be supported when parsing particular documents. QTextMarkdownImporter::Feature is a fine-grained set of flags that exactly match the md4c feature flags that we support in Qt so far. QTextMarkdownImporter is a private exported class (new in 5.14). We don't expect the corresponding flags in md4c to change in incompatible ways in the future: the md4c authors have as much respect for avoiding compatibility issues as we do, and likely will only add features, not remove them. We now enforce QTextMarkdownImporter::Features compatibility with QTextDocument::MarkdownFeatures by setting them directly. We check QTextMarkdownImporter::Features compatibility with md4c's #define'd feature flags using static asserts, so that any hypothetical incompatibility would be detected at compile time. The enum conversion from QTextDocument::MarkdownFeatures to QTextMarkdownImporter::Features is moved to a new QTextMarkdownImporter constructor; thus the conversions from QTextDocument::MarkdownFeatures to QTextMarkdownImporter::Features, and then to unsigned (in QTextMarkdownImporter::import()) are adjacent in the same private class implementation. If incompatibility ever occurred, we would need to replace one or both of those with another suitable conversion function. Change-Id: I0bf8a21eb7559df1d38406b948ef657f9060c67b Reviewed-by: Vitaly Fanaskov --- src/gui/text/qtextdocument.cpp | 2 +- src/gui/text/qtextdocument.h | 1 - src/gui/text/qtextmarkdownimporter.cpp | 21 +++++++++++++++++++++ src/gui/text/qtextmarkdownimporter_p.h | 34 ++++++++++++++++++---------------- 4 files changed, 40 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 22c249d604..e94f635651 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -3420,7 +3420,7 @@ QString QTextDocument::toMarkdown(QTextDocument::MarkdownFeatures features) cons #if QT_CONFIG(textmarkdownreader) void QTextDocument::setMarkdown(const QString &markdown, QTextDocument::MarkdownFeatures features) { - QTextMarkdownImporter(static_cast(int(features))).import(this, markdown); + QTextMarkdownImporter(features).import(this, markdown); } #endif diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index 2fadc40cd2..7d46238257 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -152,7 +152,6 @@ public: #endif #if QT_CONFIG(textmarkdownwriter) || QT_CONFIG(textmarkdownreader) - // Must be in sync with QTextMarkdownImporter::Features, should be in sync with #define MD_FLAG_* in md4c enum MarkdownFeature { MarkdownNoHTML = 0x0020 | 0x0040, MarkdownDialectCommonMark = 0, diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp index fe7e422923..87ade1f973 100644 --- a/src/gui/text/qtextmarkdownimporter.cpp +++ b/src/gui/text/qtextmarkdownimporter.cpp @@ -60,6 +60,22 @@ static const QChar Space = QLatin1Char(' '); // TODO maybe eliminate the margins after all views recognize BlockQuoteLevel, CSS can format it, etc. static const int BlockQuoteIndent = 40; // pixels, same as in QTextHtmlParserNode::initializeProperties +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureCollapseWhitespace) == MD_FLAG_COLLAPSEWHITESPACE); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeaturePermissiveATXHeaders) == MD_FLAG_PERMISSIVEATXHEADERS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeaturePermissiveURLAutoLinks) == MD_FLAG_PERMISSIVEURLAUTOLINKS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeaturePermissiveMailAutoLinks) == MD_FLAG_PERMISSIVEEMAILAUTOLINKS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureNoIndentedCodeBlocks) == MD_FLAG_NOINDENTEDCODEBLOCKS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureNoHTMLBlocks) == MD_FLAG_NOHTMLBLOCKS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureNoHTMLSpans) == MD_FLAG_NOHTMLSPANS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureTables) == MD_FLAG_TABLES); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureStrikeThrough) == MD_FLAG_STRIKETHROUGH); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeaturePermissiveWWWAutoLinks) == MD_FLAG_PERMISSIVEWWWAUTOLINKS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeaturePermissiveAutoLinks) == MD_FLAG_PERMISSIVEAUTOLINKS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureTasklists) == MD_FLAG_TASKLISTS); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::FeatureNoHTML) == MD_FLAG_NOHTML); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::DialectCommonMark) == MD_DIALECT_COMMONMARK); +Q_STATIC_ASSERT(int(QTextMarkdownImporter::DialectGitHub) == MD_DIALECT_GITHUB); + // -------------------------------------------------------- // MD4C callback function wrappers @@ -122,6 +138,11 @@ QTextMarkdownImporter::QTextMarkdownImporter(QTextMarkdownImporter::Features fea { } +QTextMarkdownImporter::QTextMarkdownImporter(QTextDocument::MarkdownFeatures features) + : QTextMarkdownImporter(static_cast(int(features))) +{ +} + void QTextMarkdownImporter::import(QTextDocument *doc, const QString &markdown) { MD_PARSER callbacks = { diff --git a/src/gui/text/qtextmarkdownimporter_p.h b/src/gui/text/qtextmarkdownimporter_p.h index 35655aff8a..f450da5eb3 100644 --- a/src/gui/text/qtextmarkdownimporter_p.h +++ b/src/gui/text/qtextmarkdownimporter_p.h @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -67,27 +68,28 @@ class Q_GUI_EXPORT QTextMarkdownImporter { public: enum Feature { - // Must be kept in sync with MD_FLAG_* in md4c.h - FeatureCollapseWhitespace = 0x0001, // MD_FLAG_COLLAPSEWHITESPACE - FeaturePermissiveATXHeaders = 0x0002, // MD_FLAG_PERMISSIVEATXHEADERS - FeaturePermissiveURLAutoLinks = 0x0004, // MD_FLAG_PERMISSIVEURLAUTOLINKS - FeaturePermissiveMailAutoLinks = 0x0008, // MD_FLAG_PERMISSIVEEMAILAUTOLINKS - FeatureNoIndentedCodeBlocks = 0x0010, // MD_FLAG_NOINDENTEDCODEBLOCKS - FeatureNoHTMLBlocks = 0x0020, // MD_FLAG_NOHTMLBLOCKS - FeatureNoHTMLSpans = 0x0040, // MD_FLAG_NOHTMLSPANS - FeatureTables = 0x0100, // MD_FLAG_TABLES - FeatureStrikeThrough = 0x0200, // MD_FLAG_STRIKETHROUGH - FeaturePermissiveWWWAutoLinks = 0x0400, // MD_FLAG_PERMISSIVEWWWAUTOLINKS - FeatureTasklists = 0x0800, // MD_FLAG_TASKLISTS + FeatureCollapseWhitespace = 0x0001, + FeaturePermissiveATXHeaders = 0x0002, + FeaturePermissiveURLAutoLinks = 0x0004, + FeaturePermissiveMailAutoLinks = 0x0008, + FeatureNoIndentedCodeBlocks = 0x0010, + FeatureNoHTMLBlocks = 0x0020, + FeatureNoHTMLSpans = 0x0040, + FeatureTables = 0x0100, + FeatureStrikeThrough = 0x0200, + FeaturePermissiveWWWAutoLinks = 0x0400, + FeatureTasklists = 0x0800, // composite flags - FeaturePermissiveAutoLinks = FeaturePermissiveMailAutoLinks | FeaturePermissiveURLAutoLinks | FeaturePermissiveWWWAutoLinks, // MD_FLAG_PERMISSIVEAUTOLINKS - FeatureNoHTML = FeatureNoHTMLBlocks | FeatureNoHTMLSpans, // MD_FLAG_NOHTML - DialectCommonMark = 0, // MD_DIALECT_COMMONMARK - DialectGitHub = FeaturePermissiveAutoLinks | FeatureTables | FeatureStrikeThrough | FeatureTasklists // MD_DIALECT_GITHUB + FeaturePermissiveAutoLinks = FeaturePermissiveMailAutoLinks + | FeaturePermissiveURLAutoLinks | FeaturePermissiveWWWAutoLinks, + FeatureNoHTML = QTextDocument::MarkdownNoHTML, + DialectCommonMark = QTextDocument::MarkdownDialectCommonMark, + DialectGitHub = QTextDocument::MarkdownDialectGitHub }; Q_DECLARE_FLAGS(Features, Feature) QTextMarkdownImporter(Features features); + QTextMarkdownImporter(QTextDocument::MarkdownFeatures features); void import(QTextDocument *doc, const QString &markdown); -- cgit v1.2.3