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 const auto *node = relative->parent();
542
543 const NativeEnum *nativeEnum{nullptr};
544 if (auto *ne_if = dynamic_cast<const NativeEnumInterface *>(relative))
545 nativeEnum = ne_if->nativeEnum();
546
547 if (nativeEnum && nativeEnum->enumNode()
548 && !enumValue.startsWith("%1."_L1.arg(nativeEnum->prefix())))
549 return "%1<@op>.</@op>%2"_L1.arg(nativeEnum->prefix(), enumValue);
550
551 // Respect existing prefixes in \value arguments of \qmlenum topic
552 if (relative->isEnumType(Genus::QML)
553 && enumValue.section(' ', 0, 0).contains('.'_L1)) {
554 return "<@op>%1</@op>"_L1.arg(enumValue);
555 }
556
557 if (!relative->isEnumType())
558 return enumValue;
559
560 QStringList parts;
561 while (!node->isHeader() && node->parent()) {
562 parts.prepend(markedUpName(node));
563 if (node->parent() == relative || node->parent()->name().isEmpty())
564 break;
565 node = node->parent();
566 }
567 if (static_cast<const EnumNode *>(relative)->isScoped())
568 parts.append(relative->name());
569
570 parts.append(enumValue);
571 const auto &delim = (relative->genus() == Genus::QML) ? "."_L1 : "::"_L1;
572 return parts.join("<@op>%1</@op>"_L1.arg(delim));
573}
574
575QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
576 const Location & /* location */)
577{
578 static QSet<QString> types{
579 QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
580 QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
581 QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
582 QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
583 QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
584 QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
585 QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
586 QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
587 QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
588 QLatin1String("cond")
589 };
590
591 static QSet<QString> keywords{
592 QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"),
593 QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
594 QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"),
595 QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
596 QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"),
597 QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
598 QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"),
599 QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"),
600 QLatin1String("friend"), QLatin1String("goto"), QLatin1String("if"),
601 QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"),
602 QLatin1String("mutable"), QLatin1String("namespace"), QLatin1String("new"),
603 QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"),
604 QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"),
605 QLatin1String("protected"), QLatin1String("public"), QLatin1String("register"),
606 QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
607 QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"),
608 QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
609 QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"),
610 QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"),
611 QLatin1String("union"), QLatin1String("using"), QLatin1String("virtual"),
612 QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"),
613 QLatin1String("xor"), QLatin1String("xor_eq"), QLatin1String("synchronized"),
614 // Qt specific
615 QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
616 };
617
618 QString code = in;
619 QString out;
620 QStringView text;
621 int braceDepth = 0;
622 int parenDepth = 0;
623 int i = 0;
624 int start = 0;
625 int finish = 0;
626 QChar ch;
627 static const QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
628 static const QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
629 static const QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\‍("));
630 bool atEOF = false;
631
632 auto readChar = [&]() {
633 if (i < code.size())
634 ch = code[i++];
635 else
636 atEOF = true;
637 };
638
639 readChar();
640 while (!atEOF) {
641 QString tag;
642 bool target = false;
643
644 if (ch.isLetter() || ch == '_') {
645 QString ident;
646 do {
647 ident += ch;
648 finish = i;
649 readChar();
650 } while (!atEOF && (ch.isLetterOrNumber() || ch == '_'));
651
652 if (classRegExp.match(ident).hasMatch()) {
653 tag = QStringLiteral("type");
654 } else if (functionRegExp.match(ident).hasMatch()) {
655 tag = QStringLiteral("func");
656 target = true;
657 } else if (types.contains(ident)) {
658 tag = QStringLiteral("type");
659 } else if (keywords.contains(ident)) {
660 tag = QStringLiteral("keyword");
661 } else if (braceDepth == 0 && parenDepth == 0) {
662 if (code.indexOf(findFunctionRegExp, i - 1) == i - 1)
663 tag = QStringLiteral("func");
664 target = true;
665 }
666 } else if (ch.isDigit()) {
667 do {
668 finish = i;
669 readChar();
670 } while (!atEOF && (ch.isLetterOrNumber() || ch == '.' || ch == '\''));
671 tag = QStringLiteral("number");
672 } else {
673 switch (ch.unicode()) {
674 case '+':
675 case '-':
676 case '!':
677 case '%':
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 finish = i;
692 readChar();
693 tag = QStringLiteral("op");
694 break;
695 case '"':
696 finish = i;
697 readChar();
698
699 while (!atEOF && ch != '"') {
700 if (ch == '\\')
701 readChar();
702 readChar();
703 }
704 finish = i;
705 readChar();
706 tag = QStringLiteral("string");
707 break;
708 case '#':
709 finish = i;
710 readChar();
711 while (!atEOF && ch != '\n') {
712 if (ch == '\\')
713 readChar();
714 finish = i;
715 readChar();
716 }
717 tag = QStringLiteral("preprocessor");
718 break;
719 case '\'':
720 finish = i;
721 readChar();
722
723 while (!atEOF && ch != '\'') {
724 if (ch == '\\')
725 readChar();
726 readChar();
727 }
728 finish = i;
729 readChar();
730 tag = QStringLiteral("char");
731 break;
732 case '(':
733 finish = i;
734 readChar();
735 ++parenDepth;
736 break;
737 case ')':
738 finish = i;
739 readChar();
740 --parenDepth;
741 break;
742 case ':':
743 finish = i;
744 readChar();
745 if (!atEOF && ch == ':') {
746 finish = i;
747 readChar();
748 tag = QStringLiteral("op");
749 }
750 break;
751 case '/':
752 finish = i;
753 readChar();
754 if (!atEOF && ch == '/') {
755 do {
756 finish = i;
757 readChar();
758 } while (!atEOF && ch != '\n');
759 tag = QStringLiteral("comment");
760 } else if (ch == '*') {
761 bool metAster = false;
762 bool metAsterSlash = false;
763
764 finish = i;
765 readChar();
766
767 while (!metAsterSlash) {
768 if (atEOF)
769 break;
770 if (ch == '*')
771 metAster = true;
772 else if (metAster && ch == '/')
773 metAsterSlash = true;
774 else
775 metAster = false;
776 finish = i;
777 readChar();
778 }
779 tag = QStringLiteral("comment");
780 } else {
781 tag = QStringLiteral("op");
782 }
783 break;
784 case '{':
785 finish = i;
786 readChar();
787 braceDepth++;
788 break;
789 case '}':
790 finish = i;
791 readChar();
792 braceDepth--;
793 break;
794 default:
795 finish = i;
796 readChar();
797 }
798 }
799
800 text = QStringView{code}.mid(start, finish - start);
801 start = finish;
802
803 if (!tag.isEmpty()) {
804 out += QStringLiteral("<@");
805 out += tag;
806 if (target) {
807 out += QStringLiteral(" target=\"");
808 out += text;
809 out += QStringLiteral("()\"");
810 }
811 out += QStringLiteral(">");
812 }
813
814 appendProtectedString(&out, text);
815
816 if (!tag.isEmpty()) {
817 out += QStringLiteral("</@");
818 out += tag;
819 out += QStringLiteral(">");
820 }
821 }
822
823 if (start < code.size()) {
824 appendProtectedString(&out, QStringView{code}.mid(start));
825 }
826
827 return out;
828}
829
830QT_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:154
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:237
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.
Definition node.h:210
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
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