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 break;
246 extra << "alias";
247 break;
248 case NodeType::Property: {
249 auto propertyNode = static_cast<const PropertyNode *>(node);
250 if (propertyNode->propertyType() == PropertyNode::PropertyType::BindableProperty)
251 extra << "bindable";
252 if (!propertyNode->isWritable())
253 extra << "read-only";
254 }
255 break;
257 auto qmlProperty = static_cast<const QmlPropertyNode *>(node);
258 extra << qmlProperty->hints();
259 // "default: value" not included in hints() to avoid "default default: value"
260 if (!qmlProperty->isDefault() && !qmlProperty->defaultValue().isEmpty())
261 extra << u"default: "_s + qmlProperty->defaultValue();
262 }
263 break;
264 default:
265 break;
266 }
267 }
268
269 // Add status for both Summary and Details
270 if (auto status = nodeStatusAsString(node)) {
271 if (!extra.isEmpty())
272 extra.last() += ','_L1;
273 extra << *status;
274 }
275
276 QString extraStr = extra.join(QLatin1Char(' '));
277 if (!extraStr.isEmpty()) {
278 extraStr.prepend(style == Section::Details ? '[' : '(');
279 extraStr.append(style == Section::Details ? ']' : ')');
280 }
281
282 return extraStr;
283}
284
285QString CodeMarker::protect(const QString &str)
286{
287 return Utilities::protect(str);
288}
289
290void CodeMarker::appendProtectedString(QString *output, QStringView str)
291{
292 qsizetype n = str.size();
293 output->reserve(output->size() + n * 2 + 30);
294 const QChar *data = str.constData();
295 for (int i = 0; i != n; ++i) {
296 switch (data[i].unicode()) {
297 case '&':
298 *output += Utilities::samp;
299 break;
300 case '<':
301 *output += Utilities::slt;
302 break;
303 case '>':
304 *output += Utilities::sgt;
305 break;
306 case '"':
307 *output += Utilities::squot;
308 break;
309 default:
310 *output += data[i];
311 }
312 }
313}
314
315QString CodeMarker::typified(const QString &string, bool trailingSpace)
316{
317 QString result;
318 QString pendingWord;
319
320 for (int i = 0; i <= string.size(); ++i) {
321 QChar ch;
322 if (i != string.size())
323 ch = string.at(i);
324
325 QChar lower = ch.toLower();
326 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || ch.digitValue() >= 0
327 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) {
328 pendingWord += ch;
329 } else {
330 if (!pendingWord.isEmpty()) {
331 bool isProbablyType = (pendingWord != QLatin1String("const"));
332 if (isProbablyType)
333 result += QLatin1String("<@type>");
334 result += pendingWord;
335 if (isProbablyType)
336 result += QLatin1String("</@type>");
337 }
338 pendingWord.clear();
339
340 switch (ch.unicode()) {
341 case '\0':
342 break;
343 case '&':
344 result += QLatin1String("&amp;");
345 break;
346 case '<':
347 result += QLatin1String("&lt;");
348 break;
349 case '>':
350 result += QLatin1String("&gt;");
351 break;
352 default:
353 result += ch;
354 }
355 }
356 }
357 if (trailingSpace && string.size()) {
358 if (!string.endsWith(QLatin1Char('*')) && !string.endsWith(QLatin1Char('&')))
359 result += QLatin1Char(' ');
360 }
361 return result;
362}
363
365{
366 QString tag;
367 const QString &name = node->name();
368
369 switch (node->nodeType()) {
371 tag = QLatin1String("@namespace");
372 break;
373 case NodeType::Class:
374 case NodeType::Struct:
375 case NodeType::Union:
376 tag = QLatin1String("@class");
377 break;
378 case NodeType::Enum:
379 tag = QLatin1String("@enum");
380 break;
383 tag = QLatin1String("@typedef");
384 break;
386 tag = QLatin1String("@function");
387 break;
389 tag = QLatin1String("@property");
390 break;
392 tag = QLatin1String("@property");
393 break;
394 case NodeType::Page:
395 tag = QLatin1String("@property");
396 break;
397 default:
398 tag = QLatin1String("@unknown");
399 break;
400 }
401 return (QLatin1Char('<') + tag + QLatin1Char('>') + protect(name) + QLatin1String("</") + tag
402 + QLatin1Char('>'));
403}
404
406{
407 QString tag;
408 if (node->isFunction()) {
409 const auto *fn = static_cast<const FunctionNode *>(node);
410 switch (fn->metaness()) {
412 tag = QLatin1String("@signal");
413 break;
415 tag = QLatin1String("@signalhandler");
416 break;
418 tag = QLatin1String("@method");
419 break;
420 default:
421 tag = QLatin1String("@unknown");
422 break;
423 }
424 } else if (node->isQmlProperty()) {
425 tag = QLatin1String("@property");
426 } else {
427 tag = QLatin1String("@unknown");
428 }
429 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name()) + QLatin1String("</")
430 + tag + QLatin1Char('>');
431}
432
433QString CodeMarker::linkTag(const Node *node, const QString &body)
434{
435 return QLatin1String("<@link node=\"") + Utilities::stringForNode(node) + QLatin1String("\">") + body
436 + QLatin1String("</@link>");
437}
438
439QT_END_NAMESPACE
Access
Definition access.h:11
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.
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:421
#define CONFIG_LANGUAGE
Definition config.h:406
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