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
webxmlgenerator.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 "aggregate.h"
8#include "config.h"
11#include "node.h"
12#include "nodecontext.h"
13#include "propertynode.h"
14#include "qdocdatabase.h"
15#include "quoter.h"
17#include "utilities.h"
18#include "textutils.h"
19
20#include <QtCore/qxmlstream.h>
21
23
24using namespace Qt::StringLiterals;
25
26static CodeMarker *marker_ = nullptr;
27
29
31{
32 m_indexWriteState = {};
34}
35
40
42{
43 return "WebXML";
44}
45
47{
48 // As this is meant to be an intermediate format,
49 // use .html for internal references. The name of
50 // the output file is set separately in
51 // beginSubPage() calls.
52 return "html";
53}
54
55/*!
56 Most of the output is generated by QDocIndexFiles and the append() callback.
57 Some pages produce supplementary output while being generated, and that's
58 handled here.
59*/
60qsizetype WebXMLGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
61{
62 if (m_supplement && currentWriter)
63 addAtomElements(*currentWriter, atom, relative, marker);
64 return 0;
65}
66
68{
69 QByteArray data;
70 QXmlStreamWriter writer(&data);
71 writer.setAutoFormatting(true);
72 beginSubPage(aggregate, Generator::fileName(aggregate, "webxml"));
73 writer.writeStartDocument();
74 writer.writeStartElement("WebXML");
75 writer.writeStartElement("document");
76
77 generateIndexSections(writer, aggregate);
78
79 writer.writeEndElement(); // document
80 writer.writeEndElement(); // WebXML
81 writer.writeEndDocument();
82
83 out() << data;
84 endSubPage();
85}
86
88{
89 QByteArray data;
90 currentWriter.emplace(&data);
91 currentWriter->setAutoFormatting(true);
92 beginSubPage(pn, Generator::fileName(pn, "webxml"));
93 currentWriter->writeStartDocument();
94 currentWriter->writeStartElement("WebXML");
95 currentWriter->writeStartElement("document");
96
97 generateIndexSections(*currentWriter, pn);
98
99 currentWriter->writeEndElement(); // document
100 currentWriter->writeEndElement(); // WebXML
101 currentWriter->writeEndDocument();
102
103 out() << data;
104 endSubPage();
105}
106
107void WebXMLGenerator::generateExampleFilePage(const PageNode *en, ResolvedFile resolved_file, CodeMarker* /* marker */)
108{
109 // TODO: [generator-insufficient-structural-abstraction]
110
111 QByteArray data;
112 QXmlStreamWriter writer(&data);
113 writer.setAutoFormatting(true);
114 beginSubPage(en, linkForExampleFile(resolved_file.get_query(), "webxml"));
115 writer.writeStartDocument();
116 writer.writeStartElement("WebXML");
117 writer.writeStartElement("document");
118 writer.writeStartElement("page");
119 writer.writeAttribute("name", resolved_file.get_query());
120 writer.writeAttribute("href", linkForExampleFile(resolved_file.get_query()));
121 const QString title = exampleFileTitle(static_cast<const ExampleNode *>(en), resolved_file.get_query());
122 writer.writeAttribute("title", title);
123 writer.writeAttribute("fulltitle", title);
124 writer.writeAttribute("subtitle", resolved_file.get_query());
125 writer.writeStartElement("description");
126
127 if (Config::instance().get(CONFIG_LOCATIONINFO).asBool()) {
128 writer.writeAttribute("path", resolved_file.get_path());
129 writer.writeAttribute("line", "0");
130 writer.writeAttribute("column", "0");
131 }
132
133 Quoter quoter;
134 Doc::quoteFromFile(en->doc().location(), quoter, std::move(resolved_file));
135 QString code = quoter.quoteTo(en->location(), QString(), QString());
136 writer.writeTextElement("code", trimmedTrailing(code, QString(), QString()));
137
138 writer.writeEndElement(); // description
139 writer.writeEndElement(); // page
140 writer.writeEndElement(); // document
141 writer.writeEndElement(); // WebXML
142 writer.writeEndDocument();
143
144 out() << data;
145 endSubPage();
146}
147
148void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, Node *node)
149{
150 marker_ = CodeMarker::markerForFileName(node->location().filePath());
151 const Config &config = Config::instance();
153 context.documentLocation = [this](const Node *target) {
154 return fullDocumentLocation(target);
155 };
156 context.inclusionPolicy = config.createInclusionPolicy();
157 context.storeLocationInfo = config.get(CONFIG_LOCATIONINFO).asBool();
159 auto qdocIndexFiles = QDocIndexFiles::qdocIndexFiles();
160 if (qdocIndexFiles) {
161 qdocIndexFiles->generateIndexSections(writer, node, context, m_indexWriteState, this);
162 // generateIndexSections returns early for collection nodes (groups,
163 // modules, QML modules, concepts) so their member listings can be
164 // written last in an index file. When rendering a single collection
165 // page, that leaves the document empty, so emit the section explicitly.
166 if (node->isCollectionNode())
167 std::ignore = qdocIndexFiles->generateIndexSection(writer, node, context,
168 m_indexWriteState, this);
169 }
170}
171
172// Handles callbacks from QDocIndexFiles to add documentation to node
173void WebXMLGenerator::append(QXmlStreamWriter &writer, Node *node)
174{
175 Q_ASSERT(marker_);
176
177 // The index walk doesn't visit shared comment nodes; capture the prose here
178 const Doc &effectiveDoc = (node->doc().body().isEmpty() && node->isSharingComment())
180 : node->doc();
181
182 writer.writeStartElement("description");
183 if (Config::instance().get(CONFIG_LOCATIONINFO).asBool()) {
184 writer.writeAttribute("path", effectiveDoc.location().filePath());
185 writer.writeAttribute("line", QString::number(effectiveDoc.location().lineNo()));
186 writer.writeAttribute("column", QString::number(effectiveDoc.location().columnNo()));
187 }
188
189 if (node->isTextPageNode())
190 generateRelations(writer, node);
191
192 if (node->isModule()) {
193 writer.writeStartElement("generatedlist");
194 writer.writeAttribute("contents", "classesbymodule");
195 auto *cnn = static_cast<CollectionNode *>(node);
196
197 if (cnn->hasNamespaces()) {
198 writer.writeStartElement("section");
199 writer.writeStartElement("heading");
200 writer.writeAttribute("level", "1");
201 writer.writeCharacters("Namespaces");
202 writer.writeEndElement(); // heading
203 NodeMap namespaces{cnn->getMembers(NodeType::Namespace)};
204 generateAnnotatedList(writer, node, namespaces);
205 writer.writeEndElement(); // section
206 }
207 if (cnn->hasClasses()) {
208 writer.writeStartElement("section");
209 writer.writeStartElement("heading");
210 writer.writeAttribute("level", "1");
211 writer.writeCharacters("Classes");
212 writer.writeEndElement(); // heading
213 NodeMap classes{cnn->getMembers([](const Node *n){ return n->isClassNode(); })};
214 generateAnnotatedList(writer, node, classes);
215 writer.writeEndElement(); // section
216 }
217 writer.writeEndElement(); // generatedlist
218 }
219
220 m_inLink = m_inSectionHeading = m_hasQuotingInformation = false;
221
222 const Atom *atom = effectiveDoc.body().firstAtom();
223 while (atom)
224 atom = addAtomElements(writer, atom, node, marker_);
225
226 QList<Text> alsoList = effectiveDoc.alsoList();
227 supplementAlsoList(node, alsoList);
228
229 if (!alsoList.isEmpty()) {
230 writer.writeStartElement("see-also");
231 for (const auto &item : alsoList) {
232 const auto *atom = item.firstAtom();
233 while (atom)
234 atom = addAtomElements(writer, atom, node, marker_);
235 }
236 writer.writeEndElement(); // see-also
237 }
238
239 if (node->isExample()) {
240 m_supplement = true;
242 m_supplement = false;
243 } else if (node->isGroup()) {
244 auto *cn = static_cast<CollectionNode *>(node);
245 if (!cn->noAutoList())
246 generateAnnotatedList(writer, node, cn->members());
247 }
248
249 writer.writeEndElement(); // description
250}
251
253{
254 // Don't generate nodes that are already processed, or if they're not supposed to
255 // generate output, ie. external, index or images nodes.
256 if (!node->url().isNull() || node->isExternalPage() || node->isIndexNode())
257 return;
258
259 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
260 const NodeContext context = node->createContext();
261 if (!InclusionFilter::isIncluded(policy, context))
262 return;
263
264 if (node->parent()) {
265 if (node->isNamespace() || node->isClassNode() || node->isHeader())
266 generateCppReferencePage(static_cast<Aggregate *>(node), nullptr);
267 else if (node->isCollectionNode()) {
268 if (node->wasSeen()) {
269 // see remarks in base class impl.
270 m_qdb->mergeCollections(static_cast<CollectionNode *>(node));
271 generatePageNode(static_cast<PageNode *>(node), nullptr);
272 }
273 } else if (node->isTextPageNode())
274 generatePageNode(static_cast<PageNode *>(node), nullptr);
275 // else if TODO: anything else?
276 }
277
278 if (node->isAggregate()) {
279 auto *aggregate = static_cast<Aggregate *>(node);
280 for (auto c : aggregate->childNodes()) {
281 if ((c->isAggregate() || c->isTextPageNode() || c->isCollectionNode())
282 && !c->isPrivate())
283 generateDocumentation(c);
284 }
285 }
286}
287
288const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Atom *atom,
289 const Node *relative, CodeMarker *marker)
290{
291 bool keepQuoting = false;
292
293 if (!atom)
294 return nullptr;
295
296 switch (atom->type()) {
297 case Atom::AnnotatedList: {
298 const CollectionNode *cn = m_qdb->getCollectionNode(atom->string(), NodeType::Group);
299 if (cn)
300 generateAnnotatedList(writer, relative, cn->members());
301 } break;
302 case Atom::AutoLink: {
303 const Node *node{nullptr};
304 QString link{};
305
306 if (!m_inLink && !m_inSectionHeading) {
307 link = getAutoLink(atom, relative, &node, Genus::API);
308
309 if (!link.isEmpty() && node && node->isDeprecated()
310 && relative->parent() != node && !relative->isDeprecated()) {
311 link.clear();
312 }
313 }
314
315 startLink(writer, atom, node, link);
316
317 writer.writeCharacters(atom->string());
318
319 if (m_inLink) {
320 writer.writeEndElement(); // link
321 m_inLink = false;
322 }
323
324 break;
325 }
326 case Atom::BaseName:
327 break;
328 case Atom::BriefLeft:
329
330 writer.writeStartElement("brief");
331 switch (relative->nodeType()) {
333 writer.writeCharacters("This property");
334 break;
336 writer.writeCharacters("This variable");
337 break;
338 default:
339 break;
340 }
341 if (relative->isProperty() || relative->isVariable()) {
342 QString str;
343 const Atom *a = atom->next();
344 while (a != nullptr && a->type() != Atom::BriefRight) {
346 str += a->string();
347 a = a->next();
348 }
349 str[0] = str[0].toLower();
350 if (str.endsWith('.'))
351 str.chop(1);
352
353 const QList<QStringView> words = QStringView{str}.split(' ');
354 if (!words.isEmpty()) {
355 QStringView first(words.at(0));
356 if (!(first == u"contains" || first == u"specifies" || first == u"describes"
357 || first == u"defines" || first == u"holds" || first == u"determines"))
358 writer.writeCharacters(" holds ");
359 else
360 writer.writeCharacters(" ");
361 }
362 }
363 break;
364
365 case Atom::BriefRight:
366 if (relative->isProperty() || relative->isVariable())
367 writer.writeCharacters(".");
368
369 writer.writeEndElement(); // brief
370 break;
371
372 case Atom::C:
373 writer.writeStartElement("teletype");
374 if (m_inLink)
375 writer.writeAttribute("type", "normal");
376 else
377 writer.writeAttribute("type", "highlighted");
378
379 writer.writeCharacters(plainCode(atom->string()));
380 writer.writeEndElement(); // teletype
381 break;
382
383 case Atom::Code:
384 if (!m_hasQuotingInformation)
385 writer.writeTextElement(
386 "code", trimmedTrailing(plainCode(atom->string()), QString(), QString()));
387 else
388 keepQuoting = true;
389 break;
390
391 case Atom::CodeBad:
392 writer.writeTextElement("badcode",
393 trimmedTrailing(plainCode(atom->string()), QString(), QString()));
394 break;
395
397 if (m_quoting) {
398 if (quoteCommand == "dots") {
399 writer.writeAttribute("indent", atom->string());
400 writer.writeCharacters("...");
401 } else {
402 writer.writeCharacters(atom->string());
403 }
404 writer.writeEndElement(); // code
405 keepQuoting = true;
406 }
407 break;
408
410 if (m_quoting) {
411 quoteCommand = atom->string();
412 writer.writeStartElement(quoteCommand);
413 }
414 break;
415
416 case Atom::DetailsSummaryLeft: // Ignore/skip \details summary
417 return atom->find(Atom::DetailsSummaryRight, nullptr);
418
420 break;
421
423 if (!m_inLink) {
424 QString link = linkForExampleFile(atom->string());
425 if (!link.isEmpty())
426 startLink(writer, atom, relative, link);
427 }
428 } break;
429
431 if (!m_inLink) {
432 QString link = atom->string();
433 if (!link.isEmpty())
434 startLink(writer, atom, nullptr, "images/used-in-examples/" + link);
435 }
436 } break;
437
439 writer.writeStartElement("footnote");
440 break;
441
443 writer.writeEndElement(); // footnote
444 break;
445
447 writer.writeEndElement(); // raw
448 break;
449 case Atom::FormatIf:
450 writer.writeStartElement("raw");
451 writer.writeAttribute("format", atom->string());
452 break;
454 if (atom->string() == ATOM_FORMATTING_BOLD)
455 writer.writeStartElement("bold");
456 else if (atom->string() == ATOM_FORMATTING_ITALIC)
457 writer.writeStartElement("italic");
458 else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
459 writer.writeStartElement("underline");
460 else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
461 writer.writeStartElement("subscript");
462 else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
463 writer.writeStartElement("superscript");
464 else if (atom->string() == ATOM_FORMATTING_TELETYPE || atom->string() == ATOM_FORMATTING_NOTRANSLATE)
465 writer.writeStartElement("teletype");
466 else if (atom->string() == ATOM_FORMATTING_PARAMETER)
467 writer.writeStartElement("argument");
468 else if (atom->string() == ATOM_FORMATTING_INDEX)
469 writer.writeStartElement("index");
470 } break;
471
473 if (atom->string() == ATOM_FORMATTING_BOLD)
474 writer.writeEndElement();
475 else if (atom->string() == ATOM_FORMATTING_ITALIC)
476 writer.writeEndElement();
477 else if (atom->string() == ATOM_FORMATTING_UNDERLINE)
478 writer.writeEndElement();
479 else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT)
480 writer.writeEndElement();
481 else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT)
482 writer.writeEndElement();
483 else if (atom->string() == ATOM_FORMATTING_TELETYPE || atom->string() == ATOM_FORMATTING_NOTRANSLATE)
484 writer.writeEndElement();
485 else if (atom->string() == ATOM_FORMATTING_PARAMETER)
486 writer.writeEndElement();
487 else if (atom->string() == ATOM_FORMATTING_INDEX)
488 writer.writeEndElement();
489 else if (atom->string() == ATOM_FORMATTING_TRADEMARK && appendTrademark(atom))
490 writer.writeCharacters(QChar(0x2122)); // 'TM' symbol
491 }
492 if (m_inLink) {
493 writer.writeEndElement(); // link
494 m_inLink = false;
495 }
496 break;
497
499 writer.writeStartElement("generatedlist");
500 writer.writeAttribute("contents", atom->string());
501 writer.writeEndElement();
502 break;
503
504 // TODO: The other generators treat inlineimage and image
505 // simultaneously as the diffirences aren't big. It should be
506 // possible to do the same for webxmlgenerator instead of
507 // repeating the code.
508
509 // TODO: [generator-insufficient-structural-abstraction]
510 case Atom::Image:
511 case Atom::InlineImage: {
512 auto maybe_resolved_file{file_resolver.resolve(atom->string())};
513 if (!maybe_resolved_file) {
514 // TODO: [uncentralized-admonition][failed-resolve-file]
515 relative->location().warning(QStringLiteral("Missing image: %1").arg(atom->string()));
516 } else {
517 ResolvedFile file{*maybe_resolved_file};
518 QString file_name{QFileInfo{file.get_path()}.fileName()};
519
520 // TODO: [uncentralized-output-directory-structure]
521 Config::copyFile(relative->doc().location(), file.get_path(), file_name,
522 "%1/%2"_L1.arg(outputDir(), imagesOutputDir()));
523
524 writer.writeStartElement(atom->typeString().toLower());
525 const auto &imgPath = "%1/%2"_L1.arg(imagesOutputDir(), file_name);
526 // TODO: [uncentralized-output-directory-structure]
527 writer.writeAttribute("href", imgPath);
528 writer.writeEndElement();
529 // TODO: [uncentralized-output-directory-structure]
530 setImageFileName(relative, imgPath);
531 }
532 break;
533 }
534 case Atom::ImageText:
535 break;
536
538 writer.writeStartElement("para");
539 writer.writeTextElement("bold", "Important:");
540 writer.writeCharacters(" ");
541 break;
542
544 writer.writeStartElement("legalese");
545 break;
546
548 writer.writeEndElement(); // legalese
549 break;
550
551 case Atom::Link:
552 case Atom::LinkNode:
553 if (!m_inLink) {
554 const Node *node = nullptr;
555 QString link = getLink(atom, relative, &node);
556 if (!link.isEmpty())
557 startLink(writer, atom, node, link);
558 }
559 break;
560
561 case Atom::ListLeft:
562 writer.writeStartElement("list");
563
564 if (atom->string() == ATOM_LIST_BULLET)
565 writer.writeAttribute("type", "bullet");
566 else if (atom->string() == ATOM_LIST_TAG)
567 writer.writeAttribute("type", "definition");
568 else if (atom->string() == ATOM_LIST_VALUE) {
569 if (relative->isEnumType())
570 writer.writeAttribute("type", "enum");
571 else
572 writer.writeAttribute("type", "definition");
573 } else {
574 writer.writeAttribute("type", "ordered");
575 if (atom->string() == ATOM_LIST_UPPERALPHA)
576 writer.writeAttribute("start", "A");
577 else if (atom->string() == ATOM_LIST_LOWERALPHA)
578 writer.writeAttribute("start", "a");
579 else if (atom->string() == ATOM_LIST_UPPERROMAN)
580 writer.writeAttribute("start", "I");
581 else if (atom->string() == ATOM_LIST_LOWERROMAN)
582 writer.writeAttribute("start", "i");
583 else if (atom->next() != nullptr) // ATOM_LIST_NUMERIC with explicit start
584 writer.writeAttribute("start", atom->next()->string());
585 else
586 writer.writeAttribute("start", "1");
587 }
588 break;
589
591 break;
592 case Atom::ListTagLeft: {
593 writer.writeStartElement("definition");
594
595 writer.writeTextElement(
596 "term", plainCode(marker->markedUpEnumValue(atom->next()->string(), relative)));
597 } break;
598
600 writer.writeEndElement(); // definition
601 break;
602
604 writer.writeStartElement("item");
605 break;
606
608 writer.writeEndElement(); // item
609 break;
610
611 case Atom::ListRight:
612 writer.writeEndElement(); // list
613 break;
614
615 case Atom::NoteLeft:
616 writer.writeStartElement("para");
617 writer.writeTextElement("bold", "Note:");
618 writer.writeCharacters(" ");
619 break;
620
621 // End admonition elements
623 case Atom::NoteRight:
625 writer.writeEndElement(); // para
626 break;
627
628 case Atom::Nop:
629 break;
630
632 case Atom::ParaLeft:
633 writer.writeStartElement("para");
634 break;
635
637 case Atom::ParaRight:
638 writer.writeEndElement(); // para
639 break;
640
642 writer.writeStartElement("quote");
643 break;
644
646 writer.writeEndElement(); // quote
647 break;
648
649 case Atom::RawString:
650 writer.writeCharacters(atom->string());
651 break;
652
654 writer.writeStartElement("section");
655 writer.writeAttribute("id",
656 TextUtils::asAsciiPrintable(Text::sectionHeading(atom).toString()));
657 break;
658
660 writer.writeEndElement(); // section
661 break;
662
664 writer.writeStartElement("heading");
665 int unit = atom->string().toInt(); // + hOffset(relative)
666 writer.writeAttribute("level", QString::number(unit));
667 m_inSectionHeading = true;
668 } break;
669
671 writer.writeEndElement(); // heading
672 m_inSectionHeading = false;
673 break;
674
677 break;
678
680 if (m_quoting) {
681 writer.writeStartElement(atom->string());
682 }
683 break;
684
686 if (m_quoting) {
687 writer.writeAttribute("identifier", atom->string());
688 writer.writeEndElement();
689 keepQuoting = true;
690 }
691 break;
692
694 if (m_quoting) {
695 const QString &location = atom->string();
696 writer.writeAttribute("location", location);
697 auto maybe_resolved_file{file_resolver.resolve(location)};
698 // const QString resolved = Doc::resolveFile(Location(), location);
699 if (maybe_resolved_file)
700 writer.writeAttribute("path", (*maybe_resolved_file).get_path());
701 else {
702 // TODO: [uncetnralized-admonition][failed-resolve-file]
703 QString details = std::transform_reduce(
704 file_resolver.get_search_directories().cbegin(),
705 file_resolver.get_search_directories().cend(),
706 u"Searched directories:"_s,
707 std::plus(),
708 [](const DirectoryPath &directory_path) -> QString { return u' ' + directory_path.value(); }
709 );
710
711 relative->location().warning(u"Cannot find file to quote from: %1"_s.arg(location), details);
712 }
713 }
714 break;
715
716 case Atom::String:
717 writer.writeCharacters(atom->string());
718 break;
719 case Atom::TableLeft:
720 writer.writeStartElement("table");
721 if (atom->string().contains("%"))
722 writer.writeAttribute("width", atom->string());
723 break;
724
725 case Atom::TableRight:
726 writer.writeEndElement(); // table
727 break;
728
730 writer.writeStartElement("header");
731 break;
732
734 writer.writeEndElement(); // header
735 break;
736
738 writer.writeStartElement("row");
739 break;
740
742 writer.writeEndElement(); // row
743 break;
744
745 case Atom::TableItemLeft: {
746 writer.writeStartElement("item");
747 QStringList spans = atom->string().split(",");
748 if (spans.size() == 2) {
749 if (spans.at(0) != "1")
750 writer.writeAttribute("colspan", spans.at(0).trimmed());
751 if (spans.at(1) != "1")
752 writer.writeAttribute("rowspan", spans.at(1).trimmed());
753 }
754 } break;
756 writer.writeEndElement(); // item
757 break;
758
760 // Skip to the closing \endtoc atom
761 if (const auto *endtoc = atom->find(Atom::TableOfContentsRight))
762 atom = endtoc;
763 break;
764
765 case Atom::Target:
766 writer.writeStartElement("target");
767 writer.writeAttribute("name", TextUtils::asAsciiPrintable(atom->string()));
768 writer.writeEndElement();
769 break;
770
772 writer.writeStartElement("para");
773 writer.writeTextElement("bold", "Warning:");
774 writer.writeCharacters(" ");
775 break;
776
779 writer.writeCharacters(atom->typeString());
780 break;
781 default:
782 break;
783 }
784
785 m_hasQuotingInformation = keepQuoting;
786 return atom->next();
787}
788
789void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom, const Node *node,
790 const QString &link)
791{
792 QString fullName = link;
793 if (node)
794 fullName = node->fullName();
795 if (!fullName.isEmpty() && !link.isEmpty()) {
796 writer.writeStartElement("link");
797 if (atom && !atom->string().isEmpty())
798 writer.writeAttribute("raw", atom->string());
799 else
800 writer.writeAttribute("raw", fullName);
801 writer.writeAttribute("href", link);
802 writer.writeAttribute("type", targetType(node));
803 if (node) {
804 switch (node->nodeType()) {
805 case NodeType::Enum:
806 writer.writeAttribute("enum", fullName);
807 break;
808 case NodeType::Example: {
809 const auto *en = static_cast<const ExampleNode *>(node);
810 const QString fileTitle = atom ? exampleFileTitle(en, atom->string()) : QString();
811 if (!fileTitle.isEmpty()) {
812 writer.writeAttribute("page", fileTitle);
813 break;
814 }
815 }
816 Q_FALLTHROUGH();
817 case NodeType::Page:
818 writer.writeAttribute("page", fullName);
819 break;
820 case NodeType::Property: {
821 const auto *propertyNode = static_cast<const PropertyNode *>(node);
822 if (!propertyNode->getters().empty())
823 writer.writeAttribute("getter", propertyNode->getters().at(0)->fullName());
824 } break;
825 default:
826 break;
827 }
828 }
829 m_inLink = true;
830 }
831}
832
833void WebXMLGenerator::endLink(QXmlStreamWriter &writer)
834{
835 if (m_inLink) {
836 writer.writeEndElement(); // link
837 m_inLink = false;
838 }
839}
840
841void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node)
842{
843 if (node && !node->links().empty()) {
844 std::pair<QString, QString> anchorPair;
845 const Node *linkNode;
846
847 for (auto it = node->links().cbegin(); it != node->links().cend(); ++it) {
848
849 linkNode = m_qdb->findNodeForTarget(it.value().first, node);
850
851 if (!linkNode)
852 linkNode = node;
853
854 if (linkNode == node)
855 anchorPair = it.value();
856 else
857 anchorPair = anchorForNode(linkNode);
858
859 writer.writeStartElement("relation");
860 writer.writeAttribute("href", anchorPair.first);
861 writer.writeAttribute("type", targetType(linkNode));
862
863 switch (it.key()) {
864 case Node::StartLink:
865 writer.writeAttribute("meta", "start");
866 break;
867 case Node::NextLink:
868 writer.writeAttribute("meta", "next");
869 break;
870 case Node::PreviousLink:
871 writer.writeAttribute("meta", "previous");
872 break;
873 case Node::ContentsLink:
874 writer.writeAttribute("meta", "contents");
875 break;
876 default:
877 writer.writeAttribute("meta", "");
878 }
879 writer.writeAttribute("description", anchorPair.second);
880 writer.writeEndElement(); // link
881 }
882 }
883}
884
885void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer, const Node *relative,
886 const NodeMap &nodeMap)
887{
888 generateAnnotatedList(writer, relative, nodeMap.values());
889}
890
891void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer, const Node *relative,
892 const NodeList &nodeList)
893{
894 writer.writeStartElement("table");
895 writer.writeAttribute("width", "100%");
896
897 for (const auto *node : nodeList) {
898 writer.writeStartElement("row");
899 writer.writeStartElement("item");
900 writer.writeStartElement("para");
901 const QString link = linkForNode(node, relative);
902 startLink(writer, node->doc().body().firstAtom(), node, link);
903 endLink(writer);
904 writer.writeEndElement(); // para
905 writer.writeEndElement(); // item
906
907 writer.writeStartElement("item");
908 writer.writeStartElement("para");
909 writer.writeCharacters(node->doc().briefText().toString());
910 writer.writeEndElement(); // para
911 writer.writeEndElement(); // item
912 writer.writeEndElement(); // row
913 }
914 writer.writeEndElement(); // table
915}
916
918{
919 return Generator::fileBase(node);
920}
921
922QT_END_NAMESPACE
#define ATOM_LIST_BULLET
Definition atom.h:209
#define ATOM_FORMATTING_TELETYPE
Definition atom.h:204
#define ATOM_LIST_LOWERALPHA
Definition atom.h:212
#define ATOM_FORMATTING_UNDERLINE
Definition atom.h:207
#define ATOM_LIST_UPPERALPHA
Definition atom.h:215
#define ATOM_FORMATTING_NOTRANSLATE
Definition atom.h:199
#define ATOM_LIST_TAG
Definition atom.h:210
#define ATOM_LIST_LOWERROMAN
Definition atom.h:213
#define ATOM_FORMATTING_SUBSCRIPT
Definition atom.h:202
#define ATOM_FORMATTING_BOLD
Definition atom.h:195
#define ATOM_FORMATTING_TRADEMARK
Definition atom.h:205
#define ATOM_LIST_VALUE
Definition atom.h:211
#define ATOM_FORMATTING_ITALIC
Definition atom.h:197
#define ATOM_LIST_UPPERROMAN
Definition atom.h:216
#define ATOM_FORMATTING_SUPERSCRIPT
Definition atom.h:203
#define ATOM_FORMATTING_INDEX
Definition atom.h:196
#define ATOM_FORMATTING_PARAMETER
Definition atom.h:200
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
AtomType type() const
Return the type of this atom.
Definition atom.h:144
@ CaptionLeft
Definition atom.h:29
@ ListTagLeft
Definition atom.h:67
@ TableRight
Definition atom.h:97
@ GeneratedList
Definition atom.h:52
@ BriefRight
Definition atom.h:27
@ CodeQuoteArgument
Definition atom.h:33
@ WarningLeft
Definition atom.h:110
@ TableOfContentsLeft
Definition atom.h:104
@ SidebarLeft
Definition atom.h:87
@ TableHeaderRight
Definition atom.h:99
@ InlineImage
Definition atom.h:58
@ TableRowRight
Definition atom.h:101
@ FootnoteRight
Definition atom.h:46
@ SnippetCommand
Definition atom.h:92
@ TableRowLeft
Definition atom.h:100
@ Nop
Definition atom.h:74
@ WarningRight
Definition atom.h:111
@ LegaleseRight
Definition atom.h:61
@ ListTagRight
Definition atom.h:68
@ CaptionRight
Definition atom.h:30
@ ListItemNumber
Definition atom.h:66
@ CodeBad
Definition atom.h:32
@ RawString
Definition atom.h:82
@ Target
Definition atom.h:106
@ AnnotatedList
Definition atom.h:22
@ SectionRight
Definition atom.h:84
@ SectionHeadingLeft
Definition atom.h:85
@ TableLeft
Definition atom.h:96
@ ListItemRight
Definition atom.h:70
@ Image
Definition atom.h:54
@ TableItemRight
Definition atom.h:103
@ ListItemLeft
Definition atom.h:69
@ ImportantRight
Definition atom.h:57
@ Code
Definition atom.h:31
@ String
Definition atom.h:95
@ ListLeft
Definition atom.h:65
@ CodeQuoteCommand
Definition atom.h:34
@ BriefLeft
Definition atom.h:26
@ ImageText
Definition atom.h:55
@ ExampleFileLink
Definition atom.h:43
@ LegaleseLeft
Definition atom.h:60
@ ListRight
Definition atom.h:71
@ C
Definition atom.h:28
@ ParaRight
Definition atom.h:78
@ FormattingLeft
Definition atom.h:50
@ FormattingRight
Definition atom.h:51
@ SectionHeadingRight
Definition atom.h:86
@ Link
Definition atom.h:63
@ ImportantLeft
Definition atom.h:56
@ FormatEndif
Definition atom.h:48
@ UnhandledFormat
Definition atom.h:109
@ ExampleImageLink
Definition atom.h:44
@ FootnoteLeft
Definition atom.h:45
@ AutoLink
Definition atom.h:23
@ SnippetLocation
Definition atom.h:94
@ TableHeaderLeft
Definition atom.h:98
@ QuotationLeft
Definition atom.h:80
@ SectionLeft
Definition atom.h:83
@ LinkNode
Definition atom.h:64
@ TableItemLeft
Definition atom.h:102
@ NoteRight
Definition atom.h:76
@ QuotationRight
Definition atom.h:81
@ ParaLeft
Definition atom.h:77
@ BaseName
Definition atom.h:24
@ FormatIf
Definition atom.h:49
@ SnippetIdentifier
Definition atom.h:93
@ NoteLeft
Definition atom.h:75
@ SidebarRight
Definition atom.h:88
@ UnknownCommand
Definition atom.h:112
@ DetailsSummaryRight
Definition atom.h:40
@ DetailsSummaryLeft
Definition atom.h:39
const Atom * next() const
Return the next atom in the atom list.
Definition atom.h:141
A class for holding the members of a collection of doc pages.
const NodeList & members() const
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:95
InclusionPolicy createInclusionPolicy() const
Definition config.cpp:1621
Definition doc.h:32
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:89
static void quoteFromFile(const Location &location, Quoter &quoter, ResolvedFile resolved_file, CodeMarker *marker=nullptr)
Definition doc.cpp:463
const Text & body() const
Definition doc.cpp:114
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
static bool appendTrademark(const Atom *atom)
Returns true if a trademark symbol should be appended to the output as determined by atom.
QTextStream & out()
void endSubPage()
Flush the text stream associated with the subpage, and then pop it off the text stream stack and dele...
void generateRequiredLinks(const Node *node, CodeMarker *marker)
Generates either a link to the project folder for example node, or a list of links files/images if 'u...
HtmlGenerator(FileResolver &file_resolver)
void initializeGenerator() override
Initializes the HTML output generator's data structures from the configuration (Config) singleton.
void terminateGenerator() override
Gracefully terminates the HTML output generator.
static bool isIncluded(const InclusionPolicy &policy, const NodeContext &context)
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:19
This class imports QDoc index files and serializes index sections for module indexes and WebXML outpu...
Definition text.h:12
static Text sectionHeading(const Atom *sectionBegin)
Definition text.cpp:157
const Atom * firstAtom() const
Definition text.h:33
bool isEmpty() const
Definition text.h:30
QString fileBase(const Node *node) const override
QString format() const override
Returns the format identifier for this producer (e.g., "HTML", "DocBook", "template").
virtual const Atom * addAtomElements(QXmlStreamWriter &writer, const Atom *atom, const Node *relative, CodeMarker *marker)
void terminateGenerator() override
Gracefully terminates the HTML output generator.
void generateDocumentation(Node *node) override
Recursive writing of HTML files from the root node.
void append(QXmlStreamWriter &writer, Node *node) override
virtual void generateIndexSections(QXmlStreamWriter &writer, Node *node)
void generatePageNode(PageNode *pn, CodeMarker *marker) override
Generate the HTML page for an entity that doesn't map to any underlying parsable C++ or QML element.
WebXMLGenerator(FileResolver &file_resolver)
QString fileExtension() const override
Returns "html" for this subclass of Generator.
void initializeGenerator() override
Initializes the HTML output generator's data structures from the configuration (Config) singleton.
void generateExampleFilePage(const PageNode *en, ResolvedFile file, CodeMarker *marker=nullptr) override
Generate an html file with the contents of a C++ or QML source file.
void generateCppReferencePage(Aggregate *aggregate, CodeMarker *marker) override
Generate a reference page for the C++ class, namespace, or header file documented in node using the c...
qsizetype generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) override
Most of the output is generated by QDocIndexFiles and the append() callback.
#define CONFIG_LOCATIONINFO
Definition config.h:421
NodeType
Definition genustypes.h:165
Combined button and popup list for selecting options.
QList< Node * > NodeList
Definition node.h:45
QMap< QString, Node * > NodeMap
Definition node.h:48
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isExternalPage() const
Returns true if the node type is ExternalPage.
Definition node.h:100
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:239
bool isGroup() const
Returns true if the node type is Group.
Definition node.h:105
SharedCommentNode * sharedCommentNode()
Definition node.h:252
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:110
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
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:94
virtual bool isTextPageNode() const
Returns true if the node is a PageNode but not an Aggregate.
Definition node.h:155
Aggregate * parent() const
Returns the node's parent pointer, that is, the aggregate under which the node is documented.
Definition node.h:211
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:133
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
Definition node.h:136
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:138
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:235
virtual bool wasSeen() const
Returns the seen flag data member of this node if it is a NamespaceNode or a CollectionNode.
Definition node.h:195
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:114
NodeContext createContext() const
Definition node.cpp:175
bool isModule() const
Returns true if the node type is Module.
Definition node.h:108
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
Definition node.h:250
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:145
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:146
bool isExample() const
Returns true if the node type is Example.
Definition node.h:99
bool isIndexNode() const
Returns true if this node was created from something in an index file.
Definition node.h:107
Represents a file that is reachable by QDoc based on its current configuration.
static CodeMarker * marker_