From c2566a8c56f19b0c704a99b9d22997747e21f82a Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Thu, 10 Oct 2019 13:57:53 +0200 Subject: qdoc: Introduce ignorewords configuration variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QDoc automatically generates hyperlinks for words that qualify for auto-linking. This may become a problem whenever there is a qualified word as a section or a page title, and that word appear in the documentation frequently. For example, 'macOS' is mentioned often and each occurrence is linked, which is unnecessary and sometimes confusing. Provide a way to exclude specified words from being auto-linked. Create a new helper function, DocParser::isAutoLinkString(), to determine whether a string qualifies, and replace duplicated code with calls to that function. Fixes: QTBUG-79135 Change-Id: Ie53fe7ca0692f7b8e10a5f2208df5cd2ee2aab1d Reviewed-by: Topi Reiniƶ --- src/qdoc/config.cpp | 1 + src/qdoc/config.h | 2 + src/qdoc/doc.cpp | 178 ++++++++++++++------------------- src/qdoc/doc/qdoc-manual-qdocconf.qdoc | 34 +++++++ 4 files changed, 113 insertions(+), 102 deletions(-) diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp index 73f29d56a..e7ca42150 100644 --- a/src/qdoc/config.cpp +++ b/src/qdoc/config.cpp @@ -74,6 +74,7 @@ QString ConfigStrings::HOMEPAGE = QStringLiteral("homepage"); QString ConfigStrings::HOMETITLE = QStringLiteral("hometitle"); QString ConfigStrings::IGNOREDIRECTIVES = QStringLiteral("ignoredirectives"); QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens"); +QString ConfigStrings::IGNOREWORDS = QStringLiteral("ignorewords"); QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs"); QString ConfigStrings::IMAGES = QStringLiteral("images"); QString ConfigStrings::INCLUDEPATHS = QStringLiteral("includepaths"); diff --git a/src/qdoc/config.h b/src/qdoc/config.h index 85a12c0b3..451946ecd 100644 --- a/src/qdoc/config.h +++ b/src/qdoc/config.h @@ -217,6 +217,7 @@ struct ConfigStrings static QString HOMETITLE; static QString IGNOREDIRECTIVES; static QString IGNORETOKENS; + static QString IGNOREWORDS; static QString IMAGEDIRS; static QString IMAGES; static QString INCLUDEPATHS; @@ -298,6 +299,7 @@ struct ConfigStrings #define CONFIG_HOMETITLE ConfigStrings::HOMETITLE #define CONFIG_IGNOREDIRECTIVES ConfigStrings::IGNOREDIRECTIVES #define CONFIG_IGNORETOKENS ConfigStrings::IGNORETOKENS +#define CONFIG_IGNOREWORDS ConfigStrings::IGNOREWORDS #define CONFIG_IMAGEDIRS ConfigStrings::IMAGEDIRS #define CONFIG_IMAGES ConfigStrings::IMAGES #define CONFIG_INCLUDEPATHS ConfigStrings::INCLUDEPATHS diff --git a/src/qdoc/doc.cpp b/src/qdoc/doc.cpp index 9929a2a2a..61838b69c 100644 --- a/src/qdoc/doc.cpp +++ b/src/qdoc/doc.cpp @@ -426,6 +426,7 @@ public: static QStringList exampleDirs; static QStringList sourceFiles; static QStringList sourceDirs; + static QStringList ignorewords; static bool quoting; private: @@ -470,6 +471,8 @@ private: QString getCode(int cmd, CodeMarker *marker, const QString &argStr = QString()); QString getUnmarkedCode(int cmd); + inline bool isAutoLinkString(const QString &word); + bool isAutoLinkString(const QString &word, int &curPos); bool isBlankLine(); bool isLeftBraceAhead(); bool isLeftBracketAhead(); @@ -517,6 +520,7 @@ QStringList DocParser::exampleFiles; QStringList DocParser::exampleDirs; QStringList DocParser::sourceFiles; QStringList DocParser::sourceDirs; +QStringList DocParser::ignorewords; bool DocParser::quoting = false; /*! @@ -1364,63 +1368,17 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate, pos = backslashPos; } } + } else if (isAutoLinkString(cmdStr)) { + appendWord(cmdStr); } else { - int curPos = 0; - int numUppercase = 0; - int numLowercase = 0; - int numStrangeSymbols = 0; - - while (curPos < cmdStr.size()) { - unsigned char latin1Ch = cmdStr.at(curPos).toLatin1(); - if (islower(latin1Ch)) { - ++numLowercase; - ++curPos; - } else if (isupper(latin1Ch)) { - ++numUppercase; - ++curPos; - } else if (isdigit(latin1Ch)) { - if (curPos > 0) - ++curPos; - else - break; - } else if (latin1Ch == '_' || latin1Ch == '@') { - ++numStrangeSymbols; - ++curPos; - } else if ((latin1Ch == ':') && (curPos < cmdStr.size() - 1) - && (cmdStr.at(curPos + 1) == QLatin1Char(':'))) { - ++numStrangeSymbols; - curPos += 2; - } else if (latin1Ch == '(') { - if (curPos > 0) { - if ((curPos < cmdStr.size() - 1) - && (cmdStr.at(curPos + 1) == QLatin1Char(')'))) { - ++numStrangeSymbols; - pos += 2; - break; - } else { - // ### handle functions with signatures - // and function calls - break; - } - } else { - break; - } - } else { - break; - } - } - if ((numUppercase >= 1 && numLowercase >= 2) || numStrangeSymbols > 0) { - appendWord(cmdStr); - } else { - if (!cmdStr.endsWith("propertygroup")) { - // The QML and JS property group commands are no longer required - // for grouping QML and JS properties. They are allowed but ignored. - location().warning(tr("Unknown command '\\%1'").arg(cmdStr), - detailsUnknownCommand(metaCommandSet, cmdStr)); - } - enterPara(); - append(Atom::UnknownCommand, cmdStr); + if (!cmdStr.endsWith("propertygroup")) { + // The QML and JS property group commands are no longer required + // for grouping QML and JS properties. They are allowed but ignored. + location().warning(tr("Unknown command '\\%1'").arg(cmdStr), + detailsUnknownCommand(metaCommandSet,cmdStr)); } + enterPara(); + append(Atom::UnknownCommand, cmdStr); } } } // case '\\' (qdoc markup command) @@ -1505,50 +1463,7 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate, if (newWord) { int startPos = pos; - int numInternalUppercase = 0; - int numLowercase = 0; - int numStrangeSymbols = 0; - - while (pos < len) { - unsigned char latin1Ch = input_.at(pos).toLatin1(); - if (islower(latin1Ch)) { - ++numLowercase; - ++pos; - } else if (isupper(latin1Ch)) { - if (pos > startPos) - ++numInternalUppercase; - ++pos; - } else if (isdigit(latin1Ch)) { - if (pos > startPos) - ++pos; - else - break; - } else if (latin1Ch == '_' || latin1Ch == '@') { - ++numStrangeSymbols; - ++pos; - } else if (latin1Ch == ':' && pos < len - 1 - && input_.at(pos + 1) == QLatin1Char(':')) { - ++numStrangeSymbols; - pos += 2; - } else if (latin1Ch == '(') { - if (pos > startPos) { - if (pos < len - 1 && input_.at(pos + 1) == QLatin1Char(')')) { - ++numStrangeSymbols; - pos += 2; - break; - } else { - // ### handle functions with signatures - // and function calls - break; - } - } else { - break; - } - } else { - break; - } - } - + bool autolink = isAutoLinkString(input_, pos); if (pos == startPos) { if (!ch.isSpace()) { appendChar(ch); @@ -1556,9 +1471,8 @@ void DocParser::parse(const QString &source, DocPrivate *docPrivate, } } else { QString word = input_.mid(startPos, pos - startPos); - // is word a C++ symbol or an English word? - if ((numInternalUppercase >= 1 && numLowercase >= 2) || numStrangeSymbols > 0) { - if (word.startsWith(QString("__"))) + if (autolink) { + if (ignorewords.contains(word) || word.startsWith(QString("__"))) appendWord(word); else append(Atom::AutoLink, word); @@ -1775,6 +1689,65 @@ bool DocParser::openCommand(int cmd) return ok; } +/*! + Returns \c true if \a word qualifies for auto-linking. +*/ +inline bool DocParser::isAutoLinkString(const QString &word) +{ + int start = 0; + return isAutoLinkString(word, start); +} + +bool DocParser::isAutoLinkString(const QString &word, int &curPos) +{ + int len = word.size(); + int startPos = curPos; + int numUppercase = 0; + int numLowercase = 0; + int numStrangeSymbols = 0; + + while (curPos < len) { + unsigned char latin1Ch = word.at(curPos).toLatin1(); + if (islower(latin1Ch)) { + ++numLowercase; + ++curPos; + } else if (isupper(latin1Ch)) { + if (curPos > startPos) + ++numUppercase; + ++curPos; + } else if (isdigit(latin1Ch)) { + if (curPos > startPos) + ++curPos; + else + break; + } else if (latin1Ch == '_' || latin1Ch == '@') { + ++numStrangeSymbols; + ++curPos; + } else if ((latin1Ch == ':') && + (curPos < len - 1) && + (word.at(curPos + 1) == QLatin1Char(':'))) { + ++numStrangeSymbols; + curPos += 2; + } else if (latin1Ch == '(') { + if (curPos > startPos) { + if ((curPos < len - 1) && + (word.at(curPos + 1) == QLatin1Char(')'))) { + ++numStrangeSymbols; + pos += 2; + break; + } else { + break; + } + } else { + break; + } + } else { + break; + } + } + return ((numUppercase >= 1 && numLowercase >= 2) || numStrangeSymbols > 0); +} + bool DocParser::closeCommand(int endCmd) { if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) { @@ -3049,6 +3022,7 @@ void Doc::initialize(const Config &config) DocParser::exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS); DocParser::sourceFiles = config.getCanonicalPathList(CONFIG_SOURCES); DocParser::sourceDirs = config.getCanonicalPathList(CONFIG_SOURCEDIRS); + DocParser::ignorewords = config.getStringList(CONFIG_IGNOREWORDS); QmlTypeNode::qmlOnly = config.getBool(CONFIG_QMLONLY); QStringMap reverseAliasMap; diff --git a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc b/src/qdoc/doc/qdoc-manual-qdocconf.qdoc index 0f38aabce..534c06a74 100644 --- a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc +++ b/src/qdoc/doc/qdoc-manual-qdocconf.qdoc @@ -105,6 +105,7 @@ \li \l {HTML.footer-variable} {HTML.footer} \li \l {HTML.postheader-variable} {HTML.postheader} \li \l {HTML.style-variable} {HTML.style} + \li \l {ignorewords-variable} {ignorewords} \li \l {imagedirs-variable} {imagedirs} \li \l {images-variable} {images} \li \l {images.fileextensions-variable} {images.fileextensions} @@ -595,6 +596,39 @@ See also \l headerdirs. + \target ignorewords-variable + \section1 ignorewords + + The \c ignorewords variable is used for specifying a list of strings + that QDoc will ignore when resolving hyperlink targets. + + QDoc has an auto-linking feature, where linking is attempted for words + that resemble C++, QML, or JavaScript entities. Specifically, a string + qualifies for auto-linking if it is at least three characters in + length, has no whitespace, and it + + \list + \li is a \e camelCase word, that is, it contains at least one + uppercase character at index greater than zero, or + \li contains the substring \c {()} or \c {::}, or + \li contains at least one special character, \c {@} or \c {_}. + \endlist + + Adding a qualified word to \c ignorewords stops QDoc from linking + that word automatically. For example, if the word \e OpenGL is a + valid link target (a section, \l{page-command}{\\page}, or + \l {externalpage-command}{\\externalpage} title), a hyperlink for + each occurrence can be avoided with + + \badcode + ignorewords += OpenGL + \endcode + + Linking explicitly with \l {l-command}{\\l} continues to work for + ignored words. + + The \c ignorewords variable was introduced in QDoc 5.14. + \target imagedirs-variable \section1 imagedirs -- cgit v1.2.3