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
codemarker.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "codemarker.h"
5
6#include "classnode.h"
7#include "config.h"
8#include "functionnode.h"
9#include "enumnode.h"
10#include "genustypes.h"
11#include "propertynode.h"
13#include "utilities.h"
14
15#include <QtCore/qobjectdefs.h>
16
18
19using namespace Qt::StringLiterals;
20
21QString CodeMarker::s_defaultLang;
22QList<CodeMarker *> CodeMarker::s_markers;
23
24
25/*!
26 When a code marker constructs itself, it puts itself into
27 the static list of code markers. All the code markers in
28 the static list get initialized in initialize(), which is
29 not called until after the qdoc configuration file has
30 been read.
31 */
33{
34 s_markers.prepend(this);
35}
36
37/*!
38 When a code marker destroys itself, it removes itself from
39 the static list of code markers.
40 */
42{
43 s_markers.removeAll(this);
44}
45
46/*!
47 A code market performs no initialization by default. Marker-specific
48 initialization is performed in subclasses.
49 */
51
52/*!
53 Terminating a code marker is trivial.
54 */
56{
57 // nothing.
58}
59
60/*!
61 All the code markers in the static list are initialized
62 here, after the qdoc configuration file has been loaded.
63 */
65{
66 s_defaultLang = Config::instance().get(CONFIG_LANGUAGE).asString();
67 for (const auto &marker : std::as_const(s_markers))
68 marker->initializeMarker();
69}
70
71/*!
72 All the code markers in the static list are terminated here.
73 */
75{
76 for (const auto &marker : std::as_const(s_markers))
77 marker->terminateMarker();
78}
79
80CodeMarker *CodeMarker::markerForCode(const QString &code)
81{
82 CodeMarker *defaultMarker = markerForLanguage(s_defaultLang);
83 if (defaultMarker != nullptr && defaultMarker->recognizeCode(code))
84 return defaultMarker;
85
86 for (const auto &marker : std::as_const(s_markers)) {
87 if (marker->recognizeCode(code))
88 return marker;
89 }
90
91 return defaultMarker;
92}
93
94CodeMarker *CodeMarker::markerForFileName(const QString &fileName)
95{
96 CodeMarker *defaultMarker = markerForLanguage(s_defaultLang);
97 qsizetype dot = -1;
98 while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
99 QString ext = fileName.mid(dot + 1);
100 if (defaultMarker != nullptr && defaultMarker->recognizeExtension(ext))
101 return defaultMarker;
102 for (const auto &marker : std::as_const(s_markers)) {
103 if (marker->recognizeExtension(ext))
104 return marker;
105 }
106 --dot;
107 }
108 return defaultMarker;
109}
110
111CodeMarker *CodeMarker::markerForLanguage(const QString &lang)
112{
113 for (const auto &marker : std::as_const(s_markers)) {
114 if (marker->recognizeLanguage(lang))
115 return marker;
116 }
117 return nullptr;
118}
119
120/*!
121 Returns a string representing the \a node status, set using \preliminary, \since,
122 and \deprecated commands.
123
124 If a string is returned, it is one of:
125 \list
126 \li \c {"preliminary"}
127 \li \c {"since <version_since>, deprecated in <version_deprecated>"}
128 \li \c {"since <version_since>, until <version_deprecated>"}
129 \li \c {"since <version_since>"}
130 \li \c {"since <version_since>, deprecated"}
131 \li \c {"deprecated in <version_deprecated>"}
132 \li \c {"until <version_deprecated>"}
133 \li \c {"deprecated"}
134 \endlist
135
136 If \a node has no related status information, returns std::nullopt.
137*/
139{
140 if (node->isPreliminary())
141 return std::optional(u"preliminary"_s);
142
143 QStringList result;
144 if (const auto &since = node->since(); !since.isEmpty())
145 result << "since %1"_L1.arg(since);
146 if (const auto &deprecated = node->deprecatedSince(); !deprecated.isEmpty()) {
147 if (node->isDeprecated())
148 result << "deprecated in %1"_L1.arg(deprecated);
149 else
150 result << "until %1"_L1.arg(deprecated);
151 } else if (node->isDeprecated()) {
152 result << u"deprecated"_s;
153 }
154
155 return result.isEmpty() ? std::nullopt : std::optional(result.join(u", "_s));
156}
157
158/*!
159 Returns the 'extra' synopsis string for \a node with status information,
160 using a specified section \a style.
161*/
162QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
163{
164 if (style != Section::Summary && style != Section::Details)
165 return {};
166
167 QStringList extra;
168 if (style == Section::Details) {
169 switch (node->nodeType()) {
170 case NodeType::Enum:
171 if (static_cast<const EnumNode *>(node)->isAnonymous())
172 extra << "anonymous";
173 break;
174 case NodeType::Struct:
175 case NodeType::Union:
176 case NodeType::Class:
177 if (static_cast<const ClassNode *>(node)->isAnonymous())
178 extra << "anonymous";
179 break;
180 case NodeType::Function: {
181 const auto *func = static_cast<const FunctionNode *>(node);
182 if (func->isStatic()) {
183 extra << "static";
184 } else if (!func->isNonvirtual()) {
185 if (func->isFinal())
186 extra << "final";
187 if (func->isOverride())
188 extra << "override";
189 if (func->isPureVirtual())
190 extra << "pure";
191 extra << "virtual";
192 }
193
194 if (func->isExplicit()) extra << "explicit";
195 if (func->isConstexpr()) extra << "constexpr";
196 if (auto noexcept_info = func->getNoexcept()) {
197 extra << (QString("noexcept") + (!(*noexcept_info).isEmpty() ? "(...)" : ""));
198 }
199
200 if (func->access() == Access::Protected)
201 extra << "protected";
202 else if (func->access() == Access::Private)
203 extra << "private";
204
205 if (func->isSignal()) {
207 extra << "private";
208 extra << "signal";
209 } else if (func->isSlot())
210 extra << "slot";
211 else if (func->isDefault())
212 extra << "default";
213 else if (func->isInvokable())
214 extra << "invokable";
215 }
216 break;
218 extra << "alias";
219 break;
220 case NodeType::Property: {
221 auto propertyNode = static_cast<const PropertyNode *>(node);
223 extra << "bindable";
224 if (!propertyNode->isWritable())
225 extra << "read-only";
226 }
227 break;
229 auto qmlProperty = static_cast<const QmlPropertyNode *>(node);
230 if (qmlProperty->isDefault())
231 extra << u"default"_s;
232 // Call non-const overloads to ensure attributes are fetched from
233 // associated C++ properties
234 else if (const_cast<QmlPropertyNode *>(qmlProperty)->isReadOnly())
235 extra << u"read-only"_s;
236 else if (const_cast<QmlPropertyNode *>(qmlProperty)->isRequired())
237 extra << u"required"_s;
238 else if (!qmlProperty->defaultValue().isEmpty()) {
239 extra << u"default: "_s + qmlProperty->defaultValue();
240 }
241 break;
242 }
243 default:
244 break;
245 }
246 }
247
248 // Add status for both Summary and Details
249 if (auto status = nodeStatusAsString(node)) {
250 if (!extra.isEmpty())
251 extra.last() += ','_L1;
252 extra << *status;
253 }
254
255 QString extraStr = extra.join(QLatin1Char(' '));
256 if (!extraStr.isEmpty()) {
257 extraStr.prepend(style == Section::Details ? '[' : '(');
258 extraStr.append(style == Section::Details ? ']' : ')');
259 }
260
261 return extraStr;
262}
263
264QString CodeMarker::protect(const QString &str)
265{
266 return Utilities::protect(str);
267}
268
269void CodeMarker::appendProtectedString(QString *output, QStringView str)
270{
271 qsizetype n = str.size();
272 output->reserve(output->size() + n * 2 + 30);
273 const QChar *data = str.constData();
274 for (int i = 0; i != n; ++i) {
275 switch (data[i].unicode()) {
276 case '&':
277 *output += Utilities::samp;
278 break;
279 case '<':
280 *output += Utilities::slt;
281 break;
282 case '>':
283 *output += Utilities::sgt;
284 break;
285 case '"':
286 *output += Utilities::squot;
287 break;
288 default:
289 *output += data[i];
290 }
291 }
292}
293
294QString CodeMarker::typified(const QString &string, bool trailingSpace)
295{
296 QString result;
297 QString pendingWord;
298
299 for (int i = 0; i <= string.size(); ++i) {
300 QChar ch;
301 if (i != string.size())
302 ch = string.at(i);
303
304 QChar lower = ch.toLower();
305 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || ch.digitValue() >= 0
306 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) {
307 pendingWord += ch;
308 } else {
309 if (!pendingWord.isEmpty()) {
310 bool isProbablyType = (pendingWord != QLatin1String("const"));
311 if (isProbablyType)
312 result += QLatin1String("<@type>");
313 result += pendingWord;
314 if (isProbablyType)
315 result += QLatin1String("</@type>");
316 }
317 pendingWord.clear();
318
319 switch (ch.unicode()) {
320 case '\0':
321 break;
322 case '&':
323 result += QLatin1String("&amp;");
324 break;
325 case '<':
326 result += QLatin1String("&lt;");
327 break;
328 case '>':
329 result += QLatin1String("&gt;");
330 break;
331 default:
332 result += ch;
333 }
334 }
335 }
336 if (trailingSpace && string.size()) {
337 if (!string.endsWith(QLatin1Char('*')) && !string.endsWith(QLatin1Char('&')))
338 result += QLatin1Char(' ');
339 }
340 return result;
341}
342
344{
345 QString tag;
346 const QString &name = node->name();
347
348 switch (node->nodeType()) {
350 tag = QLatin1String("@namespace");
351 break;
352 case NodeType::Class:
353 case NodeType::Struct:
354 case NodeType::Union:
355 tag = QLatin1String("@class");
356 break;
357 case NodeType::Enum:
358 tag = QLatin1String("@enum");
359 break;
362 tag = QLatin1String("@typedef");
363 break;
365 tag = QLatin1String("@function");
366 break;
368 tag = QLatin1String("@property");
369 break;
371 tag = QLatin1String("@property");
372 break;
373 case NodeType::Page:
374 tag = QLatin1String("@property");
375 break;
376 default:
377 tag = QLatin1String("@unknown");
378 break;
379 }
380 return (QLatin1Char('<') + tag + QLatin1Char('>') + protect(name) + QLatin1String("</") + tag
381 + QLatin1Char('>'));
382}
383
385{
386 QString tag;
387 if (node->isFunction()) {
388 const auto *fn = static_cast<const FunctionNode *>(node);
389 switch (fn->metaness()) {
391 tag = QLatin1String("@signal");
392 break;
394 tag = QLatin1String("@signalhandler");
395 break;
397 tag = QLatin1String("@method");
398 break;
399 default:
400 tag = QLatin1String("@unknown");
401 break;
402 }
403 } else if (node->isQmlProperty()) {
404 tag = QLatin1String("@property");
405 } else {
406 tag = QLatin1String("@unknown");
407 }
408 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name()) + QLatin1String("</")
409 + tag + QLatin1Char('>');
410}
411
412QString CodeMarker::linkTag(const Node *node, const QString &body)
413{
414 return QLatin1String("<@link node=\"") + Utilities::stringForNode(node) + QLatin1String("\">") + body
415 + QLatin1String("</@link>");
416}
417
418QT_END_NAMESPACE
Access
Definition access.h:11
The ClassNode represents a C++ class.
Definition classnode.h:21
bool isAnonymous() const
Definition classnode.h:58
QString typified(const QString &string, bool trailingSpace=false)
CodeMarker()
When a code marker constructs itself, it puts itself into the static list of code markers.
virtual void initializeMarker()
A code market performs no initialization by default.
static void initialize()
All the code markers in the static list are initialized here, after the qdoc configuration file has b...
QString taggedQmlNode(const Node *node)
virtual ~CodeMarker()
When a code marker destroys itself, it removes itself from the static list of code markers.
virtual void terminateMarker()
Terminating a code marker is trivial.
static void terminate()
All the code markers in the static list are terminated here.
QString linkTag(const Node *node, const QString &body)
QString taggedNode(const Node *node)
bool isAnonymous() const
Definition enumnode.h:34
This node is used to represent any kind of function being documented.
bool isOverride() const
const Parameters & parameters() const
bool isPureVirtual() const override
bool isConstexpr() const
bool isNonvirtual() const
bool isInvokable() const
bool isDefault() const override
Returns true if the QML property node is marked as default.
bool isExplicit() const
bool isSignal() const
bool isStatic() const override
Returns true if the FunctionNode represents a static function.
bool isFinal() const
bool isSlot() const
Metaness metaness() const
This class describes one instance of using the Q_PROPERTY macro.
PropertyType propertyType() const
bool isWritable() const
bool isDefault() const override
Returns true if the QML property node is marked as default.
bool isRequired()
Returns true if this QML property is marked with \required or the corresponding C++ property uses the...
bool isReadOnly()
Returns true if this QML property or attached property is read-only.
A class for containing the elements of one documentation section.
Definition sections.h:17
@ Summary
Definition sections.h:19
@ Details
Definition sections.h:19
static std::optional< QString > nodeStatusAsString(const Node *node)
Returns a string representing the node status, set using \preliminary,.
#define CONFIG_LANGUAGE
Definition config.h:388
NodeType
Definition genustypes.h:150
@ Private
Definition access.h:11
@ Protected
Definition access.h:11
The Node class is the base class for all the nodes in QDoc's parse tree.
NodeType nodeType() const override
Returns this node's type.
Definition node.h:89
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
Definition node.h:141
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:235
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:107
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:127
bool isPrivateSignal() const
Definition parameters.h:69