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