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
cppcodemarker.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
5
6#include "access.h"
7#include "enumnode.h"
8#include "functionnode.h"
9#include "genustypes.h"
10#include "namespacenode.h"
11#include "propertynode.h"
14#include "text.h"
15#include "tree.h"
16#include "typedefnode.h"
17#include "variablenode.h"
18
19#include <QtCore/qdebug.h>
20#include <QtCore/qregularexpression.h>
21
23
24using namespace Qt::StringLiterals;
25
26/*!
27 \internal
28
29 Wraps occurrences of each name in \a concepts inside \a text with
30 \c{<@concept target="FULLY::QUALIFIED">unqualified</@concept>}
31 markers so the HTML generator can resolve them to anchors pointing
32 at the documented concept's reference page. The target carries the
33 fully-qualified name the lookup needs; the body carries the
34 unqualified spelling the reader sees, matching the source.
35
36 \a concepts holds fully-qualified concept names captured from the
37 AST. They map to their unqualified last segment for matching against
38 the requires-clause text. The fully-qualified name goes in the
39 marker target so the generator's lookup follows the same contract as
40 the \c{\concept} command, which registers the concept under its
41 fully-qualified name.
42
43 A single combined pass substitutes every concept in one sweep over
44 the original text. Replacing one concept at a time would re-scan the
45 markers it just inserted, so a later display name could match inside
46 an earlier marker's target attribute and corrupt it. The display
47 names are de-duplicated (the requires-clause text alone can't tell
48 two concepts that share an unqualified spelling apart) and the
49 alternation lists the longest first so a prefix such as \c {Hashable}
50 yields to \c {HashableContainer}.
51
52 The input text is expected to be the \c{protect()}-escaped requires
53 clause. The unqualified concept identifiers consist of letters,
54 digits, and underscores only, so they pass through \c{protect()}
55 unchanged; word boundary anchors ensure only standalone concept-name
56 tokens are substituted.
57*/
58static QString markupConceptReferences(QString text, const QStringList &concepts)
59{
60 QStringList sorted = concepts;
61 std::sort(sorted.begin(), sorted.end(),
62 [](const QString &a, const QString &b) { return a.size() > b.size(); });
63
64 QHash<QString, QString> targetForDisplay;
65 QStringList displayAlternatives;
66 for (const QString &concept_name : std::as_const(sorted)) {
67 const QString unqualified = concept_name.section("::"_L1, -1);
68 if (targetForDisplay.contains(unqualified))
69 continue;
70 targetForDisplay.insert(unqualified, concept_name);
71 displayAlternatives << QRegularExpression::escape(unqualified);
72 }
73 if (displayAlternatives.isEmpty())
74 return text;
75
76 const QRegularExpression re("\\b("_L1 + displayAlternatives.join('|'_L1) + ")\\b"_L1);
77 QString result;
78 qsizetype last = 0;
79 auto it = re.globalMatch(text);
80 while (it.hasNext()) {
81 const QRegularExpressionMatch match = it.next();
82 result += QStringView(text).mid(last, match.capturedStart() - last);
83 const QString display = match.captured(1);
84 result += "<@concept target=\""_L1 + targetForDisplay.value(display) + "\">"_L1
85 + display + "</@concept>"_L1;
86 last = match.capturedEnd();
87 }
88 result += QStringView(text).mid(last);
89 return result;
90}
91
92/*!
93 \internal
94
95 Returns a \c{QStringList} carrying the contents of \a refs converted
96 from \c{std::string}.
97*/
98static QStringList toQStringList(const std::vector<std::string> &refs)
99{
100 QStringList result;
101 result.reserve(int(refs.size()));
102 for (const auto &s : refs)
103 result.append(QString::fromStdString(s));
104 return result;
105}
106
107/*!
108 \internal
109
110 Returns a marked-up representation of a single template parameter
111 with proper type markup for link generation.
112
113 Types (in non-type parameters and initializers) are wrapped with
114 \c{<@type>} tags so the HTML generator can create hyperlinks to
115 documented types.
116*/
117QString CppCodeMarker::formatTemplateParameter(const RelaxedTemplateParameter &param)
118{
119 QString result;
120 const auto &decl = param.valued_declaration;
121
122 if (param.template_declaration)
123 result += formatTemplateDeclStorage(*param.template_declaration, TemplateFormat::SingleLine) + ' '_L1;
124
125 // TODO: RelaxedTemplateParameter doesn't preserve whether the original
126 // declaration used 'class' or 'typename'. This would require parser changes.
127 switch (param.kind) {
129 if (param.concept_name) {
130 const QString fq = QString::fromStdString(*param.concept_name);
131 const QString unqualified = fq.section("::"_L1, -1);
132 result += "<@concept target=\""_L1 + fq + "\">"_L1
133 + unqualified + "</@concept>"_L1;
134 } else {
135 result += "typename"_L1;
136 }
137 break;
139 if (!decl.type.empty())
140 result += typified(QString::fromStdString(decl.type), false);
141 break;
142 case RelaxedTemplateParameter::Kind::TemplateTemplateParameter:
143 result += "typename"_L1;
144 break;
145 }
146
147 if (param.is_parameter_pack)
148 result += "..."_L1;
149
150 if (!decl.name.empty()) {
151 if (!result.isEmpty() && !result.endsWith(' '_L1))
152 result += ' '_L1;
153 result += protect(QString::fromStdString(decl.name));
154 }
155
156 if (!decl.initializer.empty()) {
157 result += " = "_L1;
160 result += typified(QString::fromStdString(decl.initializer), false);
161 } else {
162 result += protect(QString::fromStdString(decl.initializer));
163 }
164 }
165
166 return result;
167}
168
169/*!
170 \internal
171
172 Returns a marked-up representation of a template declaration storage,
173 handling the common case for both top-level and nested (template template
174 parameter) declarations.
175
176 When \a format is MultiLine, parameters are formatted one per line with
177 indentation. Nested template declarations (called from formatTemplateParameter)
178 are always single-line.
179
180 This helper formats the core \c{template <...>} content without wrapper tags
181 or requires clause. The caller (formatTemplateDecl) handles those for
182 top-level declarations.
183
184 \sa formatTemplateParameter()
185*/
186QString CppCodeMarker::formatTemplateDeclStorage(const TemplateDeclarationStorage &templateDecl,
187 TemplateFormat format)
188{
189 const bool multiline = (format == TemplateFormat::MultiLine);
190
191 QString result = "template &lt;"_L1;
192
193 if (multiline)
194 result += '\n'_L1;
195
196 bool first = true;
197 for (const auto &param : templateDecl.parameters) {
198 if (param.sfinae_constraint)
199 continue;
200 if (!first)
201 result += multiline ? ",\n"_L1 : ", "_L1;
202 if (multiline)
203 result += " "_L1; // indentation for multiline format
204
205 result += formatTemplateParameter(param);
206 first = false;
207 }
208
209 if (multiline)
210 result += '\n'_L1;
211
212 result += "&gt;"_L1;
213
214 return result;
215}
216
217/*!
218 Returns a marked-up representation of the template declaration suitable
219 for synopsis output. For declarations above the multiline threshold,
220 a multiline format is used for improved readability.
221
222 Types within template parameters (non-type parameter types and default
223 value initializers) are wrapped with \c{<@type>} tags to enable link
224 generation to documented types.
225*/
226QString CppCodeMarker::formatTemplateDecl(const RelaxedTemplateDeclaration *templateDecl)
227{
228 const bool multiline = templateDecl->visibleParameterCount() > QDoc::MultilineTemplateParamThreshold;
229 const auto format = multiline ? TemplateFormat::MultiLine : TemplateFormat::SingleLine;
230
231 QString content = formatTemplateDeclStorage(*templateDecl, format);
232
233 QString result;
234 if (multiline)
235 result = "<@template-block>"_L1;
236
237 result += content;
238
239 if (templateDecl->requires_clause && !templateDecl->requires_clause->empty()) {
240 QString clause = protect(QString::fromStdString(*templateDecl->requires_clause));
241 clause = markupConceptReferences(std::move(clause),
242 toQStringList(templateDecl->referenced_concepts));
243 result += " requires "_L1 + clause;
244 }
245
246 if (multiline)
247 result += '\n'_L1 + "</@template-block>"_L1;
248 else
249 result += ' '_L1;
250
251 return result;
252}
253
254/*!
255 Returns \c true.
256 */
257bool CppCodeMarker::recognizeCode(const QString & /* code */)
258{
259 return true;
260}
261
262/*!
263 Returns \c true if \a ext is any of a list of file extensions
264 for the C++ language.
265 */
266bool CppCodeMarker::recognizeExtension(const QString &extension)
267{
268 QByteArray ext = extension.toLatin1();
269 return ext == "c" || ext == "c++" || ext == "qdoc" || ext == "qtt" || ext == "qtx"
270 || ext == "cc" || ext == "cpp" || ext == "cxx" || ext == "ch" || ext == "h"
271 || ext == "h++" || ext == "hh" || ext == "hpp" || ext == "hxx";
272}
273
274/*!
275 Returns \c true if \a lang is either "c" or "cpp".
276 */
277bool CppCodeMarker::recognizeLanguage(const QString &lang)
278{
279 return lang == QLatin1String("c") || lang == QLatin1String("cpp");
280}
281
282/*!
283 Returns the type of atom used to represent C++ code in the documentation.
284*/
285Atom::AtomType CppCodeMarker::atomType() const
286{
287 return Atom::Code;
288}
289
290QString CppCodeMarker::markedUpCode(const QString &code, const Node *relative,
291 const Location &location)
292{
293 return addMarkUp(code, relative, location);
294}
295
296QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relative */,
297 Section::Style style)
298{
299 const int MaxEnumValues = 6;
300 const FunctionNode *func;
301 const VariableNode *variable;
302 const EnumNode *enume;
303 QString synopsis;
304 QString name;
305
306 name = taggedNode(node);
307 if (style != Section::Details)
308 name = linkTag(node, name);
309 name = "<@name>" + name + "</@name>";
310
311 if (style == Section::Details) {
312 if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
313 && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
314 name.prepend(taggedNode(node->parent()) + "::");
315 }
316 }
317
318 switch (node->nodeType()) {
320 case NodeType::Class:
321 case NodeType::Struct:
322 case NodeType::Union:
323 synopsis = Node::nodeTypeString(node->nodeType());
324 synopsis += QLatin1Char(' ') + name;
325 break;
327 func = (const FunctionNode *)node;
328 if (style == Section::Details) {
329 if (auto templateDecl = node->templateDecl())
330 synopsis = formatTemplateDecl(&*templateDecl);
331 }
332 if (style != Section::AllMembers && !func->returnType().isEmpty())
333 synopsis += typified(func->returnTypeString(), true);
334 synopsis += name;
335 if (!func->isMacroWithoutParams()) {
336 synopsis += QLatin1Char('(');
337 if (!func->parameters().isEmpty()) {
338 const Parameters &parameters = func->parameters();
339 for (int i = 0; i < parameters.count(); ++i) {
340 if (i > 0)
341 synopsis += ", ";
342 const Parameter &param = parameters.at(i);
343 QString name = param.name();
344 QString type = param.type();
345 QString value = param.defaultValue();
346 qsizetype insertPos = param.nameInsertionPoint();
347 if (insertPos >= 0 && style != Section::AllMembers && !name.isEmpty()) {
348 // Inside-out declarator: name goes inside the type.
349 synopsis += typified(type.left(insertPos), false);
350 synopsis += "<@param>" + protect(name) + "</@param>";
351 synopsis += typified(type.mid(insertPos), false);
352 } else {
353 bool trailingSpace = style != Section::AllMembers && !name.isEmpty();
354 synopsis += typified(type, trailingSpace);
355 if (style != Section::AllMembers && !name.isEmpty())
356 synopsis += "<@param>" + protect(name) + "</@param>";
357 }
358 if (style != Section::AllMembers && !value.isEmpty())
359 synopsis += " = " + protect(value);
360 }
361 }
362 synopsis += QLatin1Char(')');
363 }
364 if (func->isConst())
365 synopsis += " const";
366
367 if (style == Section::Summary || style == Section::Accessors) {
368 if (!func->isNonvirtual())
369 synopsis.prepend("virtual ");
370 if (func->isFinal())
371 synopsis.append(" final");
372 if (func->isOverride())
373 synopsis.append(" override");
374 if (func->isPureVirtual())
375 synopsis.append(" = 0");
376 if (func->isRef())
377 synopsis.append(" &");
378 else if (func->isRefRef())
379 synopsis.append(" &&");
380 } else if (style == Section::AllMembers) {
381 if (!func->returnType().isEmpty() && func->returnType() != "void")
382 synopsis += " : " + typified(func->returnTypeString());
383 } else {
384 if (func->isRef())
385 synopsis.append(" &");
386 else if (func->isRefRef())
387 synopsis.append(" &&");
388 if (const auto &req = func->trailingRequiresClause(); req && !req->isEmpty()) {
389 QString clause = protect(*req);
390 clause = markupConceptReferences(std::move(clause),
391 func->referencedConcepts());
392 synopsis.append(" requires " + clause);
393 }
394 }
395 break;
396 case NodeType::Enum:
397 enume = static_cast<const EnumNode *>(node);
398 synopsis = "enum";
399 if (enume->isScoped())
400 synopsis += " class";
401 if (!enume->isAnonymous())
402 synopsis += " %1"_L1.arg(name);
403 else if (style != Section::Details)
404 synopsis = linkTag(node, synopsis); // Unnamed enum: Make `enum` a link to details
405 if (style == Section::Summary) {
406 synopsis += " { ";
407
408 QStringList documentedItems = enume->doc().enumItemNames();
409 if (documentedItems.isEmpty()) {
410 const auto &enumItems = enume->items();
411 for (const auto &item : enumItems)
412 documentedItems << item.name();
413 }
414 const QStringList omitItems = enume->doc().omitEnumItemNames();
415 for (const auto &item : omitItems)
416 documentedItems.removeAll(item);
417
418 if (documentedItems.size() > MaxEnumValues) {
419 // Take the last element and keep it safe, then elide the surplus.
420 const QString last = documentedItems.last();
421 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
422 documentedItems += "&hellip;";
423 documentedItems += last;
424 }
425 synopsis += documentedItems.join(QLatin1String(", "));
426
427 if (!documentedItems.isEmpty())
428 synopsis += QLatin1Char(' ');
429 synopsis += QLatin1Char('}');
430 }
431 break;
433 if (style == Section::Details) {
434 if (auto templateDecl = node->templateDecl())
435 synopsis += formatTemplateDecl(&*templateDecl);
436 }
437 synopsis += name;
438 break;
440 if (static_cast<const TypedefNode *>(node)->associatedEnum())
441 synopsis = "flags ";
442 synopsis += name;
443 break;
444 case NodeType::Property: {
445 auto property = static_cast<const PropertyNode *>(node);
446 synopsis = name + " : " + typified(property->qualifiedDataType());
447 break;
448 }
450 auto property = static_cast<const QmlPropertyNode *>(node);
451 synopsis = name + " : " + typified(property->dataType());
452 break;
453 }
454 case NodeType::Variable:
455 variable = static_cast<const VariableNode *>(node);
456 if (style == Section::AllMembers) {
457 synopsis = name + " : " + typified(variable->dataType());
458 } else {
459 synopsis = typified(variable->leftType(), true) + name + protect(variable->rightType());
460 }
461 break;
462 default:
463 synopsis = std::move(name);
464 }
465
466 QString extra = CodeMarker::extraSynopsis(node, style);
467 if (!extra.isEmpty()) {
468 extra.prepend(u"<@extra>"_s);
469 extra.append(u"</@extra> "_s);
470 }
471
472 return extra + synopsis;
473}
474
475/*!
476 */
477QString CppCodeMarker::markedUpQmlItem(const Node *node, bool summary)
478{
479 QString name = taggedQmlNode(node);
480 QString synopsis;
481
482 if (summary) {
483 name = linkTag(node, name);
484 } else if (node->isQmlProperty()) {
485 const auto *pn = static_cast<const QmlPropertyNode *>(node);
486 if (pn->isAttached())
487 name.prepend(pn->element() + QLatin1Char('.'));
488 }
489 name = "<@name>" + name + "</@name>";
490 if (node->isQmlProperty()) {
491 const auto *pn = static_cast<const QmlPropertyNode *>(node);
492 synopsis = name + " : " + typified(pn->dataType());
493 } else if (node->isFunction(Genus::QML)) {
494 const auto *func = static_cast<const FunctionNode *>(node);
495 if (!func->returnType().isEmpty())
496 synopsis = typified(func->returnTypeString(), true) + name;
497 else
498 synopsis = name;
499 synopsis += QLatin1Char('(');
500 if (!func->parameters().isEmpty()) {
501 const Parameters &parameters = func->parameters();
502 for (int i = 0; i < parameters.count(); ++i) {
503 if (i > 0)
504 synopsis += ", ";
505 QString name = parameters.at(i).name();
506 QString type = parameters.at(i).type();
507 QString paramName;
508 if (!name.isEmpty()) {
509 synopsis += typified(type, true);
510 paramName = std::move(name);
511 } else {
512 paramName = std::move(type);
513 }
514 synopsis += "<@param>" + protect(paramName) + "</@param>";
515 }
516 }
517 synopsis += QLatin1Char(')');
518 } else {
519 synopsis = std::move(name);
520 }
521
522 QString extra = CodeMarker::extraSynopsis(node, summary ? Section::Summary : Section::Details);
523 if (!extra.isEmpty()) {
524 extra.prepend(u" <@extra>"_s);
525 extra.append(u"</@extra>"_s);
526 }
527
528 return synopsis + extra;
529}
530
531QString CppCodeMarker::markedUpName(const Node *node)
532{
533 QString name = linkTag(node, taggedNode(node));
534 if (node->isFunction() && !node->isMacro())
535 name += "()";
536 return name;
537}
538
539QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *relative)
540{
541 // A related nonmember is documented under the related class, but its
542 // values are qualified with the C++ scope in which the enum was
543 // declared, so start the scope walk from the declaration parent.
544 const auto *node = relative->declarationParent() ? relative->declarationParent()
545 : relative->parent();
546
547 const NativeEnum *nativeEnum{nullptr};
548 if (auto *ne_if = dynamic_cast<const NativeEnumInterface *>(relative))
549 nativeEnum = ne_if->nativeEnum();
550
551 if (nativeEnum && nativeEnum->enumNode()
552 && !enumValue.startsWith("%1."_L1.arg(nativeEnum->prefix())))
553 return "%1<@op>.</@op>%2"_L1.arg(nativeEnum->prefix(), enumValue);
554
555 // Respect existing prefixes in \value arguments of \qmlenum topic
556 if (relative->isEnumType(Genus::QML)
557 && enumValue.section(' ', 0, 0).contains('.'_L1)) {
558 return "<@op>%1</@op>"_L1.arg(enumValue);
559 }
560
561 if (!relative->isEnumType())
562 return enumValue;
563
564 QStringList parts;
565 while (!node->isHeader() && node->parent()) {
566 parts.prepend(markedUpName(node));
567 if (node->parent() == relative || node->parent()->name().isEmpty())
568 break;
569 node = node->parent();
570 }
571 if (static_cast<const EnumNode *>(relative)->isScoped())
572 parts.append(relative->name());
573
574 parts.append(enumValue);
575 const auto &delim = (relative->genus() == Genus::QML) ? "."_L1 : "::"_L1;
576 return parts.join("<@op>%1</@op>"_L1.arg(delim));
577}
578
579QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
580 const Location & /* location */)
581{
582 static QSet<QString> types{
583 QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
584 QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
585 QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
586 QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
587 QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
588 QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
589 QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
590 QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
591 QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
592 QLatin1String("cond")
593 };
594
595 static QSet<QString> keywords{
596 QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"),
597 QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
598 QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"),
599 QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
600 QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"),
601 QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
602 QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"),
603 QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"),
604 QLatin1String("friend"), QLatin1String("goto"), QLatin1String("if"),
605 QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"),
606 QLatin1String("mutable"), QLatin1String("namespace"), QLatin1String("new"),
607 QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"),
608 QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"),
609 QLatin1String("protected"), QLatin1String("public"), QLatin1String("register"),
610 QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
611 QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"),
612 QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
613 QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"),
614 QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"),
615 QLatin1String("union"), QLatin1String("using"), QLatin1String("virtual"),
616 QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"),
617 QLatin1String("xor"), QLatin1String("xor_eq"), QLatin1String("synchronized"),
618 // Qt specific
619 QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
620 };
621
622 QString code = in;
623 QString out;
624 QStringView text;
625 int braceDepth = 0;
626 int parenDepth = 0;
627 int i = 0;
628 int start = 0;
629 int finish = 0;
630 QChar ch;
631 static const QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
632 static const QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
633 static const QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\‍("));
634 bool atEOF = false;
635
636 auto readChar = [&]() {
637 if (i < code.size())
638 ch = code[i++];
639 else
640 atEOF = true;
641 };
642
643 readChar();
644 while (!atEOF) {
645 QString tag;
646 bool target = false;
647
648 if (ch.isLetter() || ch == '_') {
649 QString ident;
650 do {
651 ident += ch;
652 finish = i;
653 readChar();
654 } while (!atEOF && (ch.isLetterOrNumber() || ch == '_'));
655
656 if (classRegExp.match(ident).hasMatch()) {
657 tag = QStringLiteral("type");
658 } else if (functionRegExp.match(ident).hasMatch()) {
659 tag = QStringLiteral("func");
660 target = true;
661 } else if (types.contains(ident)) {
662 tag = QStringLiteral("type");
663 } else if (keywords.contains(ident)) {
664 tag = QStringLiteral("keyword");
665 } else if (braceDepth == 0 && parenDepth == 0) {
666 if (code.indexOf(findFunctionRegExp, i - 1) == i - 1)
667 tag = QStringLiteral("func");
668 target = true;
669 }
670 } else if (ch.isDigit()) {
671 do {
672 finish = i;
673 readChar();
674 } while (!atEOF && (ch.isLetterOrNumber() || ch == '.' || ch == '\''));
675 tag = QStringLiteral("number");
676 } else {
677 switch (ch.unicode()) {
678 case '+':
679 case '-':
680 case '!':
681 case '%':
682 case '^':
683 case '&':
684 case '*':
685 case ',':
686 case '.':
687 case '<':
688 case '=':
689 case '>':
690 case '?':
691 case '[':
692 case ']':
693 case '|':
694 case '~':
695 finish = i;
696 readChar();
697 tag = QStringLiteral("op");
698 break;
699 case '"':
700 finish = i;
701 readChar();
702
703 while (!atEOF && ch != '"') {
704 if (ch == '\\')
705 readChar();
706 readChar();
707 }
708 finish = i;
709 readChar();
710 tag = QStringLiteral("string");
711 break;
712 case '#':
713 finish = i;
714 readChar();
715 while (!atEOF && ch != '\n') {
716 if (ch == '\\')
717 readChar();
718 finish = i;
719 readChar();
720 }
721 tag = QStringLiteral("preprocessor");
722 break;
723 case '\'':
724 finish = i;
725 readChar();
726
727 while (!atEOF && ch != '\'') {
728 if (ch == '\\')
729 readChar();
730 readChar();
731 }
732 finish = i;
733 readChar();
734 tag = QStringLiteral("char");
735 break;
736 case '(':
737 finish = i;
738 readChar();
739 ++parenDepth;
740 break;
741 case ')':
742 finish = i;
743 readChar();
744 --parenDepth;
745 break;
746 case ':':
747 finish = i;
748 readChar();
749 if (!atEOF && ch == ':') {
750 finish = i;
751 readChar();
752 tag = QStringLiteral("op");
753 }
754 break;
755 case '/':
756 finish = i;
757 readChar();
758 if (!atEOF && ch == '/') {
759 do {
760 finish = i;
761 readChar();
762 } while (!atEOF && ch != '\n');
763 tag = QStringLiteral("comment");
764 } else if (ch == '*') {
765 bool metAster = false;
766 bool metAsterSlash = false;
767
768 finish = i;
769 readChar();
770
771 while (!metAsterSlash) {
772 if (atEOF)
773 break;
774 if (ch == '*')
775 metAster = true;
776 else if (metAster && ch == '/')
777 metAsterSlash = true;
778 else
779 metAster = false;
780 finish = i;
781 readChar();
782 }
783 tag = QStringLiteral("comment");
784 } else {
785 tag = QStringLiteral("op");
786 }
787 break;
788 case '{':
789 finish = i;
790 readChar();
791 braceDepth++;
792 break;
793 case '}':
794 finish = i;
795 readChar();
796 braceDepth--;
797 break;
798 default:
799 finish = i;
800 readChar();
801 }
802 }
803
804 text = QStringView{code}.mid(start, finish - start);
805 start = finish;
806
807 if (!tag.isEmpty()) {
808 out += QStringLiteral("<@");
809 out += tag;
810 if (target) {
811 out += QStringLiteral(" target=\"");
812 out += text;
813 out += QStringLiteral("()\"");
814 }
815 out += QStringLiteral(">");
816 }
817
818 appendProtectedString(&out, text);
819
820 if (!tag.isEmpty()) {
821 out += QStringLiteral("</@");
822 out += tag;
823 out += QStringLiteral(">");
824 }
825 }
826
827 if (start < code.size()) {
828 appendProtectedString(&out, QStringView{code}.mid(start));
829 }
830
831 return out;
832}
833
834QT_END_NAMESPACE
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
AtomType
\value AnnotatedList \value AutoLink \value BaseName \value BriefLeft \value BriefRight \value C \val...
Definition atom.h:21
@ Code
Definition atom.h:31
bool isScoped() const
Definition enumnode.h:33
This node is used to represent any kind of function being documented.
bool isMacroWithoutParams() const
bool isOverride() const
const Parameters & parameters() const
bool isPureVirtual() const override
bool isRef() const
bool isNonvirtual() const
bool isConst() const
bool isRefRef() const
bool isFinal() const
The Location class provides a way to mark a location in a file.
Definition location.h:20
Interface implemented by Node subclasses that can refer to a C++ enum.
Definition nativeenum.h:28
virtual const NativeEnum * nativeEnum() const =0
Encapsulates information about native (C++) enum values.
Definition nativeenum.h:14
The Parameter class describes one function parameter.
Definition parameter.h:14
A class for containing the elements of one documentation section.
Definition sections.h:17
@ Summary
Definition sections.h:19
@ Details
Definition sections.h:19
@ Accessors
Definition sections.h:19
@ AllMembers
Definition sections.h:19
static QStringList toQStringList(const std::vector< std::string > &refs)
static QString markupConceptReferences(QString text, const QStringList &concepts)
NodeType
Definition genustypes.h:165
constexpr std::size_t MultilineTemplateParamThreshold
Combined button and popup list for selecting options.
The Node class is the base class for all the nodes in QDoc's parse tree.
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:239
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:121
bool isEnumType(Genus g) const
Definition node.h:98
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:106
NodeType nodeType() const override
Returns this node's type.
Definition node.h:82
virtual bool isMacro() const
returns true if either FunctionNode::isMacroWithParams() or FunctionNode::isMacroWithoutParams() retu...
Definition node.h:149
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:94
Aggregate * parent() const
Returns the node's parent pointer, that is, the aggregate under which the node is documented.
Definition node.h:211
bool isProxyNode() const
Returns true if the node type is Proxy.
Definition node.h:115
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 isProperty() const
Returns true if the node type is Property.
Definition node.h:114
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
Definition node.h:124
Aggregate * declarationParent() const
Returns the aggregate in whose C++ scope the node was declared, or null if the node was not adopted b...
Definition node.h:212
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:122
A class for parsing and managing a function parameter list.
Definition main.cpp:28
bool isEmpty() const
Definition parameters.h:32
const Parameter & at(int i) const
Definition parameters.h:36
int count() const
Definition parameters.h:34
std::size_t visibleParameterCount() const
Returns the number of template parameters that are visible in rendered output — SFINAE-annotated para...
ValuedDeclaration valued_declaration