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/qfileinfo.h>
16#include <QtCore/qobjectdefs.h>
17
19
20using namespace Qt::StringLiterals;
21
22QString CodeMarker::s_defaultLang;
23QList<CodeMarker *> CodeMarker::s_markers;
24
25
26/*!
27 When a code marker constructs itself, it puts itself into
28 the static list of code markers. All the code markers in
29 the static list get initialized in initialize(), which is
30 not called until after the qdoc configuration file has
31 been read.
32 */
34{
35 s_markers.prepend(this);
36}
37
38/*!
39 When a code marker destroys itself, it removes itself from
40 the static list of code markers.
41 */
43{
44 s_markers.removeAll(this);
45}
46
47/*!
48 A code market performs no initialization by default. Marker-specific
49 initialization is performed in subclasses.
50 */
52
53/*!
54 Terminating a code marker is trivial.
55 */
57{
58 // nothing.
59}
60
61/*!
62 All the code markers in the static list are initialized
63 here, after the qdoc configuration file has been loaded.
64 */
66{
67 s_defaultLang = Config::instance().get(CONFIG_LANGUAGE).asString();
68 for (const auto &marker : std::as_const(s_markers))
69 marker->initializeMarker();
70}
71
72/*!
73 All the code markers in the static list are terminated here.
74 */
76{
77 for (const auto &marker : std::as_const(s_markers))
78 marker->terminateMarker();
79}
80
81CodeMarker *CodeMarker::markerForCode(const QString &code)
82{
83 CodeMarker *defaultMarker = markerForLanguage(s_defaultLang);
84 if (defaultMarker != nullptr && defaultMarker->recognizeCode(code))
85 return defaultMarker;
86
87 for (const auto &marker : std::as_const(s_markers)) {
88 if (marker->recognizeCode(code))
89 return marker;
90 }
91
92 return defaultMarker;
93}
94
95/*!
96 Returns the appropriate code marker for the content in the file with the
97 given \a fileName, which is typically the whole file path.
98 This is achieved by first checking the file extension, then checking the
99 file name.
100*/
101CodeMarker *CodeMarker::markerForFileName(const QString &fileName)
102{
103 CodeMarker *defaultMarker = markerForLanguage(s_defaultLang);
104 qsizetype dot = -1;
105 while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
106 QString ext = fileName.mid(dot + 1);
107 if (defaultMarker != nullptr && defaultMarker->recognizeExtension(ext))
108 return defaultMarker;
109 for (const auto &marker : std::as_const(s_markers)) {
110 if (marker->recognizeExtension(ext))
111 return marker;
112 }
113 --dot;
114 }
115 // Fall back on checking the whole file name.
116 QString name = QFileInfo(fileName).fileName();
117 for (const auto &marker : std::as_const(s_markers)) {
118 if (marker->recognizeFileName(name))
119 return marker;
120 }
121 return defaultMarker;
122}
123
124CodeMarker *CodeMarker::markerForLanguage(const QString &lang)
125{
126 for (const auto &marker : std::as_const(s_markers)) {
127 if (marker->recognizeLanguage(lang.toLower()))
128 return marker;
129 }
130 return nullptr;
131}
132
133bool CodeMarker::recognizeFileName(const QString &name)
134{
135 return (name == "qmldir"_L1);
136}
137
138bool CodeMarker::recognizeLanguage(const QString &language)
139{
140 return (language == "text"_L1);
141}
142
143/*!
144 Returns a string representing the \a node status, set using \preliminary, \since,
145 and \deprecated commands.
146
147 If a string is returned, it is one of:
148 \list
149 \li \c {"preliminary"} or the value of configuration option \c preliminary
150 (in lowercase)
151 \li \c {"since <version_since>, deprecated in <version_deprecated>"}
152 \li \c {"since <version_since>, until <version_deprecated>"}
153 \li \c {"since <version_since>"}
154 \li \c {"since <version_since>, deprecated"}
155 \li \c {"deprecated in <version_deprecated>"}
156 \li \c {"until <version_deprecated>"}
157 \li \c {"deprecated"}
158 \endlist
159
160 If \a node has no related status information, returns std::nullopt.
161*/
163{
164 if (node->isPreliminary())
165 return std::optional(Config::instance().get(CONFIG_PRELIMINARY).asString().toLower());
166
167 QStringList result;
168 if (const auto &since = node->since(); !since.isEmpty())
169 result << "since %1"_L1.arg(since);
170 if (const auto &deprecated = node->deprecatedSince(); !deprecated.isEmpty()) {
171 if (node->isDeprecated())
172 result << "deprecated in %1"_L1.arg(deprecated);
173 else
174 result << "until %1"_L1.arg(deprecated);
175 } else if (node->isDeprecated()) {
176 result << u"deprecated"_s;
177 }
178
179 return result.isEmpty() ? std::nullopt : std::optional(result.join(u", "_s));
180}
181
182/*!
183 Returns the 'extra' synopsis string for \a node with status information,
184 using a specified section \a style.
185*/
186QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
187{
188 if (style != Section::Summary && style != Section::Details)
189 return {};
190
191 QStringList extra;
192 if (style == Section::Details) {
193 switch (node->nodeType()) {
194 case NodeType::Enum:
195 if (static_cast<const EnumNode *>(node)->isAnonymous())
196 extra << "anonymous";
197 break;
198 case NodeType::Struct:
199 case NodeType::Union:
200 case NodeType::Class:
201 if (static_cast<const ClassNode *>(node)->isAnonymous())
202 extra << "anonymous";
203 break;
204 case NodeType::Function: {
205 const auto *func = static_cast<const FunctionNode *>(node);
206 if (func->isStatic()) {
207 extra << "static";
208 } else if (!func->isNonvirtual()) {
209 if (func->isFinal())
210 extra << "final";
211 if (func->isOverride())
212 extra << "override";
213 if (func->isPureVirtual())
214 extra << "pure";
215 extra << "virtual";
216 }
217
218 if (func->isExplicit()) extra << "explicit";
219 if (func->isConstexpr()) extra << "constexpr";
220 if (auto noexcept_info = func->getNoexcept()) {
221 extra << (QString("noexcept") + (!(*noexcept_info).isEmpty() ? "(...)" : ""));
222 }
223
224 if (func->access() == Access::Protected)
225 extra << "protected";
226 else if (func->access() == Access::Private)
227 extra << "private";
228
229 if (func->isSignal()) {
230 if (func->parameters().isPrivateSignal())
231 extra << "private";
232 extra << "signal";
233 } else if (func->isSlot())
234 extra << "slot";
235 else if (func->isImplicitlyGenerated())
236 extra << "implicit";
237 else if (func->isExplicitlyDefaulted())
238 extra << "<@extref target=\"cpp-explicitly-defaulted\">default</@extref>";
239 else if (func->isDeletedAsWritten())
240 extra << "<@extref target=\"cpp-deleted-functions\">delete</@extref>";
241 else if (func->isInvokable())
242 extra << "invokable";
243
244 if (func->isAttached())
245 extra << "attached";
246 }
247 break;
249 extra << "alias";
250 break;
251 case NodeType::Property: {
252 auto propertyNode = static_cast<const PropertyNode *>(node);
253 if (propertyNode->propertyType() == PropertyNode::PropertyType::BindableProperty)
254 extra << "bindable";
255 if (!propertyNode->isWritable())
256 extra << "read-only";
257 if (propertyNode->isAttached())
258 extra << "attached";
259 }
260 break;
262 auto qmlProperty = static_cast<const QmlPropertyNode *>(node);
263 extra << qmlProperty->hints();
264 // "default: value" not included in hints() to avoid "default default: value"
265 if (!qmlProperty->isDefault() && !qmlProperty->defaultValue().isEmpty())
266 extra << u"default: "_s + qmlProperty->defaultValue();
267 if (qmlProperty->isAttached())
268 extra << "attached";
269 }
270 break;
271 default:
272 break;
273 }
274 }
275
276 // Add status for both Summary and Details
277 if (auto status = nodeStatusAsString(node)) {
278 if (!extra.isEmpty())
279 extra.last() += ','_L1;
280 extra << *status;
281 }
282
283 QString extraStr = extra.join(QLatin1Char(' '));
284 if (!extraStr.isEmpty()) {
285 extraStr.prepend(style == Section::Details ? '[' : '(');
286 extraStr.append(style == Section::Details ? ']' : ')');
287 }
288
289 return extraStr;
290}
291
292QString CodeMarker::protect(const QString &str)
293{
294 return Utilities::protect(str);
295}
296
297void CodeMarker::appendProtectedString(QString *output, QStringView str)
298{
299 qsizetype n = str.size();
300 output->reserve(output->size() + n * 2 + 30);
301 const QChar *data = str.constData();
302 for (int i = 0; i != n; ++i) {
303 switch (data[i].unicode()) {
304 case '&':
305 *output += Utilities::samp;
306 break;
307 case '<':
308 *output += Utilities::slt;
309 break;
310 case '>':
311 *output += Utilities::sgt;
312 break;
313 case '"':
314 *output += Utilities::squot;
315 break;
316 default:
317 *output += data[i];
318 }
319 }
320}
321
322QString CodeMarker::typified(const QString &string, bool trailingSpace)
323{
324 QString result;
325 QString pendingWord;
326
327 for (int i = 0; i <= string.size(); ++i) {
328 QChar ch;
329 if (i != string.size())
330 ch = string.at(i);
331
332 QChar lower = ch.toLower();
333 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || ch.digitValue() >= 0
334 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) {
335 pendingWord += ch;
336 } else {
337 if (!pendingWord.isEmpty()) {
338 bool isProbablyType = (pendingWord != QLatin1String("const"));
339 if (isProbablyType)
340 result += QLatin1String("<@type>");
341 result += pendingWord;
342 if (isProbablyType)
343 result += QLatin1String("</@type>");
344 }
345 pendingWord.clear();
346
347 switch (ch.unicode()) {
348 case '\0':
349 break;
350 case '&':
351 result += QLatin1String("&amp;");
352 break;
353 case '<':
354 result += QLatin1String("&lt;");
355 break;
356 case '>':
357 result += QLatin1String("&gt;");
358 break;
359 default:
360 result += ch;
361 }
362 }
363 }
364 if (trailingSpace && string.size()) {
365 if (!string.endsWith(QLatin1Char('*')) && !string.endsWith(QLatin1Char('&')))
366 result += QLatin1Char(' ');
367 }
368 return result;
369}
370
372{
373 QString tag;
374 const QString &name = node->name();
375
376 switch (node->nodeType()) {
378 tag = QLatin1String("@namespace");
379 break;
380 case NodeType::Class:
381 case NodeType::Struct:
382 case NodeType::Union:
383 tag = QLatin1String("@class");
384 break;
385 case NodeType::Enum:
386 tag = QLatin1String("@enum");
387 break;
390 tag = QLatin1String("@typedef");
391 break;
393 tag = QLatin1String("@function");
394 break;
396 tag = QLatin1String("@property");
397 break;
399 tag = QLatin1String("@property");
400 break;
401 case NodeType::Page:
402 tag = QLatin1String("@property");
403 break;
404 default:
405 tag = QLatin1String("@unknown");
406 break;
407 }
408 return (QLatin1Char('<') + tag + QLatin1Char('>') + protect(name) + QLatin1String("</") + tag
409 + QLatin1Char('>'));
410}
411
413{
414 QString tag;
415 if (node->isFunction()) {
416 const auto *fn = static_cast<const FunctionNode *>(node);
417 switch (fn->metaness()) {
419 tag = QLatin1String("@signal");
420 break;
422 tag = QLatin1String("@signalhandler");
423 break;
425 tag = QLatin1String("@method");
426 break;
427 default:
428 tag = QLatin1String("@unknown");
429 break;
430 }
431 } else if (node->isQmlProperty()) {
432 tag = QLatin1String("@property");
433 } else {
434 tag = QLatin1String("@unknown");
435 }
436 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name()) + QLatin1String("</")
437 + tag + QLatin1Char('>');
438}
439
440QString CodeMarker::linkTag(const Node *node, const QString &body)
441{
442 return QLatin1String("<@link node=\"") + Utilities::stringForNode(node) + QLatin1String("\">") + body
443 + QLatin1String("</@link>");
444}
445
446QT_END_NAMESPACE
Access
Definition access.h:11
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.
virtual bool recognizeFileName(const QString &name)
QString linkTag(const Node *node, const QString &body)
virtual bool recognizeLanguage(const QString &language)
QString taggedNode(const Node *node)
This node is used to represent any kind of function being documented.
This class describes one instance of using the Q_PROPERTY macro.
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_PRELIMINARY
Definition config.h:423
#define CONFIG_LANGUAGE
Definition config.h:408
NodeType
Definition genustypes.h:150
Combined button and popup list for selecting options.
@ 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:82
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
Definition node.h:134
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:100
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:120