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 "node.h"
10#include "propertynode.h"
11
12#include <QtCore/qobjectdefs.h>
13
14QT_BEGIN_NAMESPACE
15
16QString CodeMarker::s_defaultLang;
17QList<CodeMarker *> CodeMarker::s_markers;
18
19/*!
20 When a code marker constructs itself, it puts itself into
21 the static list of code markers. All the code markers in
22 the static list get initialized in initialize(), which is
23 not called until after the qdoc configuration file has
24 been read.
25 */
26CodeMarker::CodeMarker()
27{
28 s_markers.prepend(t: this);
29}
30
31/*!
32 When a code marker destroys itself, it removes itself from
33 the static list of code markers.
34 */
35CodeMarker::~CodeMarker()
36{
37 s_markers.removeAll(t: this);
38}
39
40/*!
41 A code market performs no initialization by default. Marker-specific
42 initialization is performed in subclasses.
43 */
44void CodeMarker::initializeMarker() {}
45
46/*!
47 Terminating a code marker is trivial.
48 */
49void CodeMarker::terminateMarker()
50{
51 // nothing.
52}
53
54/*!
55 All the code markers in the static list are initialized
56 here, after the qdoc configuration file has been loaded.
57 */
58void CodeMarker::initialize()
59{
60 s_defaultLang = Config::instance().get(CONFIG_LANGUAGE).asString();
61 for (const auto &marker : std::as_const(t&: s_markers))
62 marker->initializeMarker();
63}
64
65/*!
66 All the code markers in the static list are terminated here.
67 */
68void CodeMarker::terminate()
69{
70 for (const auto &marker : std::as_const(t&: s_markers))
71 marker->terminateMarker();
72}
73
74CodeMarker *CodeMarker::markerForCode(const QString &code)
75{
76 CodeMarker *defaultMarker = markerForLanguage(lang: s_defaultLang);
77 if (defaultMarker != nullptr && defaultMarker->recognizeCode(code))
78 return defaultMarker;
79
80 for (const auto &marker : std::as_const(t&: s_markers)) {
81 if (marker->recognizeCode(code))
82 return marker;
83 }
84
85 return defaultMarker;
86}
87
88CodeMarker *CodeMarker::markerForFileName(const QString &fileName)
89{
90 CodeMarker *defaultMarker = markerForLanguage(lang: s_defaultLang);
91 qsizetype dot = -1;
92 while ((dot = fileName.lastIndexOf(c: QLatin1Char('.'), from: dot)) != -1) {
93 QString ext = fileName.mid(position: dot + 1);
94 if (defaultMarker != nullptr && defaultMarker->recognizeExtension(ext))
95 return defaultMarker;
96 for (const auto &marker : std::as_const(t&: s_markers)) {
97 if (marker->recognizeExtension(ext))
98 return marker;
99 }
100 --dot;
101 }
102 return defaultMarker;
103}
104
105CodeMarker *CodeMarker::markerForLanguage(const QString &lang)
106{
107 for (const auto &marker : std::as_const(t&: s_markers)) {
108 if (marker->recognizeLanguage(lang))
109 return marker;
110 }
111 return nullptr;
112}
113
114const Node *CodeMarker::nodeForString(const QString &string)
115{
116#if QT_POINTER_SIZE == 4
117 const quintptr n = string.toUInt();
118#else
119 const quintptr n = string.toULongLong();
120#endif
121 return reinterpret_cast<const Node *>(n);
122}
123
124QString CodeMarker::stringForNode(const Node *node)
125{
126 return QString::number(reinterpret_cast<quintptr>(node));
127}
128
129/*!
130 Returns the 'extra' synopsis string for \a node with status information,
131 using a specified section \a style.
132*/
133QString CodeMarker::extraSynopsis(const Node *node, Section::Style style)
134{
135 QStringList extra;
136 if (style == Section::Details) {
137 switch (node->nodeType()) {
138 case Node::Function: {
139 const auto *func = static_cast<const FunctionNode *>(node);
140 if (func->isStatic()) {
141 extra << "static";
142 } else if (!func->isNonvirtual()) {
143 if (func->isFinal())
144 extra << "final";
145 if (func->isOverride())
146 extra << "override";
147 if (func->isPureVirtual())
148 extra << "pure";
149 extra << "virtual";
150 }
151
152 if (func->isExplicit()) extra << "explicit";
153 if (func->isConstexpr()) extra << "constexpr";
154 if (auto noexcept_info = func->getNoexcept()) {
155 extra << (QString("noexcept") + (!(*noexcept_info).isEmpty() ? "(...)" : ""));
156 }
157
158 if (func->access() == Access::Protected)
159 extra << "protected";
160 else if (func->access() == Access::Private)
161 extra << "private";
162
163 if (func->isSignal()) {
164 if (func->parameters().isPrivateSignal())
165 extra << "private";
166 extra << "signal";
167 } else if (func->isSlot())
168 extra << "slot";
169 else if (func->isDefault())
170 extra << "default";
171 else if (func->isInvokable())
172 extra << "invokable";
173 }
174 break;
175 case Node::TypeAlias:
176 extra << "alias";
177 break;
178 case Node::Property: {
179 auto propertyNode = static_cast<const PropertyNode *>(node);
180 if (propertyNode->propertyType() == PropertyNode::PropertyType::BindableProperty)
181 extra << "bindable";
182 if (!propertyNode->isWritable())
183 extra << "read-only";
184 }
185 break;
186 default:
187 break;
188 }
189 } else if (style == Section::Summary) {
190 if (node->isPreliminary())
191 extra << "preliminary";
192 else if (node->isDeprecated()) {
193 extra << "deprecated";
194 if (const QString &since = node->deprecatedSince(); !since.isEmpty())
195 extra << QStringLiteral("(%1)").arg(a: since);
196 }
197 }
198
199 if (style == Section::Details && !node->since().isEmpty()) {
200 if (!extra.isEmpty())
201 extra.last() += QLatin1Char(',');
202 extra << "since" << node->since();
203 }
204
205 QString extraStr = extra.join(sep: QLatin1Char(' '));
206 if (!extraStr.isEmpty()) {
207 extraStr.prepend(c: style == Section::Details ? '[' : '(');
208 extraStr.append(c: style == Section::Details ? ']' : ')');
209 extraStr.append(c: ' ');
210 }
211
212 return extraStr;
213}
214
215static const QString samp = QLatin1String("&amp;");
216static const QString slt = QLatin1String("&lt;");
217static const QString sgt = QLatin1String("&gt;");
218static const QString squot = QLatin1String("&quot;");
219
220QString CodeMarker::protect(const QString &str)
221{
222 qsizetype n = str.size();
223 QString marked;
224 marked.reserve(asize: n * 2 + 30);
225 const QChar *data = str.constData();
226 for (int i = 0; i != n; ++i) {
227 switch (data[i].unicode()) {
228 case '&':
229 marked += samp;
230 break;
231 case '<':
232 marked += slt;
233 break;
234 case '>':
235 marked += sgt;
236 break;
237 case '"':
238 marked += squot;
239 break;
240 default:
241 marked += data[i];
242 }
243 }
244 return marked;
245}
246
247void CodeMarker::appendProtectedString(QString *output, QStringView str)
248{
249 qsizetype n = str.size();
250 output->reserve(asize: output->size() + n * 2 + 30);
251 const QChar *data = str.constData();
252 for (int i = 0; i != n; ++i) {
253 switch (data[i].unicode()) {
254 case '&':
255 *output += samp;
256 break;
257 case '<':
258 *output += slt;
259 break;
260 case '>':
261 *output += sgt;
262 break;
263 case '"':
264 *output += squot;
265 break;
266 default:
267 *output += data[i];
268 }
269 }
270}
271
272QString CodeMarker::typified(const QString &string, bool trailingSpace)
273{
274 QString result;
275 QString pendingWord;
276
277 for (int i = 0; i <= string.size(); ++i) {
278 QChar ch;
279 if (i != string.size())
280 ch = string.at(i);
281
282 QChar lower = ch.toLower();
283 if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || ch.digitValue() >= 0
284 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) {
285 pendingWord += ch;
286 } else {
287 if (!pendingWord.isEmpty()) {
288 bool isProbablyType = (pendingWord != QLatin1String("const"));
289 if (isProbablyType)
290 result += QLatin1String("<@type>");
291 result += pendingWord;
292 if (isProbablyType)
293 result += QLatin1String("</@type>");
294 }
295 pendingWord.clear();
296
297 switch (ch.unicode()) {
298 case '\0':
299 break;
300 case '&':
301 result += QLatin1String("&amp;");
302 break;
303 case '<':
304 result += QLatin1String("&lt;");
305 break;
306 case '>':
307 result += QLatin1String("&gt;");
308 break;
309 default:
310 result += ch;
311 }
312 }
313 }
314 if (trailingSpace && string.size()) {
315 if (!string.endsWith(c: QLatin1Char('*')) && !string.endsWith(c: QLatin1Char('&')))
316 result += QLatin1Char(' ');
317 }
318 return result;
319}
320
321QString CodeMarker::taggedNode(const Node *node)
322{
323 QString tag;
324 const QString &name = node->name();
325
326 switch (node->nodeType()) {
327 case Node::Namespace:
328 tag = QLatin1String("@namespace");
329 break;
330 case Node::Class:
331 case Node::Struct:
332 case Node::Union:
333 tag = QLatin1String("@class");
334 break;
335 case Node::Enum:
336 tag = QLatin1String("@enum");
337 break;
338 case Node::TypeAlias:
339 case Node::Typedef:
340 tag = QLatin1String("@typedef");
341 break;
342 case Node::Function:
343 tag = QLatin1String("@function");
344 break;
345 case Node::Property:
346 tag = QLatin1String("@property");
347 break;
348 case Node::QmlType:
349 tag = QLatin1String("@property");
350 break;
351 case Node::Page:
352 tag = QLatin1String("@property");
353 break;
354 default:
355 tag = QLatin1String("@unknown");
356 break;
357 }
358 return (QLatin1Char('<') + tag + QLatin1Char('>') + protect(str: name) + QLatin1String("</") + tag
359 + QLatin1Char('>'));
360}
361
362QString CodeMarker::taggedQmlNode(const Node *node)
363{
364 QString tag;
365 if (node->isFunction()) {
366 const auto *fn = static_cast<const FunctionNode *>(node);
367 switch (fn->metaness()) {
368 case FunctionNode::QmlSignal:
369 tag = QLatin1String("@signal");
370 break;
371 case FunctionNode::QmlSignalHandler:
372 tag = QLatin1String("@signalhandler");
373 break;
374 case FunctionNode::QmlMethod:
375 tag = QLatin1String("@method");
376 break;
377 default:
378 tag = QLatin1String("@unknown");
379 break;
380 }
381 } else if (node->isQmlProperty()) {
382 tag = QLatin1String("@property");
383 } else {
384 tag = QLatin1String("@unknown");
385 }
386 return QLatin1Char('<') + tag + QLatin1Char('>') + protect(str: node->name()) + QLatin1String("</")
387 + tag + QLatin1Char('>');
388}
389
390QString CodeMarker::linkTag(const Node *node, const QString &body)
391{
392 return QLatin1String("<@link node=\"") + stringForNode(node) + QLatin1String("\">") + body
393 + QLatin1String("</@link>");
394}
395
396QT_END_NAMESPACE
397

source code of qttools/src/qdoc/qdoc/codemarker.cpp