14using namespace Qt::Literals::StringLiterals;
18 static const QRegularExpression tag(u"</?@[^>]*>"_s);
19 QString t = markedCode;
20 t.replace(tag, QString());
21 t.replace(u"""_s, u"\""_s);
22 t.replace(u">"_s, u">"_s);
23 t.replace(u"<"_s, u"<"_s);
24 t.replace(u"&"_s, u"&"_s);
31 case Genus::CPP:
return u"cpp"_s;
32 case Genus::QML:
return u"qml"_s;
33 case Genus::DOC:
return u"doc"_s;
34 case Genus::API:
return u"api"_s;
35 case Genus::DontCare:
return {};
47 const QStringList &args = atom->strings();
48 const QString directive = args.size() > 1 ? args.at(1) : QString();
49 return Sorting::parseSortOrder(directive) == Qt::DescendingOrder
50 ? u"descending"_s : u"ascending"_s;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
133
134
135
136
137
138
139
140
141
142
143
144
145
147 DiagnosticHandler diagnosticHandler)
148 : m_briefHandling(briefHandling), m_headingOffset(headingOffset),
173
174
175
176
177
178
179
180
185 m_inlinePath.clear();
186 m_inlineBaseDepths.clear();
193 processAtoms(firstAtom);
197 while (!m_blockPath.isEmpty())
199 m_inlinePath.clear();
200 m_inlineBaseDepths.clear();
208
209
210
211
212
217 atom = skipFormatIfBlock(atom);
224 atom = dispatchAtom(atom);
232
233
234
235
236
237
238
239
240
241
242
243
249 while (atom && depth > 0) {
260
261
262
263
268 if (m_briefHandling == BriefHandling::Include)
269 openBlock(BlockType::Paragraph);
284 openBlock(BlockType::Paragraph);
292 addLeafInline(InlineType::Text, atom->string());
296 addLeafInline(InlineType::Code, stripCodeMarkerTags(atom->string()));
304 attrs[
"language"_L1] = u"qml"_s;
306 attrs[
"language"_L1] = u"text"_s;
307 attrs[
"bad"_L1] =
true;
308 }
else if (atom->count() >= 2 && !atom->string(1).isEmpty()) {
309 attrs[
"language"_L1] = atom->string(1);
311 attrs[
"language"_L1] = u"cpp"_s;
314 openBlock(BlockType::CodeBlock, attrs);
315 addLeafInline(InlineType::Text, stripCodeMarkerTags(atom->string()));
329 link.href = atom->string();
332 const auto *linkAtom =
static_cast<
const LinkAtom *>(atom);
334 linkAtom->location.filePath(),
335 linkAtom->location.lineNo()
338 link.children.append({
InlineType::Text, atom->string(), {}, {}, {}, {}, {} });
339 addInline(
std::move(link));
345 if (m_blockPath.isEmpty())
346 openBlock(BlockType::Paragraph);
352 link.href = atom->string();
358 auto *mutableAtom =
const_cast<
Atom *>(atom);
360 QString genusStr = genusToString(genus);
361 if (!genusStr.isEmpty())
362 link.attributes[
"linkGenus"_L1] = genusStr;
363 if (Tree *domain = mutableAtom->domain())
364 link.attributes[
"linkModule"_L1] = domain->physicalModuleName();
365 if (
int flags = mutableAtom->flags())
366 link.attributes[
"linkFlags"_L1] = flags;
368 const auto *linkAtom =
static_cast<
const LinkAtom *>(mutableAtom);
370 linkAtom->location.filePath(),
371 linkAtom->location.lineNo()
375 pushInlineContainer(
std::move(link));
387 const QString &fmt = atom->string();
401 pushInlineContainer(
std::move(bold));
405 InlineType type = formattingToInlineType(fmt);
410 container
.type = type;
411 pushInlineContainer(
std::move(container));
416 const QString &fmt = atom->string();
420 const qsizetype base = m_inlineBaseDepths.isEmpty() ? 0 : m_inlineBaseDepths.last();
421 if (m_inlinePath.size() > base) {
423 m_inlinePath.removeLast();
435 const qsizetype base = m_inlineBaseDepths.isEmpty() ? 0 : m_inlineBaseDepths.last();
436 if (m_inlinePath.size() > base)
437 m_inlinePath.removeLast();
441 case Atom::SectionLeft:
442 openBlock(BlockType::Section);
451 attrs[
"level"_L1] = atom->string().toInt() + m_headingOffset;
455 if (atom->count() >= 2)
456 attrs[
"sectionRef"_L1] = atom->string(1);
457 openBlock(BlockType::SectionHeading, attrs);
467 const QString &listType = atom->string();
468 attrs[
"listType"_L1] = listType;
470 openBlock(BlockType::DefinitionList, attrs);
472 openBlock(BlockType::List, attrs);
482 if (parent->type == BlockType::DefinitionList)
483 openBlock(BlockType::DefinitionDescription);
485 openBlock(BlockType::ListItem);
498 openBlock(BlockType::Note);
505 case Atom::WarningLeft:
506 openBlock(BlockType::Warning);
513 case Atom::ImportantLeft:
514 openBlock(BlockType::Important);
522 addLeafInline(InlineType::LineBreak, {});
526 openBlock(BlockType::HorizontalRule);
532 attrs[
"variant"_L1] = toString(ListPlaceholderVariant::AnnotatedGroup);
533 attrs[
"argument"_L1] = atom->string();
534 attrs[
"sort"_L1] = placeholderSortAttribute(atom);
535 openBlock(BlockType::ListPlaceholder, attrs);
541 const QString arg = atom->string();
543 attrs[
"argument"_L1] = arg;
544 attrs[
"sort"_L1] = placeholderSortAttribute(atom);
546 static constexpr auto classesPrefix =
"classes "_L1;
547 if (arg ==
"annotatedexamples"_L1) {
548 attrs[
"variant"_L1] = toString(ListPlaceholderVariant::AnnotatedExamples);
549 }
else if (arg ==
"annotatedclasses"_L1) {
550 attrs[
"variant"_L1] = toString(ListPlaceholderVariant::AnnotatedClasses);
551 }
else if (arg ==
"classes"_L1 || arg.startsWith(classesPrefix)) {
552 attrs[
"variant"_L1] = toString(ListPlaceholderVariant::CompactClasses);
553 if (arg.startsWith(classesPrefix)) {
554 const QString rootName = arg.sliced(classesPrefix.size()).trimmed();
555 if (!rootName.isEmpty())
556 attrs[
"rootName"_L1] = rootName;
563 QJsonObject fallback;
564 fallback[
"generatedList"_L1] = arg;
565 openBlock(BlockType::Div, fallback);
570 openBlock(BlockType::ListPlaceholder, attrs);
577 QString tableStyle = u"generic"_s;
580 for (
int i = 0; i < atom->count(); ++i) {
581 const QString &arg = atom->string(i);
582 if (arg ==
"borderless"_L1)
584 else if (arg.contains(
'%'_L1))
590 if (width ==
"%"_L1) {
592 int pct = atom->string(0).toInt(&ok);
593 width = ok ? QString::number(pct) +
'%'_L1 : QString();
596 attrs[
"style"_L1] = tableStyle;
597 if (!width.isEmpty())
598 attrs[
"width"_L1] = width;
599 openBlock(BlockType::Table, attrs);
607 case Atom::TableHeaderLeft:
608 openBlock(BlockType::TableHeaderRow);
615 case Atom::TableRowLeft:
616 openBlock(BlockType::TableRow);
625 const QString &spec = atom->string();
626 if (!spec.isEmpty()) {
627 const auto parts = QStringView{spec}.split(u',');
628 if (parts.size() >= 2) {
629 int colspan = qMax(1, parts[0].toInt());
630 int rowspan = qMax(1, parts[1].toInt());
632 attrs[
"colspan"_L1] = colspan;
634 attrs[
"rowspan"_L1] = rowspan;
637 openBlock(BlockType::TableCell, attrs);
645 case Atom::ListTagLeft:
646 openBlock(BlockType::DefinitionTerm);
655 attrs[
"class"_L1] = u"centerAlign"_s;
656 openBlock(BlockType::Paragraph, attrs);
660 img.href = atom->string();
663 addInline(
std::move(img));
673 if (Q_UNLIKELY(m_blockPath.isEmpty()))
678 img.href = atom->string();
681 addInline(
std::move(img));
693 attrs[
"class"_L1] = u"figCaption"_s;
694 openBlock(BlockType::Paragraph, attrs);
717
718
719
720
721
722
727 block.attributes =
std::move(attrs);
729 m_inlineBaseDepths.append(m_inlinePath.size());
731 if (m_blockPath.isEmpty()) {
732 m_result.append(std::move(block));
733 m_blockPath.append(m_result.size() - 1);
735 auto *parent = resolveBlock();
736 parent->children.append(
std::move(block));
737 m_blockPath.append(parent->children.size() - 1);
742
743
744
745
746
747
748
751 if (!m_blockPath.isEmpty()) {
752 if (Q_UNLIKELY(m_inlineBaseDepths.isEmpty())) {
753 m_inlinePath.clear();
755 m_inlineBaseDepths.clear();
759 const qsizetype expectedDepth = m_inlineBaseDepths.last();
760 if (m_inLink && m_inlinePath.size() > expectedDepth)
762 m_inlinePath.resize(expectedDepth);
763 m_inlinePath.resize(expectedDepth);
764 m_inlineBaseDepths.removeLast();
765 m_blockPath.removeLast();
770
771
772
773
774
775
776
777
778
779
782 if (!m_inlinePath.isEmpty()) {
783 resolveInline()->children.append(
std::move(inline_));
784 }
else if (!m_blockPath.isEmpty()) {
785 resolveBlock()->inlineContent.append(
std::move(inline_));
788 m_diagnose(QtWarningMsg, u"Dropping inline content outside any block"_s);
793
794
795
801 addInline(
std::move(ic));
805
806
807
808
809
810
811
812
813
816 if (!m_inlinePath.isEmpty()) {
817 auto *parent = resolveInline();
818 parent->children.append(
std::move(container));
819 m_inlinePath.append(parent->children.size() - 1);
820 }
else if (!m_blockPath.isEmpty()) {
821 auto *block = resolveBlock();
822 block->inlineContent.append(
std::move(container));
823 m_inlinePath.append(block->inlineContent.size() - 1);
828
829
830
831
832
833
836 Q_ASSERT(!m_blockPath.isEmpty());
837 Q_ASSERT(m_blockPath[0] >= 0 && m_blockPath[0] < m_result.size());
839 for (qsizetype i = 1; i < m_blockPath.size(); ++i) {
840 Q_ASSERT(m_blockPath[i] >= 0 && m_blockPath[i] < block->children.size());
841 block = &block->children[m_blockPath[i]];
847
848
849
850
851
852
856 Q_ASSERT(!m_inlinePath.isEmpty());
857 Q_ASSERT(m_inlinePath[0] >= 0 && m_inlinePath[0] < block->inlineContent.size());
859 for (qsizetype i = 1; i < m_inlinePath.size(); ++i) {
860 Q_ASSERT(m_inlinePath[i] >= 0 && m_inlinePath[i] < ic->children.size());
861 ic = &ic->children[m_inlinePath[i]];
#define ATOM_FORMATTING_TELETYPE
#define ATOM_FORMATTING_UNDERLINE
#define ATOM_FORMATTING_NOTRANSLATE
#define ATOM_FORMATTING_SUBSCRIPT
#define ATOM_FORMATTING_BOLD
#define ATOM_FORMATTING_TRADEMARK
#define ATOM_FORMATTING_ITALIC
#define ATOM_FORMATTING_LINK
#define ATOM_FORMATTING_SUPERSCRIPT
#define ATOM_FORMATTING_INDEX
#define ATOM_FORMATTING_UICONTROL
#define ATOM_FORMATTING_PARAMETER
The Atom class is the fundamental unit for representing documents internally.
AtomType type() const
Return the type of this atom.
virtual bool isLinkAtom() const
const Atom * next() const
Return the next atom in the atom list.
Converts Atom chains to QList<IR::ContentBlock> trees.
QList< ContentBlock > build(const Atom *firstAtom)
Walks the atom chain starting at firstAtom and returns a list of ContentBlock trees representing the ...
ContentBuilder(BriefHandling briefHandling=BriefHandling::Skip, int headingOffset=0, DiagnosticHandler diagnosticHandler={})
Constructs a ContentBuilder.
static QString genusToString(Genus genus)
static QString stripCodeMarkerTags(const QString &markedCode)
static QString placeholderSortAttribute(const Atom *atom)
static InlineType formattingToInlineType(const QString &formatting)
Combined button and popup list for selecting options.
Represents a structural block element in documentation.
LinkData(LinkOrigin o, LinkState s, std::optional< SourceLocation > loc=std::nullopt)
Represents inline content within a documentation block.