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
templategenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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 "aggregate.h"
7#include "codemarker.h"
9#include "config.h"
12#include "documentwriter.h"
13#include "generator.h"
14#include "hrefresolver.h"
15#include "injabridge.h"
16#include "ir/builder.h"
17#include "ir/document.h"
18#include "linkresolver.h"
19#include "node.h"
20#include "nodeextractor.h"
21#include "outputcontext.h"
23#include "pagenode.h"
24#include "qdocdatabase.h"
25#include "qmltypenode.h"
26#include "tree.h"
27#include "utilities.h"
28
29#include <utility>
30
31#include <QtCore/qdir.h>
32#include <QtCore/qdiriterator.h>
33#include <QtCore/qfile.h>
34#include <QtCore/qfileinfo.h>
35#include <QtCore/qloggingcategory.h>
36#include <QtCore/qtextstream.h>
37
39
40Q_LOGGING_CATEGORY(lcQDocTemplateGenerator, "qt.qdoc.templategenerator")
41
42using namespace Qt::Literals;
43
44static QString nodeTypeKey(const Node *node);
45static void resolveDocumentLinks(LinkResolver *resolver, IR::Document &ir, const Node *relative);
46
47/*!
48 \class TemplateGenerator
49 \internal
50 \brief Generates documentation using external templates and a pre-built IR.
51
52 TemplateGenerator implements OutputProducer and DocumentationHandler to
53 generate documentation without inheriting from Generator. It uses
54 DocumentationTraverser for tree traversal and delegates content generation
55 to templates via the IR system.
56
57 \section1 Architecture
58
59 The generator follows a composition-based design:
60 \list
61 \li \b{OutputProducer}: Lifecycle interface (prepare/produce/finalize).
62 \li \b{DocumentationHandler}: Content generation callbacks for
63 traverser.
64 \li \b{DocumentationTraverser}: Shared tree traversal logic.
65 \li \b{DocumentWriter}: Output abstraction (file or string for tests).
66 \endlist
67
68 \sa DocumentationTraverser, DocumentationHandler, OutputProducer,
69 IR::Builder
70*/
71
73 const QString &format)
74 : m_fileResolver(fileResolver)
75 , m_qdb(qdb)
76 , m_format(format.isEmpty() ? u"template"_s : format)
77{
79}
80
85
87{
88 createDefaultWriter();
89
90 const Config &config = Config::instance();
91
92 QString extensionConfig = config.get(m_format + ".extension"_L1).asString();
93 if (!extensionConfig.isEmpty())
94 m_fileExtension = extensionConfig;
95
96 QString templateDirConfig = config.get(m_format + ".templatedir"_L1).asString();
97
98 if (templateDirConfig.isEmpty()) {
99 m_templateDir.clear();
100 } else if (QDir::isAbsolutePath(templateDirConfig)) {
101 m_templateDir = templateDirConfig;
102 } else {
103 // Relative path: resolve relative to the qdocconf file's directory,
104 // consistent with how sourcedirs, headerdirs, etc. are resolved.
105 m_templateDir = QDir::cleanPath(
106 QDir(config.currentDir()).absoluteFilePath(templateDirConfig));
107 }
108
109 bool foundTemplates = false;
110 if (!m_templateDir.isEmpty()) {
111 QDir templateDir(m_templateDir);
112 if (templateDir.exists() && !templateDir.entryList(QDir::Files).isEmpty()) {
113 foundTemplates = true;
114 qCInfo(lcQDocTemplateGenerator) << "Using template directory:" << m_templateDir;
115 } else if (!templateDir.exists()) {
116 qCInfo(lcQDocTemplateGenerator)
117 << "Configured template directory does not exist:" << m_templateDir
118 << "- will use embedded templates";
119 } else {
120 qCInfo(lcQDocTemplateGenerator)
121 << "Configured template directory is empty:" << m_templateDir
122 << "- will use embedded templates";
123 }
124 } else {
125 qCInfo(lcQDocTemplateGenerator)
126 << "No external template directory configured - will use embedded templates";
127 }
128
129 if (!foundTemplates)
130 m_templateDir.clear();
131
132 m_emitStylesheet = config.get(m_format + ".stylesheet"_L1).asBool();
133
134 m_stylesheetName = config.get(m_format + ".stylesheetname"_L1).asString();
135 if (m_stylesheetName.isEmpty())
136 m_stylesheetName = u"qdoc-default.css"_s;
137
138 // Enforce plain filename — no directory separators, no traversal.
139 // The stylesheet is copied to and referenced from the output root,
140 // so subpaths would create inconsistencies between theme-provided
141 // assets and the QRC fallback.
142 if (m_stylesheetName.contains('/'_L1)
143 || m_stylesheetName.contains('\\'_L1)
144 || m_stylesheetName.contains(".."_L1)
145 || QDir::isAbsolutePath(m_stylesheetName)) {
146 qCWarning(lcQDocTemplateGenerator)
147 << "Ignoring stylesheetname:" << m_stylesheetName
148 << "— must be a plain filename (no path separators)";
149 m_stylesheetName = u"qdoc-default.css"_s;
150 }
151
152 copyAssets();
153
154 // Construct link resolvers using OutputContext data.
155 // TemplateGenerator doesn't inherit from Generator, so we use
156 // m_context (OutputContext) which has the same prefix/suffix data.
157 HrefResolverConfig hrefConfig;
158 hrefConfig.project = m_context->project;
159 hrefConfig.fileExtension = m_fileExtension;
160 hrefConfig.useOutputSubdirs = m_context->useSubdirs;
161 hrefConfig.noLinkErrors = Generator::noLinkErrors();
162 hrefConfig.inclusionPolicy = config.createInclusionPolicy();
163 hrefConfig.outputPrefixFn = [this](const Node *node) {
164 return m_context->outputPrefix(nodeTypeKey(node));
165 };
166 hrefConfig.outputSuffixFn = [this](const Node *node) {
167 return m_context->outputSuffix(nodeTypeKey(node));
168 };
169 hrefConfig.cleanRefFn = [](const QString &ref) { return Generator::cleanRef(ref); };
170 hrefConfig.qmlTypeContextFn = []() -> const QmlTypeNode * {
172 };
173 m_hrefResolver = std::make_unique<HrefResolver>(hrefConfig);
174
175 LinkResolverConfig linkConfig;
176 linkConfig.autolinkErrors = Generator::autolinkErrors();
177 linkConfig.noLinkErrors = Generator::noLinkErrors();
178 m_linkResolver = std::make_unique<LinkResolver>(&m_qdb, *m_hrefResolver, linkConfig);
179}
180
182{
183 DocumentationTraverser traverser;
184 Node *root = m_qdb.primaryTreeRoot();
185 if (root)
186 traverser.traverse(root, *this);
187}
188
190{
191 m_writer.reset();
192}
193
195{
196 return m_format;
197}
198
199void TemplateGenerator::beginDocument(const QString &outputFileName)
200{
201 if (m_writer)
202 m_writer->beginDocument(outputFileName);
203}
204
206{
207 if (m_writer)
208 m_writer->endDocument();
209}
210
212{
213 if (!node->url().isEmpty())
214 return node->url();
215
216 // Special case for simple page nodes (\page commands) with explicit
217 // non-.html extensions. Use the normalized fileBase() but preserve
218 // user specified extension
220 QFileInfo originalName(node->name());
221 QString suffix = originalName.suffix();
222 if (!suffix.isEmpty() && suffix != "html"_L1) {
223 // User specified a non-.html extension - use normalized base + original extension
224 QString name = fileBase(node);
225 return name + '.'_L1 + suffix;
226 }
227 }
228
229 QString name = fileBase(node) + '.'_L1;
230 return name + m_fileExtension;
231}
232
234{
235 Q_UNUSED(marker);
236
237 IR::PageMetadata pm = NodeExtractor::extractPageMetadata(cn, m_hrefResolver.get());
238
239 IR::Builder builder;
240 IR::Document ir = builder.buildPageIR(std::move(pm));
241
242 if (m_linkResolver)
243 resolveDocumentLinks(m_linkResolver.get(), ir, cn);
244
245 resolveImagePaths(ir);
246 renderDocument(ir, "collection"_L1);
247}
248
250{
251 Q_UNUSED(marker);
252
253 IR::PageMetadata pm = NodeExtractor::extractPageMetadata(cn, m_hrefResolver.get());
254
255 IR::Builder builder;
256 IR::Document ir = builder.buildPageIR(std::move(pm));
257
258 if (m_linkResolver)
259 resolveDocumentLinks(m_linkResolver.get(), ir, cn);
260
261 resolveImagePaths(ir);
262 renderDocument(ir, "collection"_L1);
263}
264
266{
267 Q_UNUSED(marker);
268
269 IR::PageMetadata pm = NodeExtractor::extractPageMetadata(pn, m_hrefResolver.get());
270
271 IR::Builder builder;
272 IR::Document ir = builder.buildPageIR(std::move(pm));
273
274 if (m_linkResolver)
275 resolveDocumentLinks(m_linkResolver.get(), ir, pn);
276
277 resolveImagePaths(ir);
278 renderDocument(ir, "page"_L1);
279}
280
282{
283 Q_UNUSED(marker);
284
285 // Placeholder - IR integration pending
286 if (m_writer && m_writer->isOpen()) {
287 m_writer->writeLine(QString(u"<!-- TemplateGenerator: C++ Reference Page for "_s
288 + aggregate->name() + u" -->"_s));
289 m_writer->writeLine(QString(u"<h1>"_s + aggregate->fullTitle() + u"</h1>"_s));
290 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
291 }
292}
293
295{
296 Q_UNUSED(marker);
297
298 IR::PageMetadata pm = NodeExtractor::extractPageMetadata(qcn, m_hrefResolver.get());
299 auto allMembers = NodeExtractor::extractAllMembersIR(qcn, m_hrefResolver.get());
300
301 IR::Builder builder;
302 IR::Document ir = builder.buildPageIR(std::move(pm));
303
304 if (allMembers)
305 ir.membersPageUrl = fileBase(qcn) + "-members."_L1 + m_fileExtension;
306
307 if (m_linkResolver)
308 resolveDocumentLinks(m_linkResolver.get(), ir, qcn);
309
310 resolveImagePaths(ir);
311 renderDocument(ir, "qmltype"_L1);
312
313 if (allMembers)
314 generateMemberListingPage(qcn, *allMembers);
315}
316
318{
319 Q_UNUSED(marker);
320
321 // Placeholder - IR integration pending
322 if (m_writer && m_writer->isOpen()) {
323 m_writer->writeLine(QString(u"<!-- TemplateGenerator: Proxy Page for "_s
324 + aggregate->name() + u" -->"_s));
325 m_writer->writeLine(QString(u"<h1>"_s + aggregate->fullTitle() + u"</h1>"_s));
326 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
327 }
328}
329
334
336{
337 return m_fileExtension;
338}
339
340/*!
341 \internal
342 Render phase: Format pre-built IR according to a template.
343
344 This is TemplateGenerator's core responsibility. It receives IR and
345 produces formatted output without any knowledge of Nodes or the database.
346*/
347void TemplateGenerator::renderDocument(const IR::Document &ir, const QString &templateBaseName)
348{
349 const QString templateFileName = templateBaseName + '.'_L1 + m_fileExtension;
350 QString templateContent;
351
352 if (!m_templateDir.isEmpty()) {
353 QString templatePath = m_templateDir + '/'_L1 + templateFileName;
354 QFile templateFile(templatePath);
355
356 if (templateFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
357 templateContent = QString::fromUtf8(templateFile.readAll());
358 templateFile.close();
359 }
360 }
361
362 if (templateContent.isEmpty()) {
363 QFile resourceFile(":/qdoc/templates/"_L1 + templateFileName);
364 if (resourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
365 templateContent = QString::fromUtf8(resourceFile.readAll());
366 resourceFile.close();
367 }
368 }
369
370 if (templateContent.isEmpty())
371 qFatal("TemplateGenerator: No template file found for extension '%s'. "
372 "Ensure '%s.%s' exists in the configured template directory or in resources.",
373 qPrintable(m_fileExtension), qPrintable(templateBaseName),
374 qPrintable(m_fileExtension));
375
376 QJsonObject json = ir.toJson();
377 json["stylesheetEnabled"_L1] = m_emitStylesheet;
378 json["stylesheetName"_L1] = m_stylesheetName;
379
380 auto includeCallback = [this](const QString &name) { return resolveInclude(name); };
381 QString rendered = InjaBridge::render(templateContent, json, includeCallback);
382
383 if (m_writer && m_writer->isOpen())
384 m_writer->write(rendered);
385}
386
387/*!
388 \internal
389 Render a raw QJsonObject through a named template.
390
391 Unlike renderDocument(), this takes an arbitrary JSON object rather
392 than an IR::Document. It's used for sub-pages (such as the member
393 listing page) where the data structure differs from Document's.
394*/
395void TemplateGenerator::renderJson(const QJsonObject &json, const QString &templateBaseName)
396{
397 const QString templateFileName = templateBaseName + '.'_L1 + m_fileExtension;
398 QString templateContent;
399
400 if (!m_templateDir.isEmpty()) {
401 QString templatePath = m_templateDir + '/'_L1 + templateFileName;
402 QFile templateFile(templatePath);
403
404 if (templateFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
405 templateContent = QString::fromUtf8(templateFile.readAll());
406 templateFile.close();
407 }
408 }
409
410 if (templateContent.isEmpty()) {
411 QFile resourceFile(":/qdoc/templates/"_L1 + templateFileName);
412 if (resourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
413 templateContent = QString::fromUtf8(resourceFile.readAll());
414 resourceFile.close();
415 }
416 }
417
418 if (templateContent.isEmpty())
419 qFatal("TemplateGenerator: No template file found for '%s'. "
420 "Ensure '%s.%s' exists in the configured template directory or in resources.",
421 qPrintable(templateBaseName), qPrintable(templateBaseName),
422 qPrintable(m_fileExtension));
423
424 QJsonObject enrichedJson = json;
425 enrichedJson["stylesheetEnabled"_L1] = m_emitStylesheet;
426 enrichedJson["stylesheetName"_L1] = m_stylesheetName;
427
428 auto includeCallback = [this](const QString &name) { return resolveInclude(name); };
429 QString rendered = InjaBridge::render(templateContent, enrichedJson, includeCallback);
430
431 if (m_writer && m_writer->isOpen())
432 m_writer->write(rendered);
433}
434
435/*!
436 \internal
437 Generate a member listing sub-page for a C++ class or QML type.
438
439 Opens a new output file, renders the all-members data through the
440 \c members template, and closes the file. This mirrors
441 HtmlGenerator::generateAllMembersFile() but uses the IR pipeline
442 and template system instead of inline HTML generation.
443*/
444void TemplateGenerator::generateMemberListingPage(const Node *node,
445 const IR::AllMembersIR &allMembers)
446{
447 const QString membersFileName =
448 fileBase(node) + "-members."_L1 + m_fileExtension;
449
450 QJsonObject json = allMembers.toJson();
451 json["title"_L1] = QString("List of All Members for "_L1 + allMembers.typeName);
452
453 beginDocument(membersFileName);
454 renderJson(json, "members"_L1);
456}
457
458/*!
459 \internal
460 Resolves an Inja \c{{% include %}} directive to template content.
461
462 Searches the filesystem first (for user-customized templates) and then
463 Qt's embedded resource system (for bundled defaults). This enables
464 Inja's include mechanism to work with Qt resources, where
465 \c{std::ifstream} can't open \c{:/} paths.
466
467 Returns the file content as a QString, or an empty QString if the file
468 isn't found in either location.
469*/
470QString TemplateGenerator::resolveInclude(const QString &name) const
471{
472 if (!m_templateDir.isEmpty()) {
473 QFile file(m_templateDir + '/'_L1 + name);
474 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
475 return QString::fromUtf8(file.readAll());
476 }
477
478 QFile resourceFile(":/qdoc/templates/"_L1 + name);
479 if (resourceFile.open(QIODevice::ReadOnly | QIODevice::Text))
480 return QString::fromUtf8(resourceFile.readAll());
481
482 return {};
483}
484
485/*!
486 \internal
487 Returns the output prefix/suffix key for a node based on its genus.
488
489 This is used to look up configured prefixes and suffixes from OutputContext.
490*/
491static QString nodeTypeKey(const Node *node)
492{
493 if (node->isPageNode()) {
494 switch (node->genus()) {
495 case Genus::QML:
496 return u"QML"_s;
497 case Genus::CPP:
498 return u"CPP"_s;
499 default:
500 break;
501 }
502 }
503 return QString();
504}
505
506static void resolveDocumentLinks(LinkResolver *resolver, IR::Document &ir, const Node *relative)
507{
508 if (!ir.body.isEmpty())
509 resolver->resolve(ir.body, relative);
510 for (auto &section : ir.detailSections) {
511 for (auto &member : section.members) {
512 if (!member.body.isEmpty())
513 resolver->resolve(member.body, relative);
514 if (!member.alsoList.isEmpty())
515 resolver->resolve(member.alsoList, relative);
516 }
517 }
518}
519
520/*!
521 \internal
522 Computes the base filename for a node, delegating the core computation
523 to Utilities::computeFileBase().
524
525 This handles caching and adapts the TemplateGenerator's OutputContext-based
526 prefix/suffix lookup to the shared interface.
527*/
528QString TemplateGenerator::fileBase(const Node *node) const
529{
530 if (!node->isPageNode() && !node->isCollectionNode())
531 node = node->parent();
532
533 if (node->hasFileNameBase())
534 return node->fileNameBase();
535
536 QString result = Utilities::computeFileBase(
537 node, m_context->project,
538 [this](const Node *n) -> QString {
539 if (n->isCollectionNode())
540 return {};
541 return m_context->outputPrefix(nodeTypeKey(n));
542 },
543 [this](const Node *n) { return m_context->outputSuffix(nodeTypeKey(n)); });
544
545 const_cast<Node *>(node)->setFileNameBase(result);
546 return result;
547}
548
549/*!
550 \internal
551 Creates the production FileDocumentWriter.
552
553 This is called during initialization to create the default writer
554 that writes to the filesystem. For testing, a mock writer can be
555 injected by setting m_writer before calling prepare().
556
557 Also initializes m_context with configuration values, enabling
558 OutputContext-based access to output settings.
559*/
560void TemplateGenerator::createDefaultWriter()
561{
562 // Initialize OutputContext from configuration
563 const Config &config = Config::instance();
564 m_context.emplace(OutputContext::fromConfig(config, format()));
565
566 if (m_writer)
567 return; // Writer already set (e.g., injected for testing)
568
569 m_writer = std::make_unique<FileDocumentWriter>(*m_context);
570}
571
572/*!
573 \internal
574 Walks content blocks recursively, resolving image filenames and copying
575 image files to the output directory.
576
577 For each InlineContent with type Image, the method resolves the raw
578 filename through FileResolver, copies the file to output/images/, and
579 prefixes the href with the images subdirectory path so that templates
580 render correct src attributes.
581
582*/
583void TemplateGenerator::resolveImagePaths(IR::Document &ir)
584{
585 if (!m_context)
586 return;
587
588 const QString &outDir = m_context->outputDir.path();
589 if (outDir.isEmpty())
590 return;
591
592 const QString imagesDir = u"images"_s;
593 const QString imagesDestDir = outDir + '/'_L1 + imagesDir;
594
595 QDir().mkpath(imagesDestDir);
596
597 auto resolveInlines = [&](QList<IR::InlineContent> &inlines) {
598 for (auto &inline_ : inlines) {
599 if (inline_.type != IR::InlineType::Image)
600 continue;
601
602 auto resolved = m_fileResolver.resolve(inline_.href);
603 if (!resolved)
604 continue;
605
606 const QString fileName = QFileInfo(resolved->get_path()).fileName();
607 QFile::copy(resolved->get_path(), imagesDestDir + '/'_L1 + fileName);
608 inline_.href = imagesDir + '/'_L1 + fileName;
609 }
610 };
611
612 std::function<void(QList<IR::ContentBlock> &)> walkBlocks;
613 walkBlocks = [&](QList<IR::ContentBlock> &blocks) {
614 for (auto &block : blocks) {
615 resolveInlines(block.inlineContent);
616 if (!block.children.isEmpty())
617 walkBlocks(block.children);
618 }
619 };
620
621 walkBlocks(ir.body);
622
623 for (auto &section : ir.detailSections) {
624 for (auto &member : section.members) {
625 walkBlocks(member.body);
626 walkBlocks(member.alsoList);
627 }
628 }
629}
630
631/*!
632 \internal
633 Copies static assets to the output directory using a two-tier resolution
634 strategy: templatedir assets take priority, with QRC defaults as fallback.
635
636 When a template directory provides an \c{assets/} subdirectory, all files
637 within it are copied recursively to the output directory, preserving the
638 subdirectory structure. This supports fonts, images, and other static
639 resources organized in subdirectories.
640
641 After copying theme assets, the method checks whether a stylesheet is
642 needed (controlled by \c{m_emitStylesheet}). If the theme didn't provide
643 one, the default QRC stylesheet is copied with the configured filename.
644*/
645void TemplateGenerator::copyAssets()
646{
647 if (!m_context)
648 return;
649
650 const QString &outDir = m_context->outputDir.path();
651 if (outDir.isEmpty())
652 return;
653
654 QSet<QString> themeAssets;
655
656 if (!m_templateDir.isEmpty()) {
657 QDir assetsDir(m_templateDir + "/assets"_L1);
658 if (assetsDir.exists()) {
659 QDirIterator it(assetsDir.path(), QDir::Files,
660 QDirIterator::Subdirectories);
661 int count = 0;
662 while (it.hasNext()) {
663 it.next();
664 QString rel = assetsDir.relativeFilePath(it.filePath());
665 QString dst = outDir + '/'_L1 + rel;
666 QDir().mkpath(QFileInfo(dst).path());
667 QFile::remove(dst);
668 if (QFile::copy(it.filePath(), dst)) {
669 themeAssets.insert(rel);
670 qCDebug(lcQDocTemplateGenerator) << "Asset (theme):" << rel;
671 ++count;
672 } else {
673 qCWarning(lcQDocTemplateGenerator)
674 << "Failed to copy asset:" << it.filePath() << "->" << dst;
675 }
676 }
677 if (count > 0)
678 qCInfo(lcQDocTemplateGenerator) << "Copied" << count << "theme asset(s)";
679 }
680 }
681
682 if (m_emitStylesheet && !themeAssets.contains(m_stylesheetName)) {
683 const QString dstCss = outDir + '/'_L1 + m_stylesheetName;
684 QFile::remove(dstCss);
685 QFile::copy(":/qdoc/templates/assets/qdoc-default.css"_L1, dstCss);
686 QFile(dstCss).setPermissions(QFile::ReadOwner | QFile::WriteOwner
687 | QFile::ReadGroup | QFile::ReadOther);
688 qCDebug(lcQDocTemplateGenerator) << "Asset (QRC fallback):" << m_stylesheetName;
689 }
690}
691
692QT_END_NAMESPACE
A class for holding the members of a collection of doc pages.
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:85
InclusionPolicy createInclusionPolicy() const
Definition config.cpp:1608
Traverses the node tree and dispatches to a handler for documentation generation.
void traverse(Node *root, DocumentationHandler &handler)
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
static bool noLinkErrors()
Definition generator.h:85
static QmlTypeNode * qmlTypeContext()
Definition generator.h:91
static bool autolinkErrors()
Definition generator.h:86
Assembles IR Documents from pre-extracted metadata.
Definition builder.h:15
Document buildPageIR(PageMetadata pm) const
Definition builder.cpp:59
Singleton registry for discovering output producers by format.
void registerProducer(OutputProducer *producer)
Registers producer with this registry.
void unregisterProducer(OutputProducer *producer)
Unregisters producer from this registry.
static OutputProducerRegistry & instance()
Returns the singleton registry instance.
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:19
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
void mergeCollections(CollectionNode *c)
Finds all the collection nodes with the same name and type as c and merges their members into the mem...
Generates documentation using external templates and a pre-built IR.
void generateProxyPage(Aggregate *aggregate, CodeMarker *marker) override
void endDocument() override
void beginDocument(const QString &fileName) override
void generateCppReferencePage(Aggregate *aggregate, CodeMarker *marker) override
void generatePageNode(PageNode *pn, CodeMarker *marker) override
void finalize() override
Finalizes output production.
void prepare() override
Prepares the producer for an output run.
QString fileExtension() const
TemplateGenerator(FileResolver &fileResolver, QDocDatabase &qdb, const QString &format=QString())
QString fileName(const Node *node) const override
void produce() override
Produces documentation output.
void generateCollectionNode(CollectionNode *cn, CodeMarker *marker) override
QString format() const override
Returns the format identifier for this producer (e.g., "HTML", "DocBook", "template").
void mergeCollections(CollectionNode *cn) override
void generateQmlTypePage(QmlTypeNode *qcn, CodeMarker *marker) override
void generateGenericCollectionPage(CollectionNode *cn, CodeMarker *marker) override
Definition builder.cpp:14
std::optional< IR::AllMembersIR > extractAllMembersIR(const PageNode *pn, const HrefResolver *hrefResolver)
IR::PageMetadata extractPageMetadata(const PageNode *pn, const HrefResolver *hrefResolver)
Combined button and popup list for selecting options.
Intermediate representation of the all-members listing page.
Definition member.h:112
Intermediate representation for a documentation topic.
Definition document.h:81
The Node class is the base class for all the nodes in QDoc's parse tree.
bool hasFileNameBase() const
Returns true if the node's file name base has been set.
Definition node.h:167
Genus genus() const override
Returns this node's Genus.
Definition node.h:85
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
Definition node.h:148
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:153
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:208
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:144
static QString nodeTypeKey(const Node *node)
static void resolveDocumentLinks(LinkResolver *resolver, IR::Document &ir, const Node *relative)