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 if (lookAhead->string().at(0).isDigit())
168 t += QLatin1String("Qt ");
169 t += lookAhead->string() + QLatin1String(")");
170 skipAhead = 4;
171 } else {
172 skipAhead = 1;
173 }
174 return std::pair<QString, int>(t, skipAhead);
175}
176
177/*!
178 Parses the table attributes from the given \a atom.
179 This method returns a pair containing the width (%) and
180 the attribute for this table (either "generic" or
181 "borderless").
182 */
183std::pair<QString, QString> XmlGenerator::getTableWidthAttr(const Atom *atom)
184{
185 QString p0, p1;
186 QString attr = "generic";
187 QString width;
188 if (atom->count() > 0) {
189 p0 = atom->string(0);
190 if (atom->count() > 1)
191 p1 = atom->string(1);
192 }
193 if (!p0.isEmpty()) {
194 if (p0 == QLatin1String("borderless"))
195 attr = p0;
196 else if (p0.contains(QLatin1Char('%')))
197 width = p0;
198 }
199 if (!p1.isEmpty()) {
200 if (p1 == QLatin1String("borderless"))
201 attr = p1;
202 else if (p1.contains(QLatin1Char('%')))
203 width = p1;
204 }
205
206 // Many times, in the documentation, there is a space before the % sign:
207 // this breaks the parsing logic above.
208 if (width == QLatin1String("%")) {
209 // The percentage is typically stored in p0, parse it as an int.
210 bool ok = false;
211 int widthPercentage = p0.toInt(&ok);
212 if (ok) {
213 width = QString::number(widthPercentage) + "%";
214 } else {
215 width = {};
216 }
217 }
218
219 return {width, attr};
220}
221
222/*!
223 Registers an anchor reference and returns a unique
224 and cleaned copy of the reference (the one that should be
225 used in the output).
226 To ensure unicity throughout the document, this method
227 uses the \a refMap cache.
228 */
229QString XmlGenerator::registerRef(const QString &ref, bool xmlCompliant)
230{
231 QString cleanRef = Generator::cleanRef(ref, xmlCompliant);
232
233 for (;;) {
234 QString &prevRef = refMap[cleanRef.toLower()];
235 if (prevRef.isEmpty()) {
236 // This reference has never been met before for this document: register it.
237 prevRef = ref;
238 break;
239 } else if (prevRef == ref) {
240 // This exact same reference was already found. This case typically occurs within refForNode.
241 break;
242 }
243 cleanRef += QLatin1Char('x');
244 }
245 return cleanRef;
246}
247
248/*!
249 Generates a clean and unique reference for the given \a node.
250 This reference may depend on the type of the node (typedef,
251 QML signal, etc.)
252 */
254{
255 QString ref;
256 switch (node->nodeType()) {
257 case Node::Enum:
258 ref = node->name() + "-enum";
259 break;
260 case Node::Typedef: {
261 const auto *tdf = static_cast<const TypedefNode *>(node);
262 if (tdf->associatedEnum())
263 return refForNode(tdf->associatedEnum());
264 } Q_FALLTHROUGH();
265 case Node::TypeAlias:
266 ref = node->name() + "-typedef";
267 break;
268 case Node::Function: {
269 const auto fn = static_cast<const FunctionNode *>(node);
270 switch (fn->metaness()) {
272 ref = fn->name() + "-signal";
273 break;
275 ref = fn->name() + "-signal-handler";
276 break;
278 ref = fn->name() + "-method";
279 if (fn->overloadNumber() != 0)
280 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
281 break;
282 default:
283 if (const auto *p = fn->primaryAssociatedProperty(); p && fn->doc().isEmpty()) {
284 return refForNode(p);
285 } else {
286 ref = fn->name();
287 if (fn->overloadNumber() != 0)
288 ref += QLatin1Char('-') + QString::number(fn->overloadNumber());
289 }
290 break;
291 }
292 } break;
293 case Node::SharedComment: {
294 if (!node->isPropertyGroup())
295 break;
296 } Q_FALLTHROUGH();
298 if (node->isAttached())
299 ref = node->name() + "-attached-prop";
300 else
301 ref = node->name() + "-prop";
302 break;
303 case Node::Property:
304 ref = node->name() + "-prop";
305 break;
306 case Node::Variable:
307 ref = node->name() + "-var";
308 break;
309 default:
310 break;
311 }
312 return registerRef(ref);
313}
314
315/*!
316 Construct the link string for the \a node and return it.
317 The \a relative node is used to decide whether the link
318 we are generating is in the same file as the target.
319 Note the relative node can be 0, which pretty much
320 guarantees that the link and the target aren't in the
321 same file.
322 */
323QString XmlGenerator::linkForNode(const Node *node, const Node *relative)
324{
325 if (node == nullptr)
326 return QString();
327 if (!node->url().isNull())
328 return node->url();
329 if (fileBase(node).isEmpty())
330 return QString();
331 if (node->isPrivate())
332 return QString();
333
334 QString fn = fileName(node);
335 if (node->parent() && node->parent()->isQmlType() && node->parent()->isAbstract()) {
338 fn = fileName(Generator::qmlTypeContext());
339 } else if (node->parent()->isInternal() && !noLinkErrors()) {
340 node->doc().location().warning(
341 QStringLiteral("Cannot link to property in internal type '%1'")
342 .arg(node->parent()->name()));
343 return QString();
344 }
345 }
346 }
347
348 QString link = fn;
349
350 if (!node->isPageNode() || node->isPropertyGroup()) {
351 QString ref = refForNode(node);
352 if (relative && fn == fileName(relative) && ref == refForNode(relative))
353 return QString();
354
355 link += QLatin1Char('#');
356 link += ref;
357 }
358
359 /*
360 If the output is going to subdirectories, the
361 two nodes have different output directories if 'node'
362 was read from index.
363 */
364 if (relative && (node != relative)) {
365 if (useOutputSubdirs() && !node->isExternalPage() && node->isIndexNode())
366 link.prepend("../%1/"_L1.arg(node->tree()->physicalModuleName()));
367 }
368 return link;
369}
370
371/*!
372 This function is called for links, i.e. for words that
373 are marked with the qdoc link command. For autolinks
374 that are not marked with the qdoc link command, the
375 getAutoLink() function is called
376
377 It returns the string for a link found by using the data
378 in the \a atom to search the database. It also sets \a node
379 to point to the target node for that link. \a relative points
380 to the node holding the qdoc comment where the link command
381 was found.
382 */
383QString XmlGenerator::getLink(const Atom *atom, const Node *relative, const Node **node)
384{
385 const QString &t = atom->string();
386
387 if (t.isEmpty())
388 return t;
389
390 if (t.at(0) == QChar('h')) {
391 if (t.startsWith("http:") || t.startsWith("https:"))
392 return t;
393 } else if (t.at(0) == QChar('f')) {
394 if (t.startsWith("file:") || t.startsWith("ftp:"))
395 return t;
396 } else if (t.at(0) == QChar('m')) {
397 if (t.startsWith("mailto:"))
398 return t;
399 }
400 return getAutoLink(atom, relative, node);
401}
402
403/*!
404 This function is called for autolinks, i.e. for words that
405 are not marked with the qdoc link command that qdoc has
406 reason to believe should be links.
407
408 It returns the string for a link found by using the data
409 in the \a atom to search the database. It also sets \a node
410 to point to the target node for that link. \a relative points
411 to the node holding the qdoc comment where the link command
412 was found.
413 */
414QString XmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node **node,
415 Node::Genus genus)
416{
417 QString ref;
418
419 *node = m_qdb->findNodeForAtom(atom, relative, ref, genus);
420 if (!(*node))
421 return QString();
422
423 QString link = (*node)->url();
424 if (link.isNull()) {
425 link = linkForNode(*node, relative);
426 } else if (link.isEmpty()) {
427 return link; // Explicit empty url (node is ignored as a link target)
428 }
429 if (!ref.isEmpty()) {
430 qsizetype hashtag = link.lastIndexOf(QChar('#'));
431 if (hashtag != -1)
432 link.truncate(hashtag);
433 link += QLatin1Char('#') + ref;
434 }
435 return link;
436}
437
439{
440 std::pair<QString, QString> anchorPair;
441
442 anchorPair.first = Generator::fileName(node);
443 if (node->isTextPageNode())
444 anchorPair.second = node->title();
445
446 return anchorPair;
447}
448
449/*!
450 Returns a string describing the \a node type.
451 */
452QString XmlGenerator::targetType(const Node *node)
453{
454 if (!node)
455 return QStringLiteral("external");
456
457 switch (node->nodeType()) {
458 case Node::Namespace:
459 return QStringLiteral("namespace");
460 case Node::Class:
461 case Node::Struct:
462 case Node::Union:
463 return QStringLiteral("class");
464 case Node::Page:
465 case Node::Example:
466 return QStringLiteral("page");
467 case Node::Enum:
468 return QStringLiteral("enum");
469 case Node::TypeAlias:
470 return QStringLiteral("alias");
471 case Node::Typedef:
472 return QStringLiteral("typedef");
473 case Node::Property:
474 return QStringLiteral("property");
475 case Node::Function:
476 return QStringLiteral("function");
477 case Node::Variable:
478 return QStringLiteral("variable");
479 case Node::Module:
480 return QStringLiteral("module");
481 default:
482 break;
483 }
484 return QString();
485}
486
487QT_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:196
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:849
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.