diff options
author | Luca Di Sera <luca.disera@qt.io> | 2023-12-22 15:27:06 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-12-29 19:42:02 +0000 |
commit | c0c5fa0a33055ee17c5146558bbe6a97639ce029 (patch) | |
tree | a1eb2fe59df179e4d2ebbce9f16b3edf1b2969a9 | |
parent | 45f1d7eea495ae89e18792b3af5580c1743b8116 (diff) |
QDoc: Allow brace enclosed arguments in compareswith
QDoc allows documenting the ordering relationship between a type and one
or more other types through the use of the
"\compareswith"/"\endcompareswith" command pair.
"\compareswith" takes one word to use as the comparison category, such
as "strong", and then a space separated list of words that are used to
specify the compared with types.
Due to the list being space separated, it is impossible to correctly
provide to the command a type that contains a space in its name.
For example, "unsigned long" would be always be treated as the two types
"unsigned" and "long".
To avoid the issue, the command now interprets brace-enclosed elements
as a single type, similar to how other commands in QDoc work.
For example, "{unsigned long}" will now be treated as the single type
"unsigned long".
To allow for this, "processComparesWithCommand", which takes care of
parsing the arguments passed to a "\compareswith" command, in
"docparser.cpp", was modified to parse the arguments so that
brace-enclosed arguments are treated accordingly ot the new
specification.
While QDoc already has certain procedures to parse a similar structure,
such as `DocParser::getBracedArgument`, they are coupled to the state of
the parser and are unusable in a more general situation.
In particular, due to certain structural limits and the current
implementation of the "\compareswith" command, especially its two
phase parsing to handle continued lines, the available procedures are
unusable without multiple structural changes.
Hence, an inline parser that approximates the usual braced behavior,
with some simplifications, was implemented directly in
"processComparesWithCommand".
After the command is processed and the a list of types is extracted, the
list is stored back as a string that is later extracted and used during
the generation of the output documentation set.
Previously, the list of types was joined as a space separated string,
later reconstructed during the generation by splitting on spaces.
This makes it so the original collection of words in a braced argument
that has spaces in it is lost in the translation. To avoid this, the
current behavior is kept but the list is encoded as a semi-colon
separated string and restored by splitting on semi-colons, so that
meaningful spaces are preserved.
The semi-colon character was chosen arbitrarily and has no particular
meaning.
The documentation for the "\compareswith" command was updated to reflect
the new behavior.
A simple specimen of brace-enclosed argument was added to
"generatedoutput", QDoc's end-to-end regression testing suite, under
the relevant tests for "\compareswith".
The regression files for "generatedoutput" were regenerated to respect
the changes in the regression sources.
Fixes: QTBUG-120265
Change-Id: I6872d396ae05b831478d79e66bf426f9fb353b69
Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
(cherry picked from commit e66b37641bee97d0e52931ee73d1ba9ac609a7d0)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
6 files changed, 99 insertions, 6 deletions
diff --git a/src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc b/src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc index 7745129f9..49de3e0c4 100644 --- a/src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc +++ b/src/qdoc/qdoc/doc/qdoc-manual-contextcmds.qdoc @@ -214,6 +214,25 @@ considered further details that apply to all types subject to the comparison category argument. + Types that have one or more space in their name, such as + \c{unsigned long}, should be enclosed in braces. + + For example: + + \badcode * + /\1! + ... + \compareswith strong int long {unsigned long} {unsigned int} char + ... + \endcompareswith + ... + \1/ + \endcode + + Argument enclosed in braces have their leading and trailing whitespaces + removed. + For example, \c{unsigned long} and \c{ unsigned long } are equivalent. + The comparison category argument must be one of the following: \include qdoc-manual-contextcmds.qdoc comparison-categories diff --git a/src/qdoc/qdoc/src/qdoc/docparser.cpp b/src/qdoc/qdoc/src/qdoc/docparser.cpp index c790da8db..5ef93e8cb 100644 --- a/src/qdoc/qdoc/src/qdoc/docparser.cpp +++ b/src/qdoc/qdoc/src/qdoc/docparser.cpp @@ -2732,10 +2732,84 @@ bool DocParser::isQuote(const Atom *atom) */ static void processComparesWithCommand(DocPrivate *priv, const Location &location) { + static auto take_while = [](QStringView input, auto predicate) { + QStringView::size_type end{0}; + + while (end < input.size() && std::invoke(predicate, input[end])) + ++end; + + return std::make_tuple(input.sliced(0, end), input.sliced(end)); + }; + + static auto peek = [](QStringView input, QChar c) { + return !input.empty() && input.first() == c; + }; + + static auto skip_one = [](QStringView input) { + if (input.empty()) return std::make_tuple(QStringView{}, input); + else return std::make_tuple(input.sliced(0, 1), input.sliced(1)); + }; + + static auto enclosed = [](QStringView input, QChar open, QChar close) { + if (!peek(input, open)) return std::make_tuple(QStringView{}, input); + + auto [opened, without_open] = skip_one(input); + auto [parsed, remaining] = take_while(without_open, [close](QChar c){ return c != close; }); + + if (remaining.empty()) return std::make_tuple(QStringView{}, input); + + auto [closed, without_close] = skip_one(remaining); + + return std::make_tuple(parsed.trimmed(), without_close); + }; + + static auto one_of = [](auto first, auto second) { + return [first, second](QStringView input) { + auto [parsed, remaining] = std::invoke(first, input); + + if (parsed.empty()) return std::invoke(second, input); + else return std::make_tuple(parsed, remaining); + }; + }; + + static auto collect = [](QStringView input, auto parser) { + QStringList collected{}; + + while (true) { + auto [parsed, remaining] = std::invoke(parser, input); + + if (parsed.empty()) break; + collected.append(parsed.toString()); + + input = remaining; + }; + + return collected; + }; + + static auto spaces = [](QStringView input) { + return take_while(input, [](QChar c){ return c.isSpace(); }); + }; + + static auto word = [](QStringView input) { + return take_while(input, [](QChar c){ return !c.isSpace(); }); + }; + + static auto parse_argument = [](QStringView input) { + auto [_, without_spaces] = spaces(input); + + return one_of( + [](QStringView input){ return enclosed(input, '{', '}'); }, + word + )(without_spaces); + }; + const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)}; Text text = priv->m_text.splitAtFirst(Atom::ComparesLeft); + auto *atom = text.firstAtom(); - QStringList segments{atom->string().split(QLatin1Char(' '), Qt::SkipEmptyParts)}; + QStringList segments = collect(atom->string(), parse_argument); + QString categoryString; if (!segments.isEmpty()) categoryString = segments.takeFirst(); @@ -2755,7 +2829,7 @@ static void processComparesWithCommand(DocPrivate *priv, const Location &locatio // Store cleaned-up type names back into the atom segments.removeDuplicates(); - atom->setString(segments.join(QLatin1Char(' '))); + atom->setString(segments.join(QLatin1Char(';'))); // Add an entry to meta-command map for error handling in CppCodeParser priv->m_metaCommandMap[cmd].append(ArgPair(categoryString, atom->string())); diff --git a/src/qdoc/qdoc/src/qdoc/generator.cpp b/src/qdoc/qdoc/src/qdoc/generator.cpp index a14cf6886..5440bb467 100644 --- a/src/qdoc/qdoc/src/qdoc/generator.cpp +++ b/src/qdoc/qdoc/src/qdoc/generator.cpp @@ -1533,7 +1533,7 @@ bool Generator::generateComparisonList(const Node *node) << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " with "_L1; - const QStringList types{description.firstAtom()->string().split(' '_L1)}; + const QStringList types{description.firstAtom()->string().split(';'_L1)}; for (const auto &name : types) relationshipText << Atom(Atom::AutoLink, name) << Utilities::separator(types.indexOf(name), types.size()); diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.html index 42fe0a9d9..a92815465 100644 --- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.html +++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.html @@ -19,7 +19,7 @@ <div class="table"><table class="alignedsummary" translate="no"> <tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include <ComparesStronglyWithThreeClasses></span></td></tr> </table></div> -<p>This class is <b>strongly comparable</b> with <a href="foo.html" translate="no">Foo</a>, <a href="bar.html" translate="no">Bar</a>, and <a href="baz.html" translate="no">Baz</a>.</p> +<p>This class is <b>strongly comparable</b> with <a href="foo.html" translate="no">Foo</a>, Bar Bar Jinks, and <a href="baz.html" translate="no">Baz</a>.</p> <!-- $$$ComparesStronglyWithThreeClasses-description --> <div class="descr"> <h2 id="details">Detailed Description</h2> diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.xml b/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.xml index ddca0d5ad..8c39c4fa7 100644 --- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.xml +++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/cxx20/comparesstronglywiththreeclasses.xml @@ -5,7 +5,7 @@ <db:productname>cxx20</db:productname> <db:titleabbrev>cxx20 Reference Documentation</db:titleabbrev> <db:abstract> -<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>, <db:link xlink:href="bar.xml">Bar</db:link>, and <db:link xlink:href="baz.xml">Baz</db:link>.</db:para> +<db:para>This class is <db:emphasis role="bold">strongly comparable</db:emphasis> with <db:link xlink:href="foo.xml">Foo</db:link>, Bar Bar Jinks, and <db:link xlink:href="baz.xml">Baz</db:link>.</db:para> </db:abstract> </db:info> <db:variablelist> diff --git a/src/qdoc/qdoc/tests/generatedoutput/testdata/cxx20/classes_with_various_ordering.cpp b/src/qdoc/qdoc/tests/generatedoutput/testdata/cxx20/classes_with_various_ordering.cpp index 9fe053ee6..814fe04a5 100644 --- a/src/qdoc/qdoc/tests/generatedoutput/testdata/cxx20/classes_with_various_ordering.cpp +++ b/src/qdoc/qdoc/tests/generatedoutput/testdata/cxx20/classes_with_various_ordering.cpp @@ -41,7 +41,7 @@ \class ComparesStronglyWithThreeClasses \inmodule TestQDoc - \compareswith strong Foo Bar Baz + \compareswith strong Foo {Bar Bar Jinks} Baz \endcompareswith */ |