15#include "ir/builder.h"
16#include "ir/document.h"
30#include <QtCore/qdir.h>
31#include <QtCore/qfile.h>
32#include <QtCore/qfileinfo.h>
33#include <QtCore/qloggingcategory.h>
34#include <QtCore/qtextstream.h>
38Q_LOGGING_CATEGORY(lcQDocTemplateGenerator,
"qt.qdoc.templategenerator")
40using namespace Qt::Literals;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
68 const QString &format)
69 : m_fileResolver(fileResolver)
83 createDefaultWriter();
85 const Config &config = Config::instance();
87 QString extensionConfig = config.get(m_format +
".extension"_L1).asString();
88 if (!extensionConfig.isEmpty())
89 m_fileExtension = extensionConfig;
91 QString templateDirConfig = config.get(m_format +
".templatedir"_L1).asString();
93 if (templateDirConfig.isEmpty()) {
94 m_templateDir.clear();
95 }
else if (QDir::isAbsolutePath(templateDirConfig)) {
96 m_templateDir = templateDirConfig;
99 const QString &outDir = m_context->outputDir.path();
100 if (!outDir.isEmpty())
101 m_templateDir = outDir +
"/"_L1 + templateDirConfig;
103 m_templateDir = templateDirConfig;
106 bool foundTemplates =
false;
107 if (!m_templateDir.isEmpty()) {
108 QDir templateDir(m_templateDir);
109 if (templateDir.exists() && !templateDir.entryList(QDir::Files).isEmpty()) {
110 foundTemplates =
true;
111 qCInfo(lcQDocTemplateGenerator) <<
"Using template directory:" << m_templateDir;
112 }
else if (!templateDir.exists()) {
113 qCInfo(lcQDocTemplateGenerator)
114 <<
"Configured template directory does not exist:" << m_templateDir
115 <<
"- will use embedded templates";
117 qCInfo(lcQDocTemplateGenerator)
118 <<
"Configured template directory is empty:" << m_templateDir
119 <<
"- will use embedded templates";
122 qCInfo(lcQDocTemplateGenerator)
123 <<
"No external template directory configured - will use embedded templates";
127 m_templateDir.clear();
151 m_writer->beginDocument(outputFileName);
157 m_writer->endDocument();
162 if (!node->url().isEmpty())
169 QFileInfo originalName(node->name());
170 QString suffix = originalName.suffix();
171 if (!suffix.isEmpty() && suffix !=
"html"_L1) {
173 QString name = fileBase(node);
174 return name +
'.'_L1 + suffix;
178 QString name = fileBase(node) +
'.'_L1;
179 return name + m_fileExtension;
187 if (m_writer && m_writer->isOpen()) {
188 m_writer->writeLine(QString(u"<!-- TemplateGenerator: Collection "_s + cn->name() + u" -->"_s));
189 m_writer->writeLine(QString(u"<h1>"_s + cn->fullTitle() + u"</h1>"_s));
190 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
199 if (m_writer && m_writer->isOpen()) {
200 m_writer->writeLine(QString(u"<!-- TemplateGenerator: Generic Collection "_s + cn->name() + u" -->"_s));
201 m_writer->writeLine(QString(u"<h1>"_s + cn->fullTitle() + u"</h1>"_s));
202 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
215 renderDocument(ir,
"page"_L1);
223 if (m_writer && m_writer->isOpen()) {
224 m_writer->writeLine(QString(u"<!-- TemplateGenerator: C++ Reference Page for "_s
225 + aggregate->name() + u" -->"_s));
226 m_writer->writeLine(QString(u"<h1>"_s + aggregate->fullTitle() + u"</h1>"_s));
227 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
236 if (m_writer && m_writer->isOpen()) {
237 m_writer->writeLine(QString(u"<!-- TemplateGenerator: QML Type Page for "_s
238 + qcn->name() + u" -->"_s));
239 m_writer->writeLine(QString(u"<h1>"_s + qcn->fullTitle() + u"</h1>"_s));
240 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
249 if (m_writer && m_writer->isOpen()) {
250 m_writer->writeLine(QString(u"<!-- TemplateGenerator: Proxy Page for "_s
251 + aggregate->name() + u" -->"_s));
252 m_writer->writeLine(QString(u"<h1>"_s + aggregate->fullTitle() + u"</h1>"_s));
253 m_writer->writeLine(u"<p>Template-based output (IR integration pending)</p>"_s);
264 return m_fileExtension;
268
269
270
271
272
273
276 const QString templateFileName = templateBaseName +
'.'_L1 + m_fileExtension;
277 QString templateContent;
279 if (!m_templateDir.isEmpty()) {
280 QString templatePath = m_templateDir +
'/'_L1 + templateFileName;
281 QFile templateFile(templatePath);
283 if (templateFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
284 templateContent = QString::fromUtf8(templateFile.readAll());
285 templateFile.close();
289 if (templateContent.isEmpty()) {
290 QFile resourceFile(
":/qdoc/templates/"_L1 + templateFileName);
291 if (resourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
292 templateContent = QString::fromUtf8(resourceFile.readAll());
293 resourceFile.close();
297 if (templateContent.isEmpty())
298 qFatal(
"TemplateGenerator: No template file found for extension '%s'. "
299 "Ensure '%s.%s' exists in the configured template directory or in resources.",
300 qPrintable(m_fileExtension), qPrintable(templateBaseName),
301 qPrintable(m_fileExtension));
303 auto includeCallback = [
this](
const QString &name) {
return resolveInclude(name); };
304 QString rendered = InjaBridge::render(templateContent, ir.toJson(), includeCallback);
306 if (m_writer && m_writer->isOpen())
307 m_writer->write(rendered);
311
312
313
314
315
316
317
318
319
320
321
324 if (!m_templateDir.isEmpty()) {
325 QFile file(m_templateDir +
'/'_L1 + name);
326 if (file.open(QIODevice::ReadOnly | QIODevice::Text))
327 return QString::fromUtf8(file.readAll());
330 QFile resourceFile(
":/qdoc/templates/"_L1 + name);
331 if (resourceFile.open(QIODevice::ReadOnly | QIODevice::Text))
332 return QString::fromUtf8(resourceFile.readAll());
338
339
340
341
342
359
360
361
362
363
364
365
372 return node->fileNameBase();
374 QString result = Utilities::computeFileBase(
375 node, m_context->project,
376 [
this](
const Node *n) -> QString {
377 if (n->isCollectionNode())
379 return m_context->outputPrefix(nodeTypeKey(n));
381 [
this](
const Node *n) {
return m_context->outputSuffix(nodeTypeKey(n)); });
383 const_cast<
Node *>(node)->setFileNameBase(result);
388
389
390
391
392
393
394
395
396
397
401 const Config &config = Config::instance();
402 m_context.emplace(OutputContext::fromConfig(config, format()));
407 m_writer = std::make_unique<FileDocumentWriter>(*m_context);
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...
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...
Assembles IR Documents from pre-extracted metadata.
Document buildPageIR(PageMetadata pm) const
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.
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
~TemplateGenerator() override
void generateGenericCollectionPage(CollectionNode *cn, CodeMarker *marker) override
Combined button and popup list for selecting options.
Intermediate representation for a documentation topic.
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.
Genus genus() const override
Returns this node's Genus.
virtual bool isPageNode() const
Returns true if this node represents something that generates a documentation page.
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Aggregate * parent() const
Returns the node's parent pointer.
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
static QString nodeTypeKey(const Node *node)