Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
xmlgenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 Thibaut Cuvelier
2// Copyright (C) 2021 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4
5#include "xmlgenerator.h"
6
7#include "enumnode.h"
8#include "examplenode.h"
9#include "functionnode.h"
10#include "qdocdatabase.h"
11#include "typedefnode.h"
12
13using namespace Qt::Literals::StringLiterals;
14
15QT_BEGIN_NAMESPACE
16
17const QRegularExpression XmlGenerator::m_funcLeftParen(QStringLiteral("^\\S+(\\‍(.*\\‍))"));
18
19XmlGenerator::XmlGenerator(FileResolver& file_resolver) : Generator(file_resolver) {}
20
21/*!
22 Do not display \brief for QML types, document and collection nodes
23 */
24bool XmlGenerator::hasBrief(const Node *node)
25{
26 return !(node->isQmlType() || node->isPageNode() || node->isCollectionNode());
27}
28
29/*!
30 Determines whether the list atom should be shown with three columns
31 (constant-value-description).
32 */
34{
35 while (atom && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) {
37 return true;
38 atom = atom->next();
39 }
40 return false;
41}
42
43/*!
44 Determines whether the list atom should be shown with just one column (value).
45 */
47{
48 if (atom->type() != Atom::ListLeft || atom->string() != ATOM_LIST_VALUE)
49 return false;
50
51 while (atom && atom->type() != Atom::ListTagRight)
52 atom = atom->next();
53
54 if (atom) {
56 return false;
57 if (!atom->next())
58 return false;
60 }
61 return false;
62}
63
64/*!
65 Header offset depending on the type of the node
66 */
67int XmlGenerator::hOffset(const Node *node)
68{
69 switch (node->nodeType()) {
70 case Node::Namespace:
71 case Node::Class:
72 case Node::Struct:
73 case Node::Union:
74 case Node::Module:
75 return 2;
76 case Node::QmlModule:
78 case Node::QmlType:
79 case Node::Page:
80 case Node::Group:
81 return 1;
82 case Node::Enum:
83 case Node::TypeAlias:
84 case Node::Typedef:
85 case Node::Function:
86 case Node::Property:
87 default:
88 return 3;
89 }
90}
91
92/*!
93 Rewrites the brief of this node depending on its first word.
94 Only for properties and variables (does nothing otherwise).
95 */
96void XmlGenerator::rewritePropertyBrief(const Atom *atom, const Node *relative)
97{
98 if (relative->nodeType() != Node::Property && relative->nodeType() != Node::Variable)
99 return;
100 atom = atom->next();
101 if (!atom || atom->type() != Atom::String)
102 return;
103
104 const QString firstWord =
105 atom->string().toLower().section(' ', 0, 0, QString::SectionSkipEmpty);
106 const QStringList words{ "the", "a", "an", "whether", "which" };
107 if (words.contains(firstWord)) {
108 QString str = QLatin1String("This ")
109 + QLatin1String(relative->nodeType() == Node::Property ? "property" : "variable")
110 + QLatin1String(" holds ") + atom->string().left(1).toLower()
111 + atom->string().mid(1);
112 const_cast<Atom *>(atom)->setString(str);
113 }
114}
115
116/*!
117 Returns the type of this atom as an enumeration.
118 */
120{
121 const auto &name = atom->string();
122 if (name.startsWith(QLatin1String("qml")))
123 return Node::QmlModule;
124 else if (name.startsWith(QLatin1String("groups")))
125 return Node::Group;
126 else
127 return Node::Module;
128}
129
130/*!
131 For images shown in examples, set the image file to the one it
132 will have once the documentation is generated.
133 */
134void XmlGenerator::setImageFileName(const Node *relative, const QString &fileName)
135{
136 if (relative->isExample()) {
137 const auto cen = static_cast<const ExampleNode *>(relative);
138 if (cen->imageFileName().isEmpty()) {
139 auto *en = const_cast<ExampleNode *>(cen);
140 en->setImageFileName(fileName);
141 }
142 }
143}
144
145/*!
146 Handles the differences in lists between list tags and since tags, and
147 returns the content of the list entry \a atom (first member of the pair).
148 It also returns the number of items to skip ahead (second member of the pair).
149 */
150std::pair<QString, int> XmlGenerator::getAtomListValue(const Atom *atom)
151{
152 const Atom *lookAhead = atom->next();
153 if (!lookAhead)
154 return std::pair<QString, int>(QString(), 1);
155
156 QString t = lookAhead->string();
157 lookAhead = lookAhead->next();
158 if (!lookAhead || lookAhead->type() != Atom::ListTagRight)
159 return std::pair<QString, int>(QString(), 1);
160
161 lookAhead = lookAhead->next();
162 int skipAhead;
163 if (lookAhead && lookAhead->type() == Atom::SinceTagLeft) {
164 lookAhead = lookAhead->next();
165 Q_ASSERT(lookAhead && lookAhead->type() == Atom::String);
166 t += QLatin1String(" (since ");
167 const QString sinceString = lookAhead->string();
168 if (sinceString.at(0).isDigit()) {
169 const QString productName = Config::instance().get(CONFIG_PRODUCTNAME).asString();
170 t += productName.isEmpty() ? sinceString : productName + " " + sinceString;
171 } else {
172 t += sinceString;
173 }
174 t += QLatin1String(")");
175 skipAhead = 4;
176 } else {
177 skipAhead = 1;
178 }
179 return std::pair<QString, int>(t, skipAhead);
180}
181
182/*!
183 Parses the table attributes from the given \a atom.
184 This method returns a pair containing the width (%) and
185 the attribute for this table (either "generic" or
186 "borderless").
187 */
188std::pair<QString, QString> XmlGenerator::getTableWidthAttr(const Atom *atom)
189{
190 QString p0, p1;
191 QString attr = "generic";
192 QString width;
193 if (atom->count() > 0) {
194 p0 = atom->string(0);
195 if (atom->count() > 1)
196 p1 = atom->string(1);
197 }
198 if (!p0.isEmpty()) {
199 if (p0 == QLatin1String("borderless"))
200 attr = p0;
201 else if (p0.contains(QLatin1Char('%')))
202 width = p0;
203 }
204 if (!p1.isEmpty()) {
205 if (p1 == QLatin1String("borderless"))
206 attr = p1;
207 else if (p1.contains(QLatin1Char('%')))
208 width = p1;
209 }
210
211 // Many times, in the documentation, there is a space before the % sign:
212 // this breaks the parsing logic above.
213 if (width == QLatin1String("%")) {
214 // The percentage is typically stored in p0, parse it as an int.
215 bool ok = false;
216 int widthPercentage = p0.toInt(&ok);
217 if (ok) {
218 width = QString::number(widthPercentage) + "%";
219 } else {
220 width = {};
221 }
222 }
223
224 return {width, attr};
225}
226
227/*!
228 Registers an anchor reference and returns a unique
229 and cleaned copy of the reference (the one that should be
230 used in the output).
231 To ensure unicity throughout the document, this method
232 uses the \a refMap cache.
233 */
234QString XmlGenerator::registerRef(const QString &ref, bool xmlCompliant)
235{
236 QString cleanRef = Generator::cleanRef(ref, xmlCompliant);
237
238 for (;;) {
239 QString &prevRef = refMap[cleanRef.toLower()];
240 if (prevRef.isEmpty()) {
241 // This reference has never been met before for this document: register it.
242 prevRef = ref;
243 break;
244 } else if (prevRef == ref) {
245 // This exact same reference was already found. This case typically occurs within refForNode.
246 break;
247 }
248 cleanRef += QLatin1Char('x');
249 }
250 return cleanRef;
251}
252
253/*!
254 Generates a clean and unique reference for the given \a node.
255 This reference may depend on the type of the node (typedef,
256 QML signal, etc.)
257 */
259{
260 QString ref;
261 switch (node->nodeType()) {
262 case Node::Enum:
263 ref = node->name() + "-enum";
264 break;
265 case Node::Typedef: {
266 const auto *tdf = static_cast<const TypedefNode *>(node);
267 if (tdf->associatedEnum())
268 return refForNode(tdf->associatedEnum());
269 } Q_FALLTHROUGH();
270 case Node::TypeAlias:
271 ref = node->name() + "-typedef";
272 break;
273 case Node::Function: {
274 const auto fn = static_cast<const FunctionNode *>(node);
275 switch (fn->metaness()) {
277 ref = fn->name() + "-signal";
278 break;
280 ref = fn->name() + "-signal-handler";
281 break;
283 ref = fn->name() + "-method";
284 if (fn->overloadNumber() != 0)
285 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
286 break;
287 default:
288 if (const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty()) {
289 return refForNode(p);
290 } else {
291 ref = fn->name();
292 if (fn->overloadNumber() != 0)
293 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
294 }
295 break;
296 }
297 } break;
298 case Node::SharedComment: {
299 if (!node->isPropertyGroup())
300 break;
301 } Q_FALLTHROUGH();
303 if (node->isAttached())
304 ref = node->name() + "-attached-prop";
305 else
306 ref = node->name() + "-prop";
307 break;
308 case Node::Property:
309 ref = node->name() + "-prop";
310 break;
311 case Node::Variable:
312 ref = node->name() + "-var";
313 break;
314 default:
315 break;
316 }
317 return registerRef(ref);
318}
319
320/*!
321 Construct the link string for the \a node and return it.
322 The \a relative node is used to decide whether the link
323 we are generating is in the same file as the target.
324 Note the relative node can be 0, which pretty much
325 guarantees that the link and the target aren't in the
326 same file.
327 */
328QString XmlGenerator::linkForNode(const Node *node, const Node *relative)
329{
330 if (node == nullptr)
331 return QString();
332 if (!node->url().isNull())
333 return node->url();
334 if (fileBase(node).isEmpty())
335 return QString();
336 if (node->isPrivate())
337 return QString();
338
339 QString fn = fileName(node);
340 if (node->parent() && node->parent()->isQmlType() && node->parent()->isAbstract()) {
343 fn = fileName(Generator::qmlTypeContext());
344 } else if (node->parent()->isInternal() && !noLinkErrors()) {
345 node->doc().location().warning(
346 QStringLiteral("Cannot link to property in internal type '%1'")
347 .arg(node->parent()->name()));
348 return QString();
349 }
350 }
351 }
352
353 QString link = fn;
354
355 if (!node->isPageNode() || node->isPropertyGroup()) {
356 QString ref = refForNode(node);
357 if (relative && fn == fileName(relative) && ref == refForNode(relative))
358 return QString();
359
360 link += QLatin1Char('#');
361 link += ref;
362 }
363
364 /*
365 If the output is going to subdirectories, the
366 two nodes have different output directories if 'node'
367 was read from index.
368 */
369 if (relative && (node != relative)) {
370 if (useOutputSubdirs() && !node->isExternalPage() && node->isIndexNode())
371 link.prepend("../%1/"_L1.arg(node->tree()->physicalModuleName()));
372 }
373 return link;
374}
375
376/*!
377 This function is called for links, i.e. for words that
378 are marked with the qdoc link command. For autolinks
379 that are not marked with the qdoc link command, the
380 getAutoLink() function is called
381
382 It returns the string for a link found by using the data
383 in the \a atom to search the database. It also sets \a node
384 to point to the target node for that link. \a relative points
385 to the node holding the qdoc comment where the link command
386 was found.
387 */
388QString XmlGenerator::getLink(const Atom *atom, const Node *relative, const Node **node)
389{
390 const QString &t = atom->string();
391
392 if (t.isEmpty())
393 return t;
394
395 if (t.at(0) == QChar('h')) {
396 if (t.startsWith("http:") || t.startsWith("https:"))
397 return t;
398 } else if (t.at(0) == QChar('f')) {
399 if (t.startsWith("file:") || t.startsWith("ftp:"))
400 return t;
401 } else if (t.at(0) == QChar('m')) {
402 if (t.startsWith("mailto:"))
403 return t;
404 }
405 return getAutoLink(atom, relative, node);
406}
407
408/*!
409 This function is called for autolinks, i.e. for words that
410 are not marked with the qdoc link command that qdoc has
411 reason to believe should be links.
412
413 It returns the string for a link found by using the data
414 in the \a atom to search the database. It also sets \a node
415 to point to the target node for that link. \a relative points
416 to the node holding the qdoc comment where the link command
417 was found.
418 */
419QString XmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node **node,
420 Node::Genus genus)
421{
422 QString ref;
423
424 *node = m_qdb->findNodeForAtom(atom, relative, ref, genus);
425 if (!(*node))
426 return QString();
427
428 QString link = (*node)->url();
429 if (link.isNull()) {
430 link = linkForNode(*node, relative);
431 } else if (link.isEmpty()) {
432 return link; // Explicit empty url (node is ignored as a link target)
433 }
434 if (!ref.isEmpty()) {
435 qsizetype hashtag = link.lastIndexOf(QChar('#'));
436 if (hashtag != -1)
437 link.truncate(hashtag);
438 link += QLatin1Char('#') + ref;
439 }
440 return link;
441}
442
444{
445 std::pair<QString, QString> anchorPair;
446
447 anchorPair.first = Generator::fileName(node);
448 if (node->isTextPageNode())
449 anchorPair.second = node->title();
450
451 return anchorPair;
452}
453
454/*!
455 Returns a string describing the \a node type.
456 */
457QString XmlGenerator::targetType(const Node *node)
458{
459 if (!node)
460 return QStringLiteral("external");
461
462 switch (node->nodeType()) {
463 case Node::Namespace:
464 return QStringLiteral("namespace");
465 case Node::Class:
466 case Node::Struct:
467 case Node::Union:
468 return QStringLiteral("class");
469 case Node::Page:
470 case Node::Example:
471 return QStringLiteral("page");
472 case Node::Enum:
473 return QStringLiteral("enum");
474 case Node::TypeAlias:
475 return QStringLiteral("alias");
476 case Node::Typedef:
477 return QStringLiteral("typedef");
478 case Node::Property:
479 return QStringLiteral("property");
480 case Node::Function:
481 return QStringLiteral("function");
482 case Node::Variable:
483 return QStringLiteral("variable");
484 case Node::Module:
485 return QStringLiteral("module");
486 default:
487 break;
488 }
489 return QString();
490}
491
492QT_END_NAMESPACE
#define ATOM_LIST_VALUE
Definition atom.h:214
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:18
AtomType type() const
Return the type of this atom.
Definition atom.h:149
@ ListTagRight
Definition atom.h:65
@ ListItemRight
Definition atom.h:67
@ ListItemLeft
Definition atom.h:66
@ String
Definition atom.h:92
@ ListLeft
Definition atom.h:62
@ ListRight
Definition atom.h:68
@ SinceTagLeft
Definition atom.h:87
const Atom * next() const
Return the next atom in the atom list.
Definition atom.h:146
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:90
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
This node is used to represent any kind of function being documented.
const PropertyNode * primaryAssociatedProperty() const
Returns the primary associated property, if this is an access function for one or more properties.
Metaness metaness() const
Generator(FileResolver &file_resolver)
Constructs the generator base class.
Definition generator.cpp:76
QDocDatabase * m_qdb
Definition generator.h:197
static bool noLinkErrors()
Definition generator.h:64
static bool matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
static QmlTypeNode * qmlTypeContext()
Definition generator.h:70
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:271
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:146
virtual bool isAbstract() const
Returns true if the ClassNode or QmlTypeNode is marked abstract.
Definition node.h:169
NodeType
An unsigned char value that identifies an object as a particular subclass of Node.
Definition node.h:54
@ Variable
Definition node.h:69
@ Module
Definition node.h:71
@ Struct
Definition node.h:58
@ QmlModule
Definition node.h:73
@ Typedef
Definition node.h:66
@ QmlValueType
Definition node.h:75
@ Function
Definition node.h:65
@ TypeAlias
Definition node.h:67
@ Union
Definition node.h:59
@ Page
Definition node.h:61
@ Group
Definition node.h:70
@ Enum
Definition node.h:62
@ QmlProperty
Definition node.h:74
@ QmlType
Definition node.h:72
@ SharedComment
Definition node.h:76
@ Namespace
Definition node.h:56
@ Property
Definition node.h:68
@ Class
Definition node.h:57
@ Example
Definition node.h:63
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:155
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition node.cpp:850
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:182
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:187
virtual bool isAttached() const
Returns true if the QML property or QML method node is marked as attached.
Definition node.h:176
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:244
NodeType nodeType() const
Returns this node's type.
Definition node.h:121
Genus
An unsigned char value that specifies whether the Node represents a C++ element, a QML element,...
Definition node.h:81
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
Definition node.h:185
LinkType
An unsigned char value that probably should be moved out of the Node base class.
Definition node.h:112
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:178
bool isExample() const
Returns true if the node type is Example.
Definition node.h:133
bool inherits(Aggregate *type)
Returns true if this QML type inherits type.
std::pair< QString, QString > anchorForNode(const Node *node)
static bool isThreeColumnEnumValueTable(const Atom *atom)
Determines whether the list atom should be shown with three columns (constant-value-description).
QString getLink(const Atom *atom, const Node *relative, const Node **node)
This function is called for links, i.e.
static Node::NodeType typeFromString(const Atom *atom)
Returns the type of this atom as an enumeration.
QString refForNode(const Node *node)
Generates a clean and unique reference for the given node.
static bool isOneColumnValueTable(const Atom *atom)
Determines whether the list atom should be shown with just one column (value).
QString linkForNode(const Node *node, const Node *relative)
Construct the link string for the node and return it.
static void rewritePropertyBrief(const Atom *atom, const Node *relative)
Rewrites the brief of this node depending on its first word.
static int hOffset(const Node *node)
Header offset depending on the type of the node.
static bool hasBrief(const Node *node)
Do not display.
XmlGenerator(FileResolver &file_resolver)
QString getAutoLink(const Atom *atom, const Node *relative, const Node **node, Node::Genus=Node::DontCare)
This function is called for autolinks, i.e.
#define CONFIG_PRODUCTNAME
Definition config.h:375