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
contentblock.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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 "contentblock.h"
5
6#include <QJsonArray>
7#include <QStringList>
8
10
11using namespace Qt::Literals::StringLiterals;
12
13namespace IR {
14
15/*!
16 \enum IR::InlineType
17 \internal
18 \brief Discriminator for inline content elements within a block.
19
20 Inline types represent formatting and content that flows within a block
21 element (paragraph, heading, etc.). They can nest — for example, Bold
22 containing Text, or Link containing Code.
23
24 \value Text Plain text content.
25 \value Code Inline code span, such as a class or function name.
26 \value Link Hyperlink to another page or external resource.
27 \value Bold Bold-formatted text.
28 \value Italic Italic-formatted text.
29 \value Teletype Monospaced text, typically rendered as \c{<tt>}.
30 \value Underline Underlined text.
31 \value Strikethrough Struck-through text.
32 \value Subscript Subscript text.
33 \value Superscript Superscript text.
34 \value Parameter Function parameter name.
35 \value LineBreak Explicit line break within a block.
36 \value Image Inline image.
37 \value Keyword Index keyword anchor.
38 \value Target Named anchor target for cross-references.
39*/
40
41/*!
42 \enum IR::BlockType
43 \internal
44 \brief Discriminator for structural block elements in documentation.
45
46 Block types represent the structural elements of documentation content:
47 paragraphs, code blocks, lists, sections, and callout blocks. They nest
48 to form the document structure (e.g., List containing ListItems, each
49 containing Paragraphs).
50
51 \value Paragraph A paragraph of text with inline content.
52 \value CodeBlock A block of source code, optionally with a language attribute.
53 \value List An ordered or unordered list containing ListItem children.
54 \value ListItem A single item within a List.
55 \value Section A document section containing a heading and child blocks.
56 \value SectionHeading A section heading with a level attribute (1–6).
57 \value Note A note callout block.
58 \value Warning A warning callout block.
59 \value Important An important callout block.
60 \value Details A collapsible details block.
61 \value Brief The brief description of a documented entity.
62 \value Div A generic container block.
63 \value Quotation A block quotation.
64 \value Legalese A legal text block, such as a license notice.
65 \value HorizontalRule A horizontal separator rule.
66 \value Table A table container with TableRow and TableHeaderRow children.
67 \value TableRow A data row within a table, containing TableCell children.
68 \value TableHeaderRow A header row within a table, containing header cells.
69 \value TableCell A cell within a table row.
70 \value Raw Raw format-specific content passed through without processing.
71 \value DefinitionList A definition or value list containing DefinitionTerm and DefinitionDescription pairs.
72 \value DefinitionTerm The term or key in a definition list entry.
73 \value DefinitionDescription The description or value in a definition list entry.
74 \value ListPlaceholder A short-lived placeholder emitted by the
75 content builder for \\generatelist and \\annotatedlist
76 atoms. The list-expander pass replaces the placeholder with
77 a Catalog subtree before rendering. A placeholder that
78 reaches a template indicates the expansion pass didn't run
79 and the IR isn't resolved.
80 \value Catalog A populated catalog wrapper, emitted by the
81 list-expander pass, containing reused Table, SectionHeading,
82 List, ListItem, and Link children. Renderers dispatch on
83 the block's variant attribute to select the catalog style
84 such as classes index, examples index, or group members.
85*/
86
87/*!
88 \struct IR::InlineContent
89 \internal
90 \brief Represents inline content within a documentation block.
91
92 InlineContent is a format-agnostic representation of inline content
93 such as text, code, links, and formatting. Instances nest recursively
94 to represent formatting like bold text containing a link.
95
96 Each element is either a \e leaf (has \c text, no \c children) or a
97 \e container (has \c children, no \c text). This invariant is enforced
98 by Q_ASSERT in debug builds. Typically, content-bearing types such as
99 Text, Code, and Image are leaves, while formatting types such as Bold,
100 Italic, and Link are containers whose children carry the text.
101
102 The \c href and \c title fields are metadata that can be set on either
103 leaf or container elements (e.g., Link uses \c href with children;
104 Image uses \c href as a leaf).
105
106 This is a pure value type with no dependencies on QDoc's core
107 infrastructure. It belongs in QDocLib.
108
109 \sa ContentBlock, BlockType
110*/
111
112/*!
113 \struct IR::ContentBlock
114 \internal
115 \brief Represents a structural block element in documentation.
116
117 ContentBlock is a format-agnostic representation of documentation
118 structure. Most blocks are either \e{leaf blocks} (with \c inlineContent)
119 or \e{container blocks} (with \c children). Some atom chains produce
120 blocks that mix both — serialization and plainText() handle this
121 gracefully.
122
123 The \c attributes field holds type-specific metadata as a QJsonObject.
124 Attribute keys use camelCase for compatibility with Inja dot notation
125 in templates (e.g., \c{block.attributes.listType}). Type IDs in the
126 \c type field use kebab-case (e.g., \c{"code-block"}).
127
128 This is a pure value type with no dependencies on QDoc's core
129 infrastructure. It belongs in QDocLib. Multiple renderers can read
130 the same ContentBlock concurrently — the frozen IR design supports
131 parallel rendering per output format.
132
133 \sa InlineContent, InlineType
134*/
135
136// Returns the kebab-case string ID for an InlineType.
138{
139 switch (type) {
140 case InlineType::Text: return u"text"_s;
141 case InlineType::Code: return u"code"_s;
142 case InlineType::Link: return u"link"_s;
143 case InlineType::Bold: return u"bold"_s;
144 case InlineType::Italic: return u"italic"_s;
145 case InlineType::Teletype: return u"teletype"_s;
146 case InlineType::Underline: return u"underline"_s;
147 case InlineType::Strikethrough: return u"strikethrough"_s;
148 case InlineType::Subscript: return u"subscript"_s;
149 case InlineType::Superscript: return u"superscript"_s;
150 case InlineType::Parameter: return u"parameter"_s;
151 case InlineType::LineBreak: return u"line-break"_s;
152 case InlineType::Image: return u"image"_s;
153 case InlineType::Keyword: return u"keyword"_s;
154 case InlineType::Target: return u"target"_s;
155 }
156 Q_UNREACHABLE();
157}
158
159// Returns the kebab-case string ID for a BlockType.
161{
162 switch (type) {
163 case BlockType::Paragraph: return u"paragraph"_s;
164 case BlockType::CodeBlock: return u"code-block"_s;
165 case BlockType::List: return u"list"_s;
166 case BlockType::ListItem: return u"list-item"_s;
167 case BlockType::Section: return u"section"_s;
168 case BlockType::SectionHeading: return u"section-heading"_s;
169 case BlockType::Note: return u"note"_s;
170 case BlockType::Warning: return u"warning"_s;
171 case BlockType::Important: return u"important"_s;
172 case BlockType::Details: return u"details"_s;
173 case BlockType::Brief: return u"brief"_s;
174 case BlockType::Div: return u"div"_s;
175 case BlockType::Quotation: return u"quotation"_s;
176 case BlockType::Legalese: return u"legalese"_s;
177 case BlockType::HorizontalRule: return u"horizontal-rule"_s;
178 case BlockType::Table: return u"table"_s;
179 case BlockType::TableRow: return u"table-row"_s;
180 case BlockType::TableHeaderRow: return u"table-header-row"_s;
181 case BlockType::TableCell: return u"table-cell"_s;
182 case BlockType::Raw: return u"raw"_s;
183 case BlockType::DefinitionList: return u"definition-list"_s;
184 case BlockType::DefinitionTerm: return u"definition-term"_s;
185 case BlockType::DefinitionDescription: return u"definition-description"_s;
186 case BlockType::ListPlaceholder: return u"list-placeholder"_s;
187 case BlockType::Catalog: return u"catalog"_s;
188 }
189 Q_UNREACHABLE();
190}
191
192/*!
193 Converts the InlineContent to a QJsonObject for template rendering.
194
195 The JSON uses kebab-case type IDs matching the convention in IR::Document
196 classification. Every element includes a \c text key: leaf elements use
197 their text field directly, while container elements produce a flattened
198 plain-text concatenation of their children. This ensures templates can
199 always access \c text without checking whether the element is a leaf or
200 container.
201
202 Optional fields (\c href, \c title) are omitted when empty.
203 The \c children array is omitted when empty.
204*/
206{
207 Q_ASSERT(children.isEmpty() || text.isEmpty());
208
209 QJsonObject json;
210 json["type"_L1] = inlineTypeId(type);
211 json["text"_L1] = plainText();
212
213 if (!href.isEmpty())
214 json["href"_L1] = href;
215 if (!title.isEmpty())
216 json["title"_L1] = title;
217
218 QJsonArray childArr;
219 for (const auto &child : children)
220 childArr.append(child.toJson());
221 json["children"_L1] = childArr;
222
223 if (!attributes.isEmpty())
224 json["attributes"_L1] = attributes;
225
226 return json;
227}
228
229/*!
230 Returns the concatenated plain text of this inline element and all its
231 children, recursively.
232
233 For leaf elements (Text, Code, Keyword, Target, Parameter, Image),
234 returns the \c text field. For LineBreak, returns a newline character.
235 For container elements (Bold, Italic, Link, etc.), concatenates the
236 plain text of all children.
237*/
239{
240 Q_ASSERT(children.isEmpty() || text.isEmpty());
241 if (type == InlineType::LineBreak)
242 return u"\n"_s;
243
244 if (!children.isEmpty()) {
245 QStringList parts;
246 parts.reserve(children.size());
247 for (const auto &child : children)
248 parts.append(child.plainText());
249 return parts.join(u""_s);
250 }
251
252 return text;
253}
254
255/*!
256 Converts the ContentBlock to a QJsonObject for template rendering.
257
258 The JSON uses kebab-case type IDs. A computed \c text key contains the
259 concatenated plain text of all inline content or children. Empty
260 collections (\c inlines, \c children, \c attributes) are omitted.
261*/
263{
264 QJsonObject json;
265 json["type"_L1] = blockTypeId(type);
266 json["text"_L1] = plainText();
267
268 if (!attributes.isEmpty())
269 json["attributes"_L1] = attributes;
270
271 if (!inlineContent.isEmpty()) {
272 QJsonArray arr;
273 for (const auto &inline_ : inlineContent)
274 arr.append(inline_.toJson());
275 json["inlines"_L1] = arr;
276 }
277
278 {
279 QJsonArray arr;
280 if (type == BlockType::Table) {
281 // Annotate body rows with their 1-based body-row index so
282 // templates can compute alternating-row classes from a
283 // counter that ignores header rows. Counting raw row index
284 // would shift parity by one whenever a header is present.
285 qsizetype bodyIndex = 0;
286 for (const auto &child : children) {
287 QJsonObject childJson = child.toJson();
288 if (child.type == BlockType::TableRow) {
289 ++bodyIndex;
290 QJsonObject attrs = childJson.value("attributes"_L1).toObject();
291 attrs["bodyIndex"_L1] = bodyIndex;
292 childJson["attributes"_L1] = attrs;
293 }
294 arr.append(childJson);
295 }
296 json["rows"_L1] = arr;
297 } else {
298 for (const auto &child : children)
299 arr.append(child.toJson());
300
301 if (type == BlockType::TableRow || type == BlockType::TableHeaderRow)
302 json["cells"_L1] = arr;
303 else
304 json["children"_L1] = arr;
305 }
306 }
307
308 return json;
309}
310
311/*!
312 Returns the concatenated plain text of this block's content,
313 recursively. Collects inline text first, then child block text.
314 Blocks may have both inline content and children when the atom
315 chain mixes inline and block-level elements within the same
316 container.
317*/
319{
320 QStringList parts;
321
322 if (!inlineContent.isEmpty()) {
323 parts.reserve(inlineContent.size());
324 for (const auto &inline_ : inlineContent)
325 parts.append(inline_.plainText());
326 }
327
328 if (!children.isEmpty()) {
329 QStringList childParts;
330 for (const auto &child : children)
331 childParts.append(child.plainText());
332 if (!parts.isEmpty())
333 parts.append(u"\n"_s);
334 parts.append(childParts.join(u"\n"_s));
335 }
336
337 return parts.join(u""_s);
338}
339
340} // namespace IR
341
342QT_END_NAMESPACE
Definition builder.cpp:14
static QString inlineTypeId(InlineType type)
static QString blockTypeId(BlockType type)
BlockType
InlineType
Combined button and popup list for selecting options.
Represents a structural block element in documentation.
QJsonObject toJson() const
Converts the ContentBlock to a QJsonObject for template rendering.
QString plainText() const
Returns the concatenated plain text of this block's content, recursively.
Represents inline content within a documentation block.
QJsonObject toJson() const
Converts the InlineContent to a QJsonObject for template rendering.
QString plainText() const
Returns the concatenated plain text of this inline element and all its children, recursively.