aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/generator/qtdoc
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/generator/qtdoc')
-rw-r--r--sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp1217
-rw-r--r--sources/shiboken6/generator/qtdoc/qtdocgenerator.h91
-rw-r--r--sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp274
-rw-r--r--sources/shiboken6/generator/qtdoc/qtxmltosphinx.h28
-rw-r--r--sources/shiboken6/generator/qtdoc/rstformat.h22
5 files changed, 1077 insertions, 555 deletions
diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
index 9e31b9366..2797ff254 100644
--- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
+++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
@@ -2,8 +2,10 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qtdocgenerator.h"
+#include "generatorcontext.h"
#include "codesnip.h"
#include "exception.h"
+#include "abstractmetaargument.h"
#include "apiextractorresult.h"
#include "qtxmltosphinx.h"
#include "rstformat.h"
@@ -13,6 +15,7 @@
#include <abstractmetafield.h>
#include <abstractmetafunction.h>
#include <abstractmetalang.h>
+#include "abstractmetalang_helpers.h"
#include <fileout.h>
#include <messages.h>
#include <modifications.h>
@@ -23,6 +26,8 @@
#include <functiontypeentry.h>
#include <enumtypeentry.h>
#include <complextypeentry.h>
+#include <flagstypeentry.h>
+#include <primitivetypeentry.h>
#include <qtdocparser.h>
#include <doxygenparser.h>
@@ -31,20 +36,84 @@
#include <QtCore/QTextStream>
#include <QtCore/QFile>
#include <QtCore/QDir>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QSet>
#include <algorithm>
#include <limits>
using namespace Qt::StringLiterals;
-static inline QString additionalDocumentationOption() { return QStringLiteral("additional-documentation"); }
+static inline QString classScope(const AbstractMetaClassCPtr &metaClass)
+{
+ return metaClass->fullName();
+}
+
+struct DocPackage
+{
+ QStringList classPages;
+ QStringList decoratorPages;
+ AbstractMetaFunctionCList globalFunctions;
+ AbstractMetaEnumList globalEnums;
+};
+
+struct DocGeneratorOptions
+{
+ QtXmlToSphinxParameters parameters;
+ QString extraSectionDir;
+ QString additionalDocumentationList;
+ QString inheritanceFile;
+ bool doxygen = false;
+ bool inheritanceDiagram = true;
+};
+
+struct GeneratorDocumentation
+{
+ struct Property
+ {
+ QString name;
+ Documentation documentation;
+ AbstractMetaType type;
+ AbstractMetaFunctionCPtr getter;
+ AbstractMetaFunctionCPtr setter;
+ AbstractMetaFunctionCPtr reset;
+ AbstractMetaFunctionCPtr notify;
+ };
+
+ AbstractMetaFunctionCList allFunctions;
+ AbstractMetaFunctionCList tocNormalFunctions; // Index lists
+ AbstractMetaFunctionCList tocVirtuals;
+ AbstractMetaFunctionCList tocSignalFunctions;
+ AbstractMetaFunctionCList tocSlotFunctions;
+ AbstractMetaFunctionCList tocStaticFunctions;
+
+ QList<Property> properties;
+};
+
+static bool operator<(const GeneratorDocumentation::Property &lhs,
+ const GeneratorDocumentation::Property &rhs)
+{
+ return lhs.name < rhs.name;
+}
+
+static QString propertyRefTarget(const QString &name)
+{
+ QString result = name;
+ // For sphinx referencing, disambiguate the target from the getter name
+ // by appending an invisible "Hangul choseong filler" character.
+ result.append(QChar(0x115F));
+ return result;
+}
+
+constexpr auto additionalDocumentationOption = "additional-documentation"_L1;
-static inline QString none() { return QStringLiteral("None"); }
+constexpr auto none = "None"_L1;
static bool shouldSkip(const AbstractMetaFunctionCPtr &func)
{
- // Constructors go to separate section
- if (DocParser::skipForQuery(func) || func->isConstructor())
+ if (DocParser::skipForQuery(func))
return true;
// Search a const clone (QImage::bits() vs QImage::bits() const)
@@ -77,22 +146,161 @@ static bool shouldSkip(const AbstractMetaFunctionCPtr &func)
static bool functionSort(const AbstractMetaFunctionCPtr &func1, const AbstractMetaFunctionCPtr &func2)
{
- return func1->name() < func2->name();
+ const bool ctor1 = func1->isConstructor();
+ if (ctor1 != func2->isConstructor())
+ return ctor1;
+ const QString &name1 = func1->name();
+ const QString &name2 = func2->name();
+ if (name1 != name2)
+ return name1 < name2;
+ return func1->arguments().size() < func2->arguments().size();
}
-static inline QVersionNumber versionOf(const TypeEntry *te)
+static inline QVersionNumber versionOf(const TypeEntryCPtr &te)
{
if (te) {
const auto version = te->version();
if (!version.isNull() && version > QVersionNumber(0, 0))
return version;
}
- return QVersionNumber();
+ return {};
+}
+
+struct docRef
+{
+ explicit docRef(const char *kind, QAnyStringView name) :
+ m_kind(kind), m_name(name) {}
+
+ const char *m_kind;
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, const docRef &dr)
+{
+ s << ':' << dr.m_kind << ":`" << dr.m_name << '`';
+ return s;
+}
+
+static QString fileNameToTocEntry(const QString &fileName)
+{
+ constexpr auto rstSuffix = ".rst"_L1;
+
+ QString result = fileName;
+ if (result.endsWith(rstSuffix))
+ result.chop(rstSuffix.size()); // Remove the .rst extension
+ // skip namespace if necessary
+ auto lastDot = result.lastIndexOf(u'.');
+ if (lastDot != -1)
+ result.remove(0, lastDot + 1);
+ return result;
+}
+
+static void readExtraDoc(const QFileInfo &fi,
+ const QString &moduleName,
+ const QString &outputDir,
+ DocPackage *docPackage, QStringList *extraTocEntries)
+{
+ // Strip to "Property.rst" in output directory
+ const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
+ QFile sourceFile(fi.absoluteFilePath());
+ if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile)));
+ return;
+ }
+ const QByteArray contents = sourceFile.readAll();
+ sourceFile.close();
+ QFile targetFile(outputDir + u'/' + newFileName);
+ if (!targetFile.open(QIODevice::WriteOnly|QIODevice::Text)) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForWriting(targetFile)));
+ return;
+ }
+ targetFile.write(contents);
+ if (contents.contains("decorator::"))
+ docPackage->decoratorPages.append(newFileName);
+ else
+ docPackage->classPages.append(newFileName);
+ extraTocEntries->append(fileNameToTocEntry(newFileName));
+}
+
+// Format a short documentation reference (automatically dropping the prefix
+// by using '~'), usable for property/attributes ("attr").
+struct shortDocRef
+{
+ explicit shortDocRef(const char *kind, QAnyStringView name) :
+ m_kind(kind), m_name(name) {}
+
+ const char *m_kind;
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, const shortDocRef &sdr)
+{
+ s << ':' << sdr.m_kind << ":`~" << sdr.m_name << '`';
+ return s;
+}
+
+struct functionRef : public docRef
+{
+ explicit functionRef(QAnyStringView name) : docRef("meth", name) {}
+};
+
+struct classRef : public shortDocRef
+{
+ explicit classRef(QAnyStringView name) : shortDocRef("class", name) {}
+};
+
+struct propRef : public shortDocRef // Attribute/property (short) reference
+{
+ explicit propRef(const QString &target) :
+ shortDocRef("attr", target) {}
+};
+
+struct headline
+{
+ explicit headline(QAnyStringView title, char underLineChar = '-') :
+ m_title(title), m_underLineChar(underLineChar) {}
+
+ QAnyStringView m_title;
+ char m_underLineChar;
+};
+
+static TextStream &operator<<(TextStream &s, const headline &h)
+{
+ s << h.m_title << '\n' << Pad(h.m_underLineChar, h.m_title.size()) << "\n\n";
+ return s;
+}
+
+struct pyClass
+{
+ explicit pyClass(QAnyStringView name) : m_name(name) {}
+
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, pyClass c)
+{
+ s << ".. py:class:: " << c.m_name << "\n\n";
+ return s;
}
+struct currentModule
+{
+ explicit currentModule(QAnyStringView module) : m_module(module) {}
+
+ QAnyStringView m_module;
+};
+
+static TextStream &operator<<(TextStream &s, const currentModule &m)
+{
+ s << ".. currentmodule:: " << m.m_module << "\n\n\n";
+ return s;
+}
+
+DocGeneratorOptions QtDocGenerator::m_options;
+
QtDocGenerator::QtDocGenerator()
{
- m_parameters.snippetComparison =
+ m_options.parameters.snippetComparison =
ReportHandler::debugLevel() >= ReportHandler::FullDebug;
}
@@ -103,7 +311,7 @@ QString QtDocGenerator::fileNameSuffix()
return u".rst"_s;
}
-bool QtDocGenerator::shouldGenerate(const TypeEntry *te) const
+bool QtDocGenerator::shouldGenerate(const TypeEntryCPtr &te) const
{
return Generator::shouldGenerate(te)
&& te->type() != TypeEntry::SmartPointerType;
@@ -117,28 +325,23 @@ QString QtDocGenerator::fileNameForContext(const GeneratorContext &context) cons
}
void QtDocGenerator::writeFormattedBriefText(TextStream &s, const Documentation &doc,
- const AbstractMetaClass *metaclass) const
+ const QString &scope) const
{
- writeFormattedText(s, doc.brief(), doc.format(), metaclass);
+ writeFormattedText(s, doc.brief(), doc.format(), scope);
}
void QtDocGenerator::writeFormattedDetailedText(TextStream &s, const Documentation &doc,
- const AbstractMetaClass *metaclass) const
+ const QString &scope) const
{
- writeFormattedText(s, doc.detailed(), doc.format(), metaclass);
+ writeFormattedText(s, doc.detailed(), doc.format(), scope);
}
void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
Documentation::Format format,
- const AbstractMetaClass *metaClass) const
+ const QString &scope) const
{
- QString metaClassName;
-
- if (metaClass)
- metaClassName = metaClass->fullName();
-
if (format == Documentation::Native) {
- QtXmlToSphinx x(this, m_parameters, doc, metaClassName);
+ QtXmlToSphinx x(this, m_options.parameters, doc, scope);
s << x;
} else {
const auto lines = QStringView{doc}.split(u'\n');
@@ -162,50 +365,71 @@ void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
s << '\n';
}
-static void writeInheritedByList(TextStream& s, const AbstractMetaClass* metaClass,
+static void writeInheritanceList(TextStream &s, const AbstractMetaClassCList& classes,
+ const char *label)
+{
+ s << "**" << label << ":** ";
+ for (qsizetype i = 0, size = classes.size(); i < size; ++i) {
+ if (i > 0)
+ s << ", ";
+ s << classRef(classes.at(i)->fullName());
+ }
+ s << "\n\n";
+}
+
+static void writeInheritedByList(TextStream &s, const AbstractMetaClassCPtr &metaClass,
const AbstractMetaClassCList& allClasses)
{
AbstractMetaClassCList res;
- for (auto c : allClasses) {
- if (c != metaClass && c->inheritsFrom(metaClass))
+ for (const auto &c : allClasses) {
+ if (c != metaClass && inheritsFrom(c, metaClass))
res << c;
}
- if (res.isEmpty())
- return;
+ if (!res.isEmpty())
+ writeInheritanceList(s, res, "Inherited by");
+}
+
+static void writeInheritedFromList(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ AbstractMetaClassCList res;
- s << "**Inherited by:** ";
- QStringList classes;
- for (auto c : qAsConst(res))
- classes << u":ref:`"_s + c->name() + u'`';
- s << classes.join(u", "_s) << "\n\n";
+ recurseClassHierarchy(metaClass, [&res, metaClass](const AbstractMetaClassCPtr &c) {
+ if (c.get() != metaClass.get())
+ res.append(c);
+ return false;
+ });
+
+ if (!res.isEmpty())
+ writeInheritanceList(s, res, "Inherits from");
}
void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
{
- const AbstractMetaClass *metaClass = classContext.metaClass();
+ AbstractMetaClassCPtr metaClass = classContext.metaClass();
qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
- m_packages[metaClass->package()] << fileNameForContext(classContext);
+ m_packages[metaClass->package()].classPages << fileNameForContext(classContext);
m_docParser->setPackageName(metaClass->package());
- m_docParser->fillDocumentation(const_cast<AbstractMetaClass*>(metaClass));
+ m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
- QString className = metaClass->name();
- s << ".. _" << className << ":" << "\n\n";
- s << ".. currentmodule:: " << metaClass->package() << "\n\n\n";
-
- s << className << '\n';
- s << Pad('*', className.size()) << "\n\n";
+ s << currentModule(metaClass->package()) << pyClass(metaClass->name());
+ Indentation indent(s);
auto documentation = metaClass->documentation();
+ const QString scope = classScope(metaClass);
if (documentation.hasBrief())
- writeFormattedBriefText(s, documentation, metaClass);
-
- s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
- << " :parts: 2\n\n";
- // TODO: This would be a parameter in the future...
+ writeFormattedBriefText(s, documentation, scope);
+ if (!metaClass->baseClasses().isEmpty()) {
+ if (m_options.inheritanceDiagram) {
+ s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
+ << " :parts: 2\n\n";
+ } else {
+ writeInheritedFromList(s, metaClass);
+ }
+ }
writeInheritedByList(s, metaClass, api().classes());
@@ -215,123 +439,113 @@ void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classC
if (metaClass->attributes().testFlag(AbstractMetaClass::Deprecated))
s << rstDeprecationNote("class");
- writeFunctionList(s, metaClass);
-
- //Function list
- auto functionList = metaClass->functions();
- std::sort(functionList.begin(), functionList.end(), functionSort);
+ const GeneratorDocumentation doc = generatorDocumentation(metaClass);
- s << "\nDetailed Description\n"
- "--------------------\n\n"
- << ".. _More:\n";
+ if (!doc.allFunctions.isEmpty() || !doc.properties.isEmpty()) {
+ s << '\n' << headline("Synopsis");
+ writePropertyToc(s, doc);
+ writeFunctionToc(s, u"Methods"_s, doc.tocNormalFunctions);
+ writeFunctionToc(s, u"Virtual methods"_s, doc.tocVirtuals);
+ writeFunctionToc(s, u"Slots"_s, doc.tocSlotFunctions);
+ writeFunctionToc(s, u"Signals"_s, doc.tocSignalFunctions);
+ writeFunctionToc(s, u"Static functions"_s, doc.tocStaticFunctions);
+ }
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass, nullptr);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, nullptr))
- writeFormattedDetailedText(s, documentation, metaClass);
+ s << "\n.. note::\n"
+ " This documentation may contain snippets that were automatically\n"
+ " translated from C++ to Python. We always welcome contributions\n"
+ " to the snippet translation. If you see an issue with the\n"
+ " translation, you can also let us know by creating a ticket on\n"
+ " https:/bugreports.qt.io/projects/PYSIDE\n\n";
- if (!metaClass->isNamespace())
- writeConstructors(s, metaClass);
- writeEnums(s, metaClass);
- if (!metaClass->isNamespace())
- writeFields(s, metaClass);
+ s << '\n' << headline("Detailed Description") << ".. _More:\n";
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass))
+ writeFormattedDetailedText(s, documentation, scope);
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass);
- QStringList uniqueFunctions;
- for (const auto &func : qAsConst(functionList)) {
- if (shouldSkip(func))
- continue;
+ writeEnums(s, metaClass->enums(), scope);
- if (func->isStatic())
- s << ".. staticmethod:: ";
- else
- s << ".. method:: ";
+ if (!doc.properties.isEmpty())
+ writeProperties(s, doc, metaClass);
- writeFunction(s, metaClass, func, !uniqueFunctions.contains(func->name()));
- uniqueFunctions.append(func->name());
- }
+ if (!metaClass->isNamespace())
+ writeFields(s, metaClass);
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, nullptr);
+ writeFunctions(s, doc.allFunctions, metaClass, scope);
}
-void QtDocGenerator::writeFunctionList(TextStream& s, const AbstractMetaClass* cppClass)
+void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title,
+ const AbstractMetaFunctionCList &functions)
{
- QStringList functionList;
- QStringList virtualList;
- QStringList signalList;
- QStringList slotList;
- QStringList staticFunctionList;
-
- const auto &classFunctions = cppClass->functions();
- for (const auto &func : classFunctions) {
- if (shouldSkip(func))
- continue;
-
- QString className;
- if (!func->isConstructor())
- className = cppClass->fullName() + u'.';
- else if (func->implementingClass() && func->implementingClass()->enclosingClass())
- className = func->implementingClass()->enclosingClass()->fullName() + u'.';
- QString funcName = getFuncName(func);
-
- QString str = u"def :meth:`"_s;
-
- str += funcName;
- str += u'<';
- if (!funcName.startsWith(className))
- str += className;
- str += funcName;
- str += u">` ("_s;
- str += parseArgDocStyle(cppClass, func);
- str += u')';
-
- if (func->isStatic())
- staticFunctionList << str;
- else if (func->isVirtual())
- virtualList << str;
- else if (func->isSignal())
- signalList << str;
- else if (func->isSlot())
- slotList << str;
- else
- functionList << str;
+ if (!functions.isEmpty()) {
+ s << headline(title, '^')
+ << ".. container:: function_list\n\n" << indent;
+ // Functions are sorted by the Metabuilder; erase overloads
+ QStringList toc;
+ toc.reserve(functions.size());
+ std::transform(functions.cbegin(), functions.end(),
+ std::back_inserter(toc), getFuncName);
+ toc.erase(std::unique(toc.begin(), toc.end()), toc.end());
+ for (const auto &func : toc)
+ s << "* def " << functionRef(func) << '\n';
+ s << outdent << "\n\n";
}
+}
- if (!functionList.isEmpty() || !staticFunctionList.isEmpty()) {
- QtXmlToSphinx::Table functionTable;
-
- s << "\nSynopsis\n--------\n\n";
+void QtDocGenerator::writePropertyToc(TextStream &s,
+ const GeneratorDocumentation &doc)
+{
+ if (doc.properties.isEmpty())
+ return;
- writeFunctionBlock(s, u"Functions"_s, functionList);
- writeFunctionBlock(s, u"Virtual functions"_s, virtualList);
- writeFunctionBlock(s, u"Slots"_s, slotList);
- writeFunctionBlock(s, u"Signals"_s, signalList);
- writeFunctionBlock(s, u"Static functions"_s, staticFunctionList);
+ s << headline("Properties", '^')
+ << ".. container:: function_list\n\n" << indent;
+ for (const auto &prop : doc.properties) {
+ s << "* " << propRef(propertyRefTarget(prop.name));
+ if (prop.documentation.hasBrief())
+ s << " - " << prop.documentation.brief();
+ s << '\n';
}
+ s << outdent << "\n\n";
}
-void QtDocGenerator::writeFunctionBlock(TextStream& s, const QString& title, QStringList& functions)
+void QtDocGenerator::writeProperties(TextStream &s,
+ const GeneratorDocumentation &doc,
+ const AbstractMetaClassCPtr &cppClass) const
{
- if (!functions.isEmpty()) {
- s << title << '\n'
- << Pad('^', title.size()) << '\n';
-
- std::sort(functions.begin(), functions.end());
-
- s << ".. container:: function_list\n\n";
- Indentation indentation(s);
- for (const QString &func : qAsConst(functions))
- s << "* " << func << '\n';
- s << "\n\n";
+ s << "\n.. note:: Properties can be used directly when "
+ << "``from __feature__ import true_property`` is used or via accessor "
+ << "functions otherwise.\n\n";
+
+ const QString scope = classScope(cppClass);
+ for (const auto &prop : doc.properties) {
+ const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false);
+ s << ".. py:property:: " << propertyRefTarget(prop.name)
+ << "\n :type: " << type << "\n\n\n";
+ if (!prop.documentation.isEmpty())
+ writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope);
+ s << "**Access functions:**\n";
+ if (prop.getter)
+ s << " * " << functionRef(prop.getter->name()) << '\n';
+ if (prop.setter)
+ s << " * " << functionRef(prop.setter->name()) << '\n';
+ if (prop.reset)
+ s << " * " << functionRef(prop.reset->name()) << '\n';
+ if (prop.notify)
+ s << " * Signal " << functionRef(prop.notify->name()) << '\n';
+ s << '\n';
}
}
-void QtDocGenerator::writeEnums(TextStream& s, const AbstractMetaClass* cppClass) const
+void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
+ const QString &scope) const
{
- static const QString section_title = u".. attribute:: "_s;
-
- for (const AbstractMetaEnum &en : cppClass->enums()) {
- s << section_title << cppClass->fullName() << '.' << en.name() << "\n\n";
- writeFormattedDetailedText(s, en.documentation(), cppClass);
+ for (const AbstractMetaEnum &en : enums) {
+ s << pyClass(en.name());
+ Indentation indent(s);
+ writeFormattedDetailedText(s, en.documentation(), scope);
const auto version = versionOf(en.typeEntry());
if (!version.isNull())
s << rstVersionAdded(version);
@@ -339,76 +553,20 @@ void QtDocGenerator::writeEnums(TextStream& s, const AbstractMetaClass* cppClass
}
-void QtDocGenerator::writeFields(TextStream& s, const AbstractMetaClass* cppClass) const
+void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const
{
- static const QString section_title = u".. attribute:: "_s;
+ constexpr auto section_title = ".. attribute:: "_L1;
+ const QString scope = classScope(cppClass);
for (const AbstractMetaField &field : cppClass->fields()) {
s << section_title << cppClass->fullName() << "." << field.name() << "\n\n";
- writeFormattedDetailedText(s, field.documentation(), cppClass);
- }
-}
-
-void QtDocGenerator::writeConstructors(TextStream& s, const AbstractMetaClass* cppClass) const
-{
- static const QString sectionTitle = u".. class:: "_s;
-
- auto lst = cppClass->queryFunctions(FunctionQueryOption::AnyConstructor
- | FunctionQueryOption::Visible);
- for (int i = lst.size() - 1; i >= 0; --i) {
- if (lst.at(i)->isModifiedRemoved() || lst.at(i)->functionType() == AbstractMetaFunction::MoveConstructorFunction)
- lst.removeAt(i);
- }
-
- bool first = true;
- QHash<QString, AbstractMetaArgument> arg_map;
-
- if (lst.isEmpty()) {
- s << sectionTitle << cppClass->fullName();
- } else {
- QByteArray pad;
- for (const auto &func : qAsConst(lst)) {
- s << pad;
- if (first) {
- first = false;
- s << sectionTitle;
- pad = QByteArray(sectionTitle.size(), ' ');
- }
- s << functionSignature(cppClass, func) << "\n\n";
-
- const auto version = versionOf(func->typeEntry());
- if (!version.isNull())
- s << pad << rstVersionAdded(version);
- if (func->isDeprecated())
- s << pad << rstDeprecationNote("constructor");
-
- const AbstractMetaArgumentList &arguments = func->arguments();
- for (const AbstractMetaArgument &arg : arguments) {
- if (!arg_map.contains(arg.name())) {
- arg_map.insert(arg.name(), arg);
- }
- }
- }
+ writeFormattedDetailedText(s, field.documentation(), scope);
}
-
- s << '\n';
-
- for (auto it = arg_map.cbegin(), end = arg_map.cend(); it != end; ++it) {
- s.indent(2);
- writeParameterType(s, cppClass, it.value());
- s.outdent(2);
- }
-
- s << '\n';
-
- for (const auto &func : qAsConst(lst))
- writeFormattedDetailedText(s, func->documentation(), cppClass);
}
-QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */,
- const AbstractMetaFunctionCPtr &func)
+QString QtDocGenerator::formatArgs(const AbstractMetaFunctionCPtr &func)
{
- QString ret;
+ QString ret = u"("_s;
int optArgs = 0;
const AbstractMetaArgumentList &arguments = func->arguments();
@@ -437,19 +595,19 @@ QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */
|| defValue.startsWith(u"QList")) {
defValue = u"list()"_s;
} else if (defValue == u"QVariant()") {
- defValue = none();
+ defValue = none;
} else {
defValue.replace(u"::"_s, u"."_s);
if (defValue == u"nullptr")
- defValue = none();
+ defValue = none;
else if (defValue == u"0" && arg.type().isObject())
- defValue = none();
+ defValue = none;
}
ret += u'=' + defValue;
}
}
- ret += QString(optArgs, u']');
+ ret += QString(optArgs, u']') + u')';
return ret;
}
@@ -459,12 +617,10 @@ void QtDocGenerator::writeDocSnips(TextStream &s,
TypeSystem::Language language)
{
Indentation indentation(s);
- QStringList invalidStrings;
+ static const QStringList invalidStrings{u"*"_s, u"//"_s, u"/*"_s, u"*/"_s};
const static QString startMarkup = u"[sphinx-begin]"_s;
const static QString endMarkup = u"[sphinx-end]"_s;
- invalidStrings << u"*"_s << u"//"_s << u"/*"_s << u"*/"_s;
-
for (const CodeSnip &snip : codeSnips) {
if ((snip.position != position) ||
!(snip.language & language))
@@ -472,8 +628,8 @@ void QtDocGenerator::writeDocSnips(TextStream &s,
QString code = snip.code();
while (code.contains(startMarkup) && code.contains(endMarkup)) {
- int startBlock = code.indexOf(startMarkup) + startMarkup.size();
- int endBlock = code.indexOf(endMarkup);
+ const auto startBlock = code.indexOf(startMarkup) + startMarkup.size();
+ const auto endBlock = code.indexOf(endMarkup);
if ((startBlock == -1) || (endBlock == -1))
break;
@@ -481,10 +637,10 @@ void QtDocGenerator::writeDocSnips(TextStream &s,
QString codeBlock = code.mid(startBlock, endBlock - startBlock);
const QStringList rows = codeBlock.split(u'\n');
int currentRow = 0;
- int offset = 0;
+ qsizetype offset = 0;
for (QString row : rows) {
- for (const QString &invalidString : qAsConst(invalidStrings))
+ for (const QString &invalidString : std::as_const(invalidStrings))
row.remove(invalidString);
if (row.trimmed().size() == 0) {
@@ -513,97 +669,127 @@ void QtDocGenerator::writeDocSnips(TextStream &s,
}
}
-bool QtDocGenerator::writeInjectDocumentation(TextStream& s,
- TypeSystem::DocModificationMode mode,
- const AbstractMetaClass* cppClass,
- const AbstractMetaFunctionCPtr &func)
+bool QtDocGenerator::writeDocModifications(TextStream &s,
+ const DocModificationList &mods,
+ TypeSystem::DocModificationMode mode,
+ const QString &scope) const
{
- Indentation indentation(s);
bool didSomething = false;
-
- const DocModificationList &mods = cppClass->typeEntry()->docModifications();
for (const DocModification &mod : mods) {
if (mod.mode() == mode) {
- bool modOk = func ? mod.signature() == func->minimalSignature() : mod.signature().isEmpty();
-
- if (modOk) {
- Documentation::Format fmt;
-
- if (mod.format() == TypeSystem::NativeCode)
- fmt = Documentation::Native;
- else if (mod.format() == TypeSystem::TargetLangCode)
- fmt = Documentation::Target;
- else
- continue;
-
- writeFormattedText(s, mod.code(), fmt, cppClass);
+ switch (mod.format()) {
+ case TypeSystem::NativeCode:
+ writeFormattedText(s, mod.code(), Documentation::Native, scope);
didSomething = true;
+ break;
+ case TypeSystem::TargetLangCode:
+ writeFormattedText(s, mod.code(), Documentation::Target, scope);
+ didSomething = true;
+ break;
+ default:
+ break;
}
}
}
+ return didSomething;
+}
+bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
+ TypeSystem::DocModificationMode mode,
+ const AbstractMetaClassCPtr &cppClass) const
+{
+ const bool didSomething =
+ writeDocModifications(s, DocParser::getDocModifications(cppClass),
+ mode, classScope(cppClass));
s << '\n';
- // TODO: Deprecate the use of doc string on glue code.
+ // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
// This is pre "add-function" and "inject-documentation" tags.
const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend
? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd;
- if (func)
- writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode);
- else
- writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode);
+ writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode);
return didSomething;
}
-QString QtDocGenerator::functionSignature(const AbstractMetaClass* cppClass,
- const AbstractMetaFunctionCPtr &func)
+bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
+ TypeSystem::DocModificationMode mode,
+ const DocModificationList &modifications,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &scope) const
{
- QString funcName;
+ const bool didSomething = writeDocModifications(s, modifications, mode, scope);
+ s << '\n';
- funcName = cppClass->fullName();
- if (!func->isConstructor())
- funcName += u'.' + getFuncName(func);
+ // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
+ // This is pre "add-function" and "inject-documentation" tags.
+ const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend
+ ? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd;
+ writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode);
+ return didSomething;
+}
- return funcName + u'(' + parseArgDocStyle(cppClass, func)
- + u')';
+static QString inline toRef(const QString &t)
+{
+ return ":class:`~"_L1 + t + u'`';
}
QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
- const AbstractMetaClass* cppClass) const
+ const AbstractMetaClassCPtr &cppClass,
+ bool createRef) const
{
static const QStringList nativeTypes =
- {boolT(), floatT(), intT(), pyObjectT(), pyStrT()};
+ {boolT, floatT, intT, pyObjectT, pyStrT};
- const QString name = type.name();
+ QString name = type.name();
if (nativeTypes.contains(name))
return name;
- static const QMap<QString, QString> typeMap = {
- { cPyObjectT(), pyObjectT() },
- { qStringT(), pyStrT() },
- { u"uchar"_s, pyStrT() },
+ if (type.typeUsagePattern() == AbstractMetaType::PrimitivePattern) {
+ const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name();
+ if (AbstractMetaType::cppSignedIntTypes().contains(basicName)
+ || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) {
+ return intT;
+ }
+ if (AbstractMetaType::cppFloatTypes().contains(basicName))
+ return floatT;
+ }
+
+ static const QSet<QString> stringTypes = {
+ u"uchar"_s, u"std::string"_s, u"std::wstring"_s,
+ u"std::stringview"_s, u"std::wstringview"_s,
+ qStringT, u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s
+ };
+ if (stringTypes.contains(name))
+ return pyStrT;
+
+ static const QHash<QString, QString> typeMap = {
+ { cPyObjectT, pyObjectT },
{ u"QStringList"_s, u"list of strings"_s },
- { qVariantT(), pyObjectT() },
- { u"quint32"_s, intT() },
- { u"uint32_t"_s, intT() },
- { u"quint64"_s, intT() },
- { u"qint64"_s, intT() },
- { u"size_t"_s, intT() },
- { u"int64_t"_s, intT() },
- { u"qreal"_s, floatT() }
+ { qVariantT, pyObjectT }
};
- const auto found = typeMap.find(name);
- if (found != typeMap.end())
+ const auto found = typeMap.constFind(name);
+ if (found != typeMap.cend())
return found.value();
- QString strType;
- if (type.isConstant() && name == u"char" && type.indirections() == 1) {
- strType = u"str"_s;
- } else if (name.startsWith(unsignedShortT())) {
- strType = intT();
- } else if (name.startsWith(unsignedT())) { // uint and ulong
- strType = intT();
- } else if (type.isContainer()) {
+ if (type.isFlags()) {
+ const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry());
+ auto enumTypeEntry = fte->originator();
+ auto enumName = enumTypeEntry->targetLangName();
+ if (createRef)
+ enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
+ return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
+ } else if (type.isEnum()) {
+ auto enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(type.typeEntry());
+ auto enumName = enumTypeEntry->targetLangName();
+ if (createRef)
+ enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
+ return createRef ? toRef(enumName) : enumName;
+ }
+
+ if (type.isConstant() && name == "char"_L1 && type.indirections() == 1)
+ return "str"_L1;
+
+ if (type.isContainer()) {
QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference);
strType.remove(u'*');
strType.remove(u'>');
@@ -619,16 +805,19 @@ QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.")
.arg(types[0], types[1]);
}
- } else {
- auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry());
- strType = k ? k->fullName() : type.name();
- strType = QStringLiteral(":any:`") + strType + u'`';
+ return strType;
}
- return strType;
+
+ if (auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()))
+ return createRef ? toRef(k->fullName()) : k->name();
+
+ return createRef ? toRef(name) : name;
}
-QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr& cppFunc)
+QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc)
{
+ if (cppFunc->isConstructor())
+ return "__init__"_L1;
QString result = cppFunc->name();
if (cppFunc->isOperatorOverload()) {
const QString pythonOperator = Generator::pythonOperatorFunctionName(result);
@@ -639,14 +828,16 @@ QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr& cppFunc)
return result;
}
-void QtDocGenerator::writeParameterType(TextStream& s, const AbstractMetaClass* cppClass,
+void QtDocGenerator::writeParameterType(TextStream &s,
+ const AbstractMetaClassCPtr &cppClass,
const AbstractMetaArgument &arg) const
{
s << ":param " << arg.name() << ": "
<< translateToPythonType(arg.type(), cppClass) << '\n';
}
-void QtDocGenerator::writeFunctionParametersType(TextStream &s, const AbstractMetaClass *cppClass,
+void QtDocGenerator::writeFunctionParametersType(TextStream &s,
+ const AbstractMetaClassCPtr &cppClass,
const AbstractMetaFunctionCPtr &func) const
{
s << '\n';
@@ -656,35 +847,57 @@ void QtDocGenerator::writeFunctionParametersType(TextStream &s, const AbstractMe
writeParameterType(s, cppClass, arg);
}
- if (!func->isConstructor() && !func->isVoid()) {
-
- QString retType;
+ QString retType;
+ if (!func->isConstructor()) {
// check if the return type was modified
- for (const auto &mod : func->modifications()) {
- for (const ArgumentModification &argMod : mod.argument_mods()) {
- if (argMod.index() == 0) {
- retType = argMod.modifiedType();
- break;
- }
- }
- }
-
- if (retType.isEmpty())
+ retType = func->modifiedTypeName();
+ if (retType.isEmpty() && !func->isVoid())
retType = translateToPythonType(func->type(), cppClass);
- s << ":rtype: " << retType << '\n';
}
+
+ if (!retType.isEmpty())
+ s << ":rtype: " << retType << '\n';
+
s << '\n';
}
-void QtDocGenerator::writeFunction(TextStream& s, const AbstractMetaClass* cppClass,
- const AbstractMetaFunctionCPtr &func, bool indexed)
+static bool containsFunctionDirective(const DocModification &dm)
{
- s << functionSignature(cppClass, func);
+ return dm.mode() != TypeSystem::DocModificationXPathReplace
+ && dm.code().contains(".. py:"_L1);
+}
- {
+void QtDocGenerator::writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
+ const AbstractMetaClassCPtr &cppClass, const QString &scope)
+{
+ QString lastName;
+ for (const auto &func : funcs) {
+ const bool indexed = func->name() != lastName;
+ lastName = func->name();
+ writeFunction(s, func, cppClass, scope, indexed);
+ }
+}
+
+void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass,
+ const QString &scope, bool indexed)
+{
+ const auto modifications = DocParser::getDocModifications(func, cppClass);
+
+ // Enable injecting parameter documentation by adding a complete function directive.
+ if (std::none_of(modifications.cbegin(), modifications.cend(), containsFunctionDirective)) {
+ if (func->ownerClass() == nullptr)
+ s << ".. py:function:: ";
+ else
+ s << (func->isStatic() ? ".. py:staticmethod:: " : ".. py:method:: ");
+ s << getFuncName(func) << formatArgs(func);
Indentation indentation(s);
if (!indexed)
s << "\n:noindex:";
+ if (func->cppAttributes().testFlag(FunctionAttribute::Final))
+ s << "\n:final:";
+ else if (func->isAbstract())
+ s << "\n:abstractmethod:";
s << "\n\n";
writeFunctionParametersType(s, cppClass, func);
const auto version = versionOf(func->typeEntry());
@@ -693,31 +906,94 @@ void QtDocGenerator::writeFunction(TextStream& s, const AbstractMetaClass* cppCl
if (func->isDeprecated())
s << rstDeprecationNote("function");
}
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, cppClass, func);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, cppClass, func)) {
- writeFormattedBriefText(s, func->documentation(), cppClass);
- writeFormattedDetailedText(s, func->documentation(), cppClass);
+
+ writeFunctionDocumentation(s, func, modifications, scope);
+
+ if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) {
+ const QString name = cppClass->propertySpecs().at(propIndex).name();
+ const QString target = propertyRefTarget(name);
+ if (func->isPropertyReader())
+ s << "\nGetter of property " << propRef(target) << " .\n\n";
+ else if (func->isPropertyWriter())
+ s << "\nSetter of property " << propRef(target) << " .\n\n";
+ else if (func->isPropertyResetter())
+ s << "\nReset function of property " << propRef(target) << " .\n\n";
+ else if (func->attributes().testFlag(AbstractMetaFunction::Attribute::PropertyNotify))
+ s << "\nNotification signal of property " << propRef(target) << " .\n\n";
+ }
+}
+
+void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const DocModificationList &modifications,
+ const QString &scope) const
+
+{
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) {
+ writeFormattedBriefText(s, func->documentation(), scope);
+ writeFormattedDetailedText(s, func->documentation(), scope);
}
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, cppClass, func);
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope);
+}
+
+static QStringList fileListToToc(const QStringList &items)
+{
+ QStringList result;
+ result.reserve(items.size());
+ std::transform(items.cbegin(), items.cend(), std::back_inserter(result),
+ fileNameToTocEntry);
+ return result;
+}
+
+static QStringList functionListToToc(const AbstractMetaFunctionCList &functions)
+{
+ QStringList result;
+ result.reserve(functions.size());
+ for (const auto &f : functions)
+ result.append(f->name());
+ // Functions are sorted by the Metabuilder; erase overloads
+ result.erase(std::unique(result.begin(), result.end()), result.end());
+ return result;
}
-static void writeFancyToc(TextStream& s, const QStringList& items)
+static QStringList enumListToToc(const AbstractMetaEnumList &enums)
+{
+ QStringList result;
+ result.reserve(enums.size());
+ for (const auto &e : enums)
+ result.append(e.name());
+ return result;
+}
+
+// Sort entries for a TOC by first character, dropping the
+// leading common Qt prefixes like 'Q'.
+static QChar sortKey(const QString &key)
+{
+ const auto size = key.size();
+ if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q') && key.at(1).isUpper())
+ return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S'
+ if (size >= 3 && key.startsWith("Q_"_L1))
+ return key.at(2).toUpper(); // "Q_ARG" -> 'A'
+ if (size >= 4 && key.startsWith("QT_"_L1))
+ return key.at(3).toUpper(); // "QT_TR" -> 'T'
+ auto idx = 0;
+ for (; idx < size && key.at(idx) == u'_'; ++idx) {
+ } // "__init__" -> 'I'
+ return idx < size ? key.at(idx).toUpper() : u'A';
+}
+
+static void writeFancyToc(TextStream& s, QAnyStringView title,
+ const QStringList& items,
+ QLatin1StringView referenceType)
{
using TocMap = QMap<QChar, QStringList>;
+
+ if (items.isEmpty())
+ return;
+
TocMap tocMap;
- QChar idx;
- for (QString item : items) {
- if (item.isEmpty())
- continue;
- item.chop(4); // Remove the .rst extension
- // skip namespace if necessary
- const QString className = item.split(u'.').last();
- if (className.startsWith(u'Q') && className.length() > 1)
- idx = className[1];
- else
- idx = className[0];
- tocMap[idx] << item;
- }
+ for (const QString &item : items)
+ tocMap[sortKey(item)] << item;
static const qsizetype numColumns = 4;
@@ -726,13 +1002,13 @@ static void writeFancyToc(TextStream& s, const QStringList& items)
QtXmlToSphinx::TableRow row;
const QString charEntry = u"**"_s + it.key() + u"**"_s;
row << QtXmlToSphinx::TableCell(charEntry);
- for (const QString &item : qAsConst(it.value())) {
+ for (const QString &item : std::as_const(it.value())) {
if (row.size() >= numColumns) {
table.appendRow(row);
row.clear();
row << QtXmlToSphinx::TableCell(QString{});
}
- const QString entry = u"* :doc:`"_s + item + u'`';
+ const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`';
row << QtXmlToSphinx::TableCell(entry);
}
if (row.size() > 1)
@@ -740,24 +1016,70 @@ static void writeFancyToc(TextStream& s, const QStringList& items)
}
table.normalize();
- s << ".. container:: pysidetoc\n\n";
+ s << '\n' << headline(title) << ".. container:: pysidetoc\n\n";
table.format(s);
}
bool QtDocGenerator::finishGeneration()
{
- if (!api().classes().isEmpty())
+ for (const auto &f : api().globalFunctions()) {
+ auto ncf = std::const_pointer_cast<AbstractMetaFunction>(f);
+ m_docParser->fillGlobalFunctionDocumentation(ncf);
+ m_packages[f->targetLangPackage()].globalFunctions.append(f);
+ }
+
+ for (auto e : api().globalEnums()) {
+ m_docParser->fillGlobalEnumDocumentation(e);
+ m_packages[e.typeEntry()->targetLangPackage()].globalEnums.append(e);
+ }
+
+ if (!m_packages.isEmpty())
writeModuleDocumentation();
- if (!m_additionalDocumentationList.isEmpty())
+ if (!m_options.additionalDocumentationList.isEmpty())
writeAdditionalDocumentation();
+ if (!m_options.inheritanceFile.isEmpty() && !writeInheritanceFile())
+ return false;
return true;
}
+bool QtDocGenerator::writeInheritanceFile()
+{
+ QFile inheritanceFile(m_options.inheritanceFile);
+ if (!inheritanceFile.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw Exception(msgCannotOpenForWriting(m_options.inheritanceFile));
+
+ QJsonObject dict;
+ for (const auto &c : api().classes()) {
+ const auto &bases = c->baseClasses();
+ if (!bases.isEmpty()) {
+ QJsonArray list;
+ for (const auto &base : bases)
+ list.append(QJsonValue(base->fullName()));
+ dict[c->fullName()] = list;
+ }
+ }
+ QJsonDocument document;
+ document.setObject(dict);
+ inheritanceFile.write(document.toJson(QJsonDocument::Compact));
+ return true;
+}
+
+// Remove function entries that have extra documentation pages
+static inline void removeExtraDocs(const QStringList &extraTocEntries,
+ AbstractMetaFunctionCList *functions)
+{
+ auto predicate = [&extraTocEntries](const AbstractMetaFunctionCPtr &f) {
+ return extraTocEntries.contains(f->name());
+ };
+ functions->erase(std::remove_if(functions->begin(),functions->end(), predicate),
+ functions->end());
+}
+
void QtDocGenerator::writeModuleDocumentation()
{
- QMap<QString, QStringList>::iterator it = m_packages.begin();
- for (; it != m_packages.end(); ++it) {
- std::sort(it.value().begin(), it.value().end());
+ for (auto it = m_packages.begin(), end = m_packages.end(); it != end; ++it) {
+ auto &docPackage = it.value();
+ std::sort(docPackage.classPages.begin(), docPackage.classPages.end());
QString key = it.key();
key.replace(u'.', u'/');
@@ -766,9 +1088,7 @@ void QtDocGenerator::writeModuleDocumentation()
TextStream& s = output.stream;
const QString &title = it.key();
- s << ".. module:: " << title << "\n\n"
- << title << '\n'
- << Pad('*', title.length()) << "\n\n";
+ s << ".. module:: " << title << "\n\n" << headline(title, '*');
// Store the it.key() in a QString so that it can be stripped off unwanted
// information when neeeded. For example, the RST files in the extras directory
@@ -779,11 +1099,12 @@ void QtDocGenerator::writeModuleDocumentation()
moduleName.remove(0, lastIndex + 1);
// Search for extra-sections
- if (!m_extraSectionDir.isEmpty()) {
- QDir extraSectionDir(m_extraSectionDir);
+ QStringList extraTocEntries;
+ if (!m_options.extraSectionDir.isEmpty()) {
+ QDir extraSectionDir(m_options.extraSectionDir);
if (!extraSectionDir.exists()) {
- const QString m = QStringLiteral("Extra sections directory ") +
- m_extraSectionDir + QStringLiteral(" doesn't exist");
+ const QString m = u"Extra sections directory "_s +
+ m_options.extraSectionDir + u" doesn't exist"_s;
throw Exception(m);
}
@@ -791,31 +1112,26 @@ void QtDocGenerator::writeModuleDocumentation()
const QString filter = moduleName + u".?*.rst"_s;
const auto fileList =
extraSectionDir.entryInfoList({filter}, QDir::Files, QDir::Name);
- for (const auto &fi : fileList) {
- // Strip to "Property.rst" in output directory
- const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
- it.value().append(newFileName);
- const QString newFilePath = outputDir + u'/' + newFileName;
- if (QFile::exists(newFilePath))
- QFile::remove(newFilePath);
- if (!QFile::copy(fi.absoluteFilePath(), newFilePath)) {
- qCDebug(lcShibokenDoc).noquote().nospace() << "Error copying extra doc "
- << QDir::toNativeSeparators(fi.absoluteFilePath())
- << " to " << QDir::toNativeSeparators(newFilePath);
- }
- }
+ for (const auto &fi : fileList)
+ readExtraDoc(fi, moduleName, outputDir, &docPackage, &extraTocEntries);
}
+ removeExtraDocs(extraTocEntries, &docPackage.globalFunctions);
+ const bool hasGlobals = !docPackage.globalFunctions.isEmpty()
+ || !docPackage.globalEnums.isEmpty();
+ const QString globalsPage = moduleName + "_globals.rst"_L1;
+
s << ".. container:: hide\n\n" << indent
<< ".. toctree::\n" << indent
<< ":maxdepth: 1\n\n";
- for (const QString &className : qAsConst(it.value()))
+ if (hasGlobals)
+ s << globalsPage << '\n';
+ for (const QString &className : std::as_const(docPackage.classPages))
s << className << '\n';
- s << "\n\n" << outdent << outdent
- << "Detailed Description\n--------------------\n\n";
+ s << "\n\n" << outdent << outdent << headline("Detailed Description");
// module doc is always wrong and C++istic, so go straight to the extra directory!
- QFile moduleDoc(m_extraSectionDir + u'/' + moduleName
+ QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName
+ u".rst"_s);
if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) {
s << moduleDoc.readAll();
@@ -826,19 +1142,48 @@ void QtDocGenerator::writeModuleDocumentation()
if (moduleDoc.format() == Documentation::Native) {
QString context = it.key();
QtXmlToSphinx::stripPythonQualifiers(&context);
- QtXmlToSphinx x(this, m_parameters, moduleDoc.detailed(), context);
+ QtXmlToSphinx x(this, m_options.parameters, moduleDoc.detailed(), context);
s << x;
} else {
s << moduleDoc.detailed();
}
}
- s << "\nList of Classes\n"
- << "---------------\n\n";
- writeFancyToc(s, it.value());
+ writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages),
+ "class"_L1);
+ writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages),
+ "deco"_L1);
+ writeFancyToc(s, "List of Functions", functionListToToc(docPackage.globalFunctions),
+ "py:func"_L1);
+ writeFancyToc(s, "List of Enumerations", enumListToToc(docPackage.globalEnums),
+ "any"_L1);
output.done();
+
+ if (hasGlobals)
+ writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage);
+ }
+}
+
+void QtDocGenerator::writeGlobals(const QString &package,
+ const QString &fileName,
+ const DocPackage &docPackage)
+{
+ FileOut output(fileName);
+ TextStream &s = output.stream;
+
+ // Write out functions with injected documentation
+ if (!docPackage.globalFunctions.isEmpty()) {
+ s << currentModule(package) << headline("Functions");
+ writeFunctions(s, docPackage.globalFunctions, {}, {});
+ }
+
+ if (!docPackage.globalEnums.isEmpty()) {
+ s << headline("Enumerations");
+ writeEnums(s, docPackage.globalEnums, package);
}
+
+ output.done();
}
static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
@@ -853,7 +1198,7 @@ static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
void QtDocGenerator::writeAdditionalDocumentation() const
{
- QFile additionalDocumentationFile(m_additionalDocumentationList);
+ QFile additionalDocumentationFile(m_options.additionalDocumentationList);
if (!additionalDocumentationFile.open(QIODevice::ReadOnly | QIODevice::Text))
throw Exception(msgCannotOpenForReading(additionalDocumentationFile));
@@ -878,8 +1223,8 @@ void QtDocGenerator::writeAdditionalDocumentation() const
targetDir = outDir.absolutePath();
} else {
if (!outDir.exists(dir) && !outDir.mkdir(dir)) {
- const QString m = QStringLiteral("Cannot create directory ")
- + dir + QStringLiteral(" under ")
+ const QString m = "Cannot create directory "_L1
+ + dir + " under "_L1
+ QDir::toNativeSeparators(outputDirectory());
throw Exception(m);
}
@@ -887,7 +1232,7 @@ void QtDocGenerator::writeAdditionalDocumentation() const
}
} else {
// Normal file entry
- QFileInfo fi(m_parameters.docDataDir + u'/' + line);
+ QFileInfo fi(m_options.parameters.docDataDir + u'/' + line);
if (fi.isFile()) {
const QString rstFileName = fi.baseName() + rstSuffix;
const QString rstFile = targetDir + u'/' + rstFileName;
@@ -905,7 +1250,7 @@ void QtDocGenerator::writeAdditionalDocumentation() const
// FIXME: This should be an exception, in principle, but it
// requires building all modules.
qCWarning(lcShibokenDoc, "%s",
- qPrintable(msgNonExistentAdditionalDocFile(m_parameters.docDataDir, line)));
+ qPrintable(msgNonExistentAdditionalDocFile(m_options.parameters.docDataDir, line)));
}
++count;
}
@@ -924,32 +1269,34 @@ void QtDocGenerator::writeAdditionalDocumentation() const
bool QtDocGenerator::doSetup()
{
- if (m_parameters.codeSnippetDirs.isEmpty()) {
- m_parameters.codeSnippetDirs =
- m_parameters.libSourceDir.split(QLatin1Char(PATH_SEP));
+ if (m_options.parameters.codeSnippetDirs.isEmpty()) {
+ m_options.parameters.codeSnippetDirs =
+ m_options.parameters.libSourceDir.split(QLatin1Char(PATH_SEP));
}
- if (m_docParser.isNull())
- m_docParser.reset(new QtDocParser);
+ if (m_docParser.isNull()) {
+ if (m_options.doxygen)
+ m_docParser.reset(new DoxygenParser);
+ else
+ m_docParser.reset(new QtDocParser);
+ }
- if (m_parameters.libSourceDir.isEmpty()
- || m_parameters.docDataDir.isEmpty()) {
+ if (m_options.parameters.libSourceDir.isEmpty()
+ || m_options.parameters.docDataDir.isEmpty()) {
qCWarning(lcShibokenDoc) << "Documentation data dir and/or Qt source dir not informed, "
"documentation will not be extracted from Qt sources.";
return false;
}
- m_docParser->setDocumentationDataDirectory(m_parameters.docDataDir);
- m_docParser->setLibrarySourceDirectory(m_parameters.libSourceDir);
- m_parameters.outputDirectory = outputDirectory();
+ m_docParser->setDocumentationDataDirectory(m_options.parameters.docDataDir);
+ m_docParser->setLibrarySourceDirectory(m_options.parameters.libSourceDir);
+ m_options.parameters.outputDirectory = outputDirectory();
return true;
}
-
-Generator::OptionDescriptions QtDocGenerator::options() const
+QList<OptionDescription> QtDocGenerator::options()
{
- auto result = Generator::options();
- result.append({
+ return {
{u"doc-parser=<parser>"_s,
u"The documentation parser used to interpret the documentation\n"
"input files (qdoc|doxygen)"_s},
@@ -963,27 +1310,52 @@ Generator::OptionDescriptions QtDocGenerator::options() const
u"Directory used to search for extra documentation sections"_s},
{u"library-source-dir=<dir>"_s,
u"Directory where library source code is located"_s},
- {additionalDocumentationOption() + u"=<file>"_s,
+ {additionalDocumentationOption + u"=<file>"_s,
u"List of additional XML files to be converted to .rst files\n"
- "(for example, tutorials)."_s}
- });
- return result;
+ "(for example, tutorials)."_s},
+ {u"inheritance-file=<file>"_s,
+ u"Generate a JSON file containing the class inheritance."_s},
+ {u"disable-inheritance-diagram"_s,
+ u"Disable the generation of the inheritance diagram."_s}
+ };
}
-bool QtDocGenerator::handleOption(const QString &key, const QString &value)
+class QtDocGeneratorOptionsParser : public OptionsParser
{
- if (Generator::handleOption(key, value))
+public:
+ explicit QtDocGeneratorOptionsParser(DocGeneratorOptions *o) : m_options(o) {}
+
+ bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value, OptionSource source) override;
+
+private:
+ DocGeneratorOptions *m_options;
+};
+
+bool QtDocGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource)
+{
+ if (key == "disable-inheritance-diagram"_L1) {
+ m_options->inheritanceDiagram = false;
return true;
+ }
+ return false;
+}
+
+bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString &value,
+ OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
if (key == u"library-source-dir") {
- m_parameters.libSourceDir = value;
+ m_options->parameters.libSourceDir = value;
return true;
}
if (key == u"documentation-data-dir") {
- m_parameters.docDataDir = value;
+ m_options->parameters.docDataDir = value;
return true;
}
if (key == u"documentation-code-snippets-dir") {
- m_parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP));
+ m_options->parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP));
return true;
}
@@ -991,28 +1363,39 @@ bool QtDocGenerator::handleOption(const QString &key, const QString &value)
const auto pos = value.indexOf(u':');
if (pos == -1)
return false;
- m_parameters.codeSnippetRewriteOld= value.left(pos);
- m_parameters.codeSnippetRewriteNew = value.mid(pos + 1);
+ m_options->parameters.codeSnippetRewriteOld= value.left(pos);
+ m_options->parameters.codeSnippetRewriteNew = value.mid(pos + 1);
return true;
}
if (key == u"documentation-extra-sections-dir") {
- m_extraSectionDir = value;
+ m_options->extraSectionDir = value;
return true;
}
if (key == u"doc-parser") {
qCDebug(lcShibokenDoc).noquote().nospace() << "doc-parser: " << value;
if (value == u"doxygen")
- m_docParser.reset(new DoxygenParser);
+ m_options->doxygen = true;
return true;
}
- if (key == additionalDocumentationOption()) {
- m_additionalDocumentationList = value;
+ if (key == additionalDocumentationOption) {
+ m_options->additionalDocumentationList = value;
return true;
}
+
+ if (key == u"inheritance-file") {
+ m_options->inheritanceFile = value;
+ return true;
+ }
+
return false;
}
+std::shared_ptr<OptionsParser> QtDocGenerator::createOptionsParser()
+{
+ return std::make_shared<QtDocGeneratorOptionsParser>(&m_options);
+}
+
bool QtDocGenerator::convertToRst(const QString &sourceFileName,
const QString &targetFileName,
const QString &context,
@@ -1028,20 +1411,65 @@ bool QtDocGenerator::convertToRst(const QString &sourceFileName,
sourceFile.close();
FileOut targetFile(targetFileName);
- QtXmlToSphinx x(this, m_parameters, doc, context);
+ QtXmlToSphinx x(this, m_options.parameters, doc, context);
targetFile.stream << x;
targetFile.done();
return true;
}
+GeneratorDocumentation
+ QtDocGenerator::generatorDocumentation(const AbstractMetaClassCPtr &cppClass)
+{
+ GeneratorDocumentation result;
+ const auto allFunctions = cppClass->functions();
+ result.allFunctions.reserve(allFunctions.size());
+ std::remove_copy_if(allFunctions.cbegin(), allFunctions.cend(),
+ std::back_inserter(result.allFunctions), shouldSkip);
+
+ std::stable_sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort);
+
+ for (const auto &func : std::as_const(result.allFunctions)) {
+ if (func->isStatic())
+ result.tocStaticFunctions.append(func);
+ else if (func->isVirtual())
+ result.tocVirtuals.append(func);
+ else if (func->isSignal())
+ result.tocSignalFunctions.append(func);
+ else if (func->isSlot())
+ result.tocSlotFunctions.append(func);
+ else
+ result.tocNormalFunctions.append(func);
+ }
+
+ // Find the property getters/setters
+ for (const auto &spec: cppClass->propertySpecs()) {
+ GeneratorDocumentation::Property property;
+ property.name = spec.name();
+ property.type = spec.type();
+ property.documentation = spec.documentation();
+ if (!spec.read().isEmpty())
+ property.getter = AbstractMetaFunction::find(result.allFunctions, spec.read());
+ if (!spec.write().isEmpty())
+ property.setter = AbstractMetaFunction::find(result.allFunctions, spec.write());
+ if (!spec.reset().isEmpty())
+ property.reset = AbstractMetaFunction::find(result.allFunctions, spec.reset());
+ if (!spec.notify().isEmpty())
+ property.notify = AbstractMetaFunction::find(result.tocSignalFunctions, spec.notify());
+ result.properties.append(property);
+ }
+ std::sort(result.properties.begin(), result.properties.end());
+
+ return result;
+}
+
// QtXmlToSphinxDocGeneratorInterface
QString QtDocGenerator::expandFunction(const QString &function) const
{
- const int firstDot = function.indexOf(u'.');
- const AbstractMetaClass *metaClass = nullptr;
+ const auto firstDot = function.indexOf(u'.');
+ AbstractMetaClassCPtr metaClass;
if (firstDot != -1) {
const auto className = QStringView{function}.left(firstDot);
- for (auto cls : api().classes()) {
+ for (const auto &cls : api().classes()) {
if (cls->name() == className) {
metaClass = cls;
break;
@@ -1076,8 +1504,8 @@ QString QtDocGenerator::resolveContextForMethod(const QString &context,
{
const auto currentClass = QStringView{context}.split(u'.').constLast();
- const AbstractMetaClass *metaClass = nullptr;
- for (auto cls : api().classes()) {
+ AbstractMetaClassCPtr metaClass;
+ for (const auto &cls : api().classes()) {
if (cls->name() == currentClass) {
metaClass = cls;
break;
@@ -1092,8 +1520,8 @@ QString QtDocGenerator::resolveContextForMethod(const QString &context,
funcList.append(func);
}
- const AbstractMetaClass *implementingClass = nullptr;
- for (const auto &func : qAsConst(funcList)) {
+ AbstractMetaClassCPtr implementingClass;
+ for (const auto &func : std::as_const(funcList)) {
implementingClass = func->implementingClass();
if (implementingClass->name() == currentClass)
break;
@@ -1123,7 +1551,7 @@ QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) con
{
if (link.type != QtXmlToSphinxLink::Reference || !isRelativeHtmlFile(link.linkRef))
return link;
- static const QString prefix = QStringLiteral("https://doc.qt.io/qt-")
+ static const QString prefix = "https://doc.qt.io/qt-"_L1
+ QString::number(QT_VERSION_MAJOR) + u'/';
QtXmlToSphinxLink resolved = link;
resolved.type = QtXmlToSphinxLink::External;
@@ -1134,6 +1562,5 @@ QtXmlToSphinxLink QtDocGenerator::resolveLink(const QtXmlToSphinxLink &link) con
if (anchor != -1)
resolved.linkText.truncate(anchor);
}
- qDebug() << __FUNCTION__ << link << "->" << resolved;
return resolved;
}
diff --git a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h
index b0f728fc4..3b1c82e74 100644
--- a/sources/shiboken6/generator/qtdoc/qtdocgenerator.h
+++ b/sources/shiboken6/generator/qtdoc/qtdocgenerator.h
@@ -9,11 +9,15 @@
#include "generator.h"
#include "documentation.h"
+#include <optionsparser.h>
#include "typesystem_enums.h"
-#include "typesystem_typedefs.h"
+#include "modifications_typedefs.h"
#include "qtxmltosphinxinterface.h"
class DocParser;
+struct DocGeneratorOptions;
+struct GeneratorDocumentation;
+struct DocPackage;
/**
* The DocGenerator generates documentation from library being binded.
@@ -21,6 +25,8 @@ class DocParser;
class QtDocGenerator : public Generator, public QtXmlToSphinxDocGeneratorInterface
{
public:
+ Q_DISABLE_COPY_MOVE(QtDocGenerator)
+
QtDocGenerator();
~QtDocGenerator();
@@ -31,8 +37,8 @@ public:
return "QtDocGenerator";
}
- OptionDescriptions options() const override;
- bool handleOption(const QString &key, const QString &value) override;
+ static QList<OptionDescription> options();
+ static std::shared_ptr<OptionsParser> createOptionsParser();
// QtXmlToSphinxDocGeneratorInterface
QString expandFunction(const QString &function) const override;
@@ -43,64 +49,81 @@ public:
const QLoggingCategory &loggingCategory() const override;
QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &) const override;
+ static QString getFuncName(const AbstractMetaFunctionCPtr &cppFunc);
+ static QString formatArgs(const AbstractMetaFunctionCPtr &func);
+
protected:
- bool shouldGenerate(const TypeEntry *) const override;
+ bool shouldGenerate(const TypeEntryCPtr &) const override;
static QString fileNameSuffix();
QString fileNameForContext(const GeneratorContext &context) const override;
void generateClass(TextStream &ts, const GeneratorContext &classContext) override;
bool finishGeneration() override;
private:
- void writeEnums(TextStream& s, const AbstractMetaClass* cppClass) const;
-
- void writeFields(TextStream &s, const AbstractMetaClass *cppClass) const;
- static QString functionSignature(const AbstractMetaClass* cppClass,
- const AbstractMetaFunctionCPtr &func);
- void writeFunction(TextStream& s, const AbstractMetaClass* cppClass,
- const AbstractMetaFunctionCPtr &func, bool indexed = true);
- void writeFunctionParametersType(TextStream &s, const AbstractMetaClass *cppClass,
+ void writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
+ const QString &scope) const;
+
+ void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const;
+ void writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
+ const AbstractMetaClassCPtr &cppClass, const QString &scope);
+ void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {},
+ const QString &scope = {}, bool indexed = true);
+ void writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const DocModificationList &modifications,
+ const QString &scope) const;
+ void writeFunctionParametersType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
const AbstractMetaFunctionCPtr &func) const;
- static void writeFunctionList(TextStream& s, const AbstractMetaClass* cppClass);
- static void writeFunctionBlock(TextStream& s, const QString& title,
- QStringList& functions);
- void writeParameterType(TextStream &s, const AbstractMetaClass *cppClass,
+ static void writeFunctionToc(TextStream &s, const QString &title,
+ const AbstractMetaFunctionCList &functions);
+ static void writePropertyToc(TextStream &s,
+ const GeneratorDocumentation &doc);
+ void writeProperties(TextStream &s,
+ const GeneratorDocumentation &doc,
+ const AbstractMetaClassCPtr &cppClass) const;
+ void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
const AbstractMetaArgument &arg) const;
-
- void writeConstructors(TextStream &s, const AbstractMetaClass *cppClass) const;
-
void writeFormattedText(TextStream &s, const QString &doc,
Documentation::Format format,
- const AbstractMetaClass *metaClass = nullptr) const;
+ const QString &scope = {}) const;
void writeFormattedBriefText(TextStream &s, const Documentation &doc,
- const AbstractMetaClass *metaclass = nullptr) const;
+ const QString &scope = {}) const;
void writeFormattedDetailedText(TextStream &s, const Documentation &doc,
- const AbstractMetaClass *metaclass = nullptr) const;
-
- bool writeInjectDocumentation(TextStream& s, TypeSystem::DocModificationMode mode,
- const AbstractMetaClass* cppClass,
- const AbstractMetaFunctionCPtr &func);
+ const QString &scope = {}) const;
+
+ bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
+ const AbstractMetaClassCPtr &cppClass) const;
+ bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
+ const DocModificationList &modifications,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &scope = {}) const;
+ bool writeDocModifications(TextStream &s, const DocModificationList &mods,
+ TypeSystem::DocModificationMode mode,
+ const QString &scope = {}) const;
static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips,
TypeSystem::CodeSnipPosition position, TypeSystem::Language language);
void writeModuleDocumentation();
+ void writeGlobals(const QString &package, const QString &fileName,
+ const DocPackage &docPackage);
void writeAdditionalDocumentation() const;
+ bool writeInheritanceFile();
- static QString parseArgDocStyle(const AbstractMetaClass *cppClass,
- const AbstractMetaFunctionCPtr &func);
- QString translateToPythonType(const AbstractMetaType &type, const AbstractMetaClass *cppClass) const;
- static QString getFuncName(const AbstractMetaFunctionCPtr& cppFunc);
+ QString translateToPythonType(const AbstractMetaType &type,
+ const AbstractMetaClassCPtr &cppClass,
+ bool createRef = true) const;
bool convertToRst(const QString &sourceFileName,
const QString &targetFileName,
const QString &context = QString(),
QString *errorMessage = nullptr) const;
- QString m_extraSectionDir;
+ static GeneratorDocumentation generatorDocumentation(const AbstractMetaClassCPtr &cppClass);
+
QStringList m_functionList;
- QMap<QString, QStringList> m_packages;
+ QMap<QString, DocPackage> m_packages;
QScopedPointer<DocParser> m_docParser;
- QtXmlToSphinxParameters m_parameters;
- QString m_additionalDocumentationList;
+ static DocGeneratorOptions m_options;
};
#endif // DOCGENERATOR_H
diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
index c29d2596c..55c1d2090 100644
--- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
+++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
@@ -12,16 +12,13 @@
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
+#include <QtCore/QHash>
#include <QtCore/QLoggingCategory>
#include <QtCore/QRegularExpression>
#include <QtCore/QXmlStreamReader>
using namespace Qt::StringLiterals;
-static inline QString nameAttribute() { return QStringLiteral("name"); }
-static inline QString titleAttribute() { return QStringLiteral("title"); }
-static inline QString fullTitleAttribute() { return QStringLiteral("fulltitle"); }
-
QString msgTagWarning(const QXmlStreamReader &reader, const QString &context,
const QString &tag, const QString &message)
{
@@ -62,6 +59,20 @@ static bool isHttpLink(const QString &ref)
return ref.startsWith(u"http://") || ref.startsWith(u"https://");
}
+static QString trimRight(QString s)
+{
+ while (!s.isEmpty() && s.crbegin()->isSpace())
+ s.chop(1);
+ return s;
+}
+
+static QString trimLeadingNewlines(QString s)
+{
+ while (!s.isEmpty() && s.at(0) == u'\n')
+ s.remove(0, 1);
+ return s;
+}
+
QDebug operator<<(QDebug d, const QtXmlToSphinxLink &l)
{
static const QHash<QtXmlToSphinxLink::Type, const char *> typeName = {
@@ -406,32 +417,49 @@ void QtXmlToSphinx::callHandler(WebXmlTag t, QXmlStreamReader &r)
void QtXmlToSphinx::formatCurrentTable()
{
- if (m_currentTable.isEmpty())
+ Q_ASSERT(!m_tables.isEmpty());
+ auto &table = m_tables.back();
+ if (table.isEmpty())
return;
- m_currentTable.normalize();
+ table.normalize();
m_output << '\n';
- m_currentTable.format(m_output);
+ table.format(m_output);
}
void QtXmlToSphinx::pushOutputBuffer()
{
- m_buffers.append(StringSharedPtr(new QString{}));
- m_output.setString(m_buffers.top().data());
+ m_buffers.append(std::make_shared<QString>());
+ m_output.setString(m_buffers.top().get());
}
QString QtXmlToSphinx::popOutputBuffer()
{
Q_ASSERT(!m_buffers.isEmpty());
- QString result(*m_buffers.top().data());
+ QString result(*m_buffers.top());
m_buffers.pop();
- m_output.setString(m_buffers.isEmpty() ? nullptr : m_buffers.top().data());
+ m_output.setString(m_buffers.isEmpty() ? nullptr : m_buffers.top().get());
return result;
}
+constexpr auto autoTranslatedPlaceholder = "AUTO_GENERATED\n"_L1;
+constexpr auto autoTranslatedNote =
+R"(.. warning::
+ This section contains snippets that were automatically
+ translated from C++ to Python and may contain errors.
+
+)"_L1;
+
+void QtXmlToSphinx::setAutoTranslatedNote(QString *str) const
+{
+ if (m_containsAutoTranslations)
+ str->replace(autoTranslatedPlaceholder, autoTranslatedNote);
+ else
+ str->remove(autoTranslatedPlaceholder);
+}
+
QString QtXmlToSphinx::transform(const QString& doc)
{
Q_ASSERT(m_buffers.isEmpty());
- Indentation indentation(m_output);
if (doc.trimmed().isEmpty())
return doc;
@@ -439,6 +467,9 @@ QString QtXmlToSphinx::transform(const QString& doc)
QXmlStreamReader reader(doc);
+ m_output << autoTranslatedPlaceholder;
+ Indentation indentation(m_output);
+
while (!reader.atEnd()) {
QXmlStreamReader::TokenType token = reader.readNext();
if (reader.hasError()) {
@@ -470,7 +501,7 @@ QString QtXmlToSphinx::transform(const QString& doc)
if (!m_inlineImages.isEmpty()) {
// Write out inline image definitions stored in handleInlineImageTag().
m_output << '\n' << disableIndent;
- for (const InlineImage &img : qAsConst(m_inlineImages))
+ for (const InlineImage &img : std::as_const(m_inlineImages))
m_output << ".. |" << img.tag << "| image:: " << img.href << '\n';
m_output << '\n' << enableIndent;
m_inlineImages.clear();
@@ -479,6 +510,7 @@ QString QtXmlToSphinx::transform(const QString& doc)
m_output.flush();
QString retval = popOutputBuffer();
Q_ASSERT(m_buffers.isEmpty());
+ setAutoTranslatedNote(&retval);
return retval;
}
@@ -527,7 +559,7 @@ static QString pySnippetName(const QString &path, SnippetType type)
QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &path,
const QString &identifier,
const QString &fallbackPath,
- QString *errorMessage) const
+ QString *errorMessage)
{
// For anything else but C++ header/sources (no conversion to Python),
// use existing fallback paths first.
@@ -549,6 +581,7 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa
rewrittenPath.replace(m_parameters.codeSnippetRewriteOld,
m_parameters.codeSnippetRewriteNew);
const QString code = readFromLocation(rewrittenPath, identifier, errorMessage);
+ m_containsAutoTranslations = true;
return {code, code.isNull() ? Snippet::Error : Snippet::Converted};
}
}
@@ -560,7 +593,7 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa
}
}
- resolvedPath =resolveFile(locations, path);
+ resolvedPath = resolveFile(locations, path);
if (!resolvedPath.isEmpty()) {
const QString code = readFromLocation(resolvedPath, identifier, errorMessage);
return {code, code.isNull() ? Snippet::Error : Snippet::Resolved};
@@ -576,6 +609,88 @@ QtXmlToSphinx::Snippet QtXmlToSphinx::readSnippetFromLocations(const QString &pa
return {{}, Snippet::Error};
}
+// Helpers for extracting qdoc snippets "#/// [id]"
+static QString fileNameOfDevice(const QIODevice *inputFile)
+{
+ const auto *file = qobject_cast<const QFile *>(inputFile);
+ return file ? QDir::toNativeSeparators(file->fileName()) : u"<stdin>"_s;
+}
+
+static QString msgSnippetNotFound(const QIODevice &inputFile,
+ const QString &identifier)
+{
+ return u"Code snippet file found ("_s + fileNameOfDevice(&inputFile)
+ + u"), but snippet ["_s + identifier + u"] not found."_s;
+}
+
+static QString msgEmptySnippet(const QIODevice &inputFile, int lineNo,
+ const QString &identifier)
+{
+ return u"Empty code snippet ["_s + identifier + u"] at "_s
+ + fileNameOfDevice(&inputFile) + u':' + QString::number(lineNo);
+}
+
+// Pattern to match qdoc snippet IDs with "#/// [id]" comments and helper to find ID
+static const QRegularExpression &snippetIdPattern()
+{
+ static const QRegularExpression result(uR"RX((//|#) *! *\[([^]]+)\])RX"_s);
+ Q_ASSERT(result.isValid());
+ return result;
+}
+
+static bool matchesSnippetId(QRegularExpressionMatchIterator it,
+ const QString &identifier)
+{
+ while (it.hasNext()) {
+ if (it.next().captured(2) == identifier)
+ return true;
+ }
+ return false;
+}
+
+QString QtXmlToSphinx::readSnippet(QIODevice &inputFile, const QString &identifier,
+ QString *errorMessage)
+{
+ const QByteArray identifierBA = identifier.toUtf8();
+ // Lambda that matches the snippet id
+ const auto snippetIdPred = [&identifierBA, &identifier](const QByteArray &lineBA)
+ {
+ const bool isComment = lineBA.contains('/') || lineBA.contains('#');
+ if (!isComment || !lineBA.contains(identifierBA))
+ return false;
+ const QString line = QString::fromUtf8(lineBA);
+ return matchesSnippetId(snippetIdPattern().globalMatch(line), identifier);
+ };
+
+ // Find beginning, skip over
+ int lineNo = 1;
+ for (; !inputFile.atEnd() && !snippetIdPred(inputFile.readLine());
+ ++lineNo) {
+ }
+
+ if (inputFile.atEnd()) {
+ *errorMessage = msgSnippetNotFound(inputFile, identifier);
+ return {};
+ }
+
+ QString code;
+ for (; !inputFile.atEnd(); ++lineNo) {
+ const QString line = QString::fromUtf8(inputFile.readLine());
+ auto it = snippetIdPattern().globalMatch(line);
+ if (it.hasNext()) { // Skip snippet id lines
+ if (matchesSnippetId(it, identifier))
+ break;
+ } else {
+ code += line;
+ }
+ }
+
+ if (code.isEmpty())
+ *errorMessage = msgEmptySnippet(inputFile, lineNo, identifier);
+
+ return code;
+}
+
QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &identifier,
QString *errorMessage)
{
@@ -585,7 +700,7 @@ QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &
QTextStream(errorMessage) << "Could not read code snippet file: "
<< QDir::toNativeSeparators(inputFile.fileName())
<< ": " << inputFile.errorString();
- return QString(); // null
+ return {}; // null
}
QString code = u""_s; // non-null
@@ -595,34 +710,8 @@ QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &
return CodeSnipHelpers::fixSpaces(code);
}
- const QRegularExpression searchString(u"//!\\s*\\["_s
- + identifier + u"\\]"_s);
- Q_ASSERT(searchString.isValid());
- static const QRegularExpression codeSnippetCode(u"//!\\s*\\[[\\w\\d\\s]+\\]"_s);
- Q_ASSERT(codeSnippetCode.isValid());
-
- bool getCode = false;
-
- while (!inputFile.atEnd()) {
- QString line = QString::fromUtf8(inputFile.readLine());
- if (getCode && !line.contains(searchString)) {
- line.remove(codeSnippetCode);
- code += line;
- } else if (line.contains(searchString)) {
- if (getCode)
- break;
- getCode = true;
- }
- }
-
- if (!getCode) {
- QTextStream(errorMessage) << "Code snippet file found ("
- << QDir::toNativeSeparators(location) << "), but snippet ["
- << identifier << "] not found.";
- return QString(); // null
- }
-
- return CodeSnipHelpers::fixSpaces(code);
+ code = readSnippet(inputFile, identifier, errorMessage);
+ return code.isEmpty() ? QString{} : CodeSnipHelpers::fixSpaces(code); // maintain isNull()
}
void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader)
@@ -685,9 +774,9 @@ void QtXmlToSphinx::handleParaTagEnd()
{
QString result = popOutputBuffer().simplified();
if (result.startsWith(u"**Warning:**"))
- result.replace(0, 12, QStringLiteral(".. warning:: "));
+ result.replace(0, 12, ".. warning:: "_L1);
else if (result.startsWith(u"**Note:**"))
- result.replace(0, 9, QStringLiteral(".. note:: "));
+ result.replace(0, 9, ".. note:: "_L1);
m_output << result << "\n\n";
}
@@ -756,23 +845,23 @@ void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader)
}
}
-static inline QString functionLinkType() { return QStringLiteral("function"); }
-static inline QString classLinkType() { return QStringLiteral("class"); }
+constexpr auto functionLinkType = "function"_L1;
+constexpr auto classLinkType = "class"_L1;
static inline QString fixLinkType(QStringView type)
{
// TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties
// are recognized as such or not in the binding
if (type == u"property")
- return functionLinkType();
+ return functionLinkType;
if (type == u"typedef")
- return classLinkType();
+ return classLinkType;
return type.toString();
}
static inline QString linkSourceAttribute(const QString &type)
{
- if (type == functionLinkType() || type == classLinkType())
+ if (type == functionLinkType || type == classLinkType)
return u"raw"_s;
return type == u"enum" || type == u"page"
? type : u"href"_s;
@@ -798,7 +887,7 @@ void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader)
const QString text = textR.toString();
if (m_seeAlsoContext.isNull()) {
const QString type = text.endsWith(u"()")
- ? functionLinkType() : classLinkType();
+ ? functionLinkType : classLinkType;
m_seeAlsoContext.reset(handleLinkStart(type, text));
}
handleLinkText(m_seeAlsoContext.data(), text);
@@ -817,7 +906,7 @@ void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader)
}
}
-static inline QString fallbackPathAttribute() { return QStringLiteral("path"); }
+constexpr auto fallbackPathAttribute = "path"_L1;
template <class Indent> // const char*/class Indentor
void formatSnippet(TextStream &str, Indent indent, const QString &snippet)
@@ -852,14 +941,15 @@ void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader)
|| m_lastTagName == u"dots" || m_lastTagName == u"codeline";
if (consecutiveSnippet) {
m_output.flush();
- m_output.string()->chop(2);
+ m_output.string()->chop(1); // Strip newline from previous snippet
}
QString location = reader.attributes().value(u"location"_s).toString();
QString identifier = reader.attributes().value(u"identifier"_s).toString();
QString fallbackPath;
- if (reader.attributes().hasAttribute(fallbackPathAttribute()))
- fallbackPath = reader.attributes().value(fallbackPathAttribute()).toString();
+ if (reader.attributes().hasAttribute(fallbackPathAttribute))
+ fallbackPath = reader.attributes().value(fallbackPathAttribute).toString();
QString errorMessage;
+
const Snippet snippet = readSnippetFromLocations(location, identifier,
fallbackPath, &errorMessage);
if (!errorMessage.isEmpty())
@@ -882,6 +972,7 @@ void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader)
m_output << '\n';
}
}
+
void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader)
{
QXmlStreamReader::TokenType token = reader.tokenType();
@@ -912,11 +1003,11 @@ void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader)
if (token == QXmlStreamReader::StartElement) {
if (parentTag() == WebXmlTag::para)
handleParaTagEnd(); // End <para> to prevent the table from being rst-escaped
- m_currentTable.clear();
+ m_tables.push({});
} else if (token == QXmlStreamReader::EndElement) {
// write the table on m_output
formatCurrentTable();
- m_currentTable.clear();
+ m_tables.pop();
if (parentTag() == WebXmlTag::para)
handleParaTagStart();
}
@@ -932,7 +1023,7 @@ void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader)
} else if (token == QXmlStreamReader::EndElement) {
TableCell cell;
cell.data = popOutputBuffer().trimmed();
- m_currentTable.appendRow(TableRow(1, cell));
+ m_tables.back().appendRow(TableRow(1, cell));
}
}
@@ -941,18 +1032,20 @@ void QtXmlToSphinx::handleItemTag(QXmlStreamReader& reader)
{
QXmlStreamReader::TokenType token = reader.tokenType();
if (token == QXmlStreamReader::StartElement) {
- if (m_currentTable.isEmpty())
- m_currentTable.appendRow({});
- TableRow& row = m_currentTable.last();
+ auto &table = m_tables.back();
+ if (table.isEmpty())
+ table.appendRow({});
+ TableRow& row = table.last();
TableCell cell;
cell.colSpan = reader.attributes().value(u"colspan"_s).toShort();
cell.rowSpan = reader.attributes().value(u"rowspan"_s).toShort();
row << cell;
pushOutputBuffer();
} else if (token == QXmlStreamReader::EndElement) {
- QString data = popOutputBuffer().trimmed();
- if (!m_currentTable.isEmpty()) {
- TableRow& row = m_currentTable.last();
+ QString data = trimLeadingNewlines(trimRight(popOutputBuffer()));
+ auto &table = m_tables.back();
+ if (!table.isEmpty()) {
+ TableRow& row = table.last();
if (!row.isEmpty())
row.last().data = data;
}
@@ -965,15 +1058,16 @@ void QtXmlToSphinx::handleHeaderTag(QXmlStreamReader &reader)
// C++ header with "name"/"href" attributes.
if (reader.tokenType() == QXmlStreamReader::StartElement
&& !reader.attributes().hasAttribute(u"name"_s)) {
- m_currentTable.setHeaderEnabled(true);
- m_currentTable.appendRow({});
+ auto &table = m_tables.back();
+ table.setHeaderEnabled(true);
+ table.appendRow({});
}
}
void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader)
{
if (reader.tokenType() == QXmlStreamReader::StartElement)
- m_currentTable.appendRow({});
+ m_tables.back().appendRow({});
}
enum ListType { BulletList, OrderedList, EnumeratedList };
@@ -989,27 +1083,29 @@ static inline ListType webXmlListType(QStringView t)
void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader)
{
- // BUG We do not support a list inside a table cell
static ListType listType = BulletList;
QXmlStreamReader::TokenType token = reader.tokenType();
if (token == QXmlStreamReader::StartElement) {
+ m_tables.push({});
+ auto &table = m_tables.back();
listType = webXmlListType(reader.attributes().value(u"type"_s));
if (listType == EnumeratedList) {
- m_currentTable.appendRow(TableRow{TableCell(u"Constant"_s),
- TableCell(u"Description"_s)});
- m_currentTable.setHeaderEnabled(true);
+ table.appendRow(TableRow{TableCell(u"Constant"_s),
+ TableCell(u"Description"_s)});
+ table.setHeaderEnabled(true);
}
m_output.indent();
} else if (token == QXmlStreamReader::EndElement) {
m_output.outdent();
- if (!m_currentTable.isEmpty()) {
+ const auto &table = m_tables.back();
+ if (!table.isEmpty()) {
switch (listType) {
case BulletList:
case OrderedList: {
m_output << '\n';
const char *separator = listType == BulletList ? "* " : "#. ";
const char *indentLine = listType == BulletList ? " " : " ";
- for (const TableCell &cell : m_currentTable.constFirst()) {
+ for (const TableCell &cell : table.constFirst()) {
const auto itemLines = QStringView{cell.data}.split(u'\n');
m_output << separator << itemLines.constFirst() << '\n';
for (qsizetype i = 1, max = itemLines.size(); i < max; ++i)
@@ -1023,7 +1119,7 @@ void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader)
break;
}
}
- m_currentTable.clear();
+ m_tables.pop();
}
}
@@ -1065,7 +1161,7 @@ QtXmlToSphinxLink *QtXmlToSphinx::handleLinkStart(const QString &type, QString r
if (type == u"external" || isHttpLink(ref)) {
result->type = QtXmlToSphinxLink::External;
- } else if (type == functionLinkType() && !m_context.isEmpty()) {
+ } else if (type == functionLinkType && !m_context.isEmpty()) {
result->type = QtXmlToSphinxLink::Method;
const auto rawlinklist = QStringView{result->linkRef}.split(u'.');
if (rawlinklist.size() == 1 || rawlinklist.constFirst() == m_context) {
@@ -1076,9 +1172,9 @@ QtXmlToSphinxLink *QtXmlToSphinx::handleLinkStart(const QString &type, QString r
} else {
result->linkRef = m_generator->expandFunction(result->linkRef);
}
- } else if (type == functionLinkType() && m_context.isEmpty()) {
+ } else if (type == functionLinkType && m_context.isEmpty()) {
result->type = QtXmlToSphinxLink::Function;
- } else if (type == classLinkType()) {
+ } else if (type == classLinkType) {
result->type = QtXmlToSphinxLink::Class;
result->linkRef = m_generator->expandClass(m_context, result->linkRef);
} else if (type == u"enum") {
@@ -1118,10 +1214,10 @@ static QString fixLinkText(const QtXmlToSphinxLink *linkContext,
else
QtXmlToSphinx::stripPythonQualifiers(&linktext);
if (linkContext->linkRef == linktext)
- return QString();
+ return {};
if ((linkContext->type & QtXmlToSphinxLink::FunctionMask) != 0
&& (linkContext->linkRef + u"()"_s) == linktext) {
- return QString();
+ return {};
}
return linktext;
}
@@ -1149,7 +1245,7 @@ static bool copyImage(const QString &href, const QString &docDataDir,
const QLoggingCategory &lc, QString *errorMessage)
{
const QChar slash = u'/';
- const int lastSlash = href.lastIndexOf(slash);
+ const auto lastSlash = href.lastIndexOf(slash);
const QString imagePath = lastSlash != -1 ? href.left(lastSlash) : QString();
const QString imageFileName = lastSlash != -1 ? href.right(href.size() - lastSlash - 1) : href;
QFileInfo imageSource(docDataDir + slash + href);
@@ -1162,7 +1258,7 @@ static bool copyImage(const QString &href, const QString &docDataDir,
// FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or
// nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint".
QString relativeTargetDir = context;
- const int lastDot = relativeTargetDir.lastIndexOf(u'.');
+ const auto lastDot = relativeTargetDir.lastIndexOf(u'.');
if (lastDot != -1)
relativeTargetDir.truncate(lastDot);
relativeTargetDir.replace(u'.', slash);
@@ -1229,7 +1325,7 @@ void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
// enclosed by '|' and define it further down. Determine tag from the base
//file name with number.
QString tag = href;
- int pos = tag.lastIndexOf(u'/');
+ auto pos = tag.lastIndexOf(u'/');
if (pos != -1)
tag.remove(0, pos + 1);
pos = tag.indexOf(u'.');
@@ -1297,11 +1393,11 @@ void QtXmlToSphinx::handlePageTag(QXmlStreamReader &reader)
m_output << disableIndent;
- const auto title = reader.attributes().value(titleAttribute());
+ const auto title = reader.attributes().value("title");
if (!title.isEmpty())
m_output << rstLabel(title.toString());
- const auto fullTitle = reader.attributes().value(fullTitleAttribute());
+ const auto fullTitle = reader.attributes().value("fulltitle");
const int size = fullTitle.isEmpty()
? writeEscapedRstText(m_output, title)
: writeEscapedRstText(m_output, fullTitle);
@@ -1314,7 +1410,7 @@ void QtXmlToSphinx::handleTargetTag(QXmlStreamReader &reader)
{
if (reader.tokenType() != QXmlStreamReader::StartElement)
return;
- const auto name = reader.attributes().value(nameAttribute());
+ const auto name = reader.attributes().value("name");
if (!name.isEmpty())
m_output << rstLabel(name.toString());
}
@@ -1405,7 +1501,7 @@ void QtXmlToSphinx::Table::normalize()
//QDoc3 generates tables with wrong number of columns. We have to
//check and if necessary, merge the last columns.
qsizetype maxCols = -1;
- for (const auto &row : qAsConst(m_rows)) {
+ for (const auto &row : std::as_const(m_rows)) {
if (row.size() > maxCols)
maxCols = row.size();
}
@@ -1489,13 +1585,11 @@ void QtXmlToSphinx::Table::format(TextStream& s) const
// print line
s << '+';
for (qsizetype col = 0; col < headerColumnCount; ++col) {
- char c;
+ char c = '-';
if (col >= row.size() || row[col].rowSpan == -1)
c = ' ';
else if (i == 1 && hasHeader())
c = '=';
- else
- c = '-';
s << Pad(c, colWidths.at(col)) << '+';
}
s << '\n';
diff --git a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h
index bb58c0878..398c5bc97 100644
--- a/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h
+++ b/sources/shiboken6/generator/qtdoc/qtxmltosphinx.h
@@ -6,14 +6,11 @@
#include <textstream.h>
-#include "qtcompat.h"
-
-#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QScopedPointer>
-#include <QtCore/QSharedPointer>
#include <QtCore/QStack>
-#include <QtCore/QTextStream>
+
+#include <memory>
QT_BEGIN_NAMESPACE
class QDebug;
@@ -73,14 +70,9 @@ public:
return m_normalized;
}
- void clear() {
- m_normalized = false;
- m_rows.clear();
- }
-
void appendRow(const TableRow &row) { m_rows.append(row); }
- const TableRow &constFirst() { return m_rows.constFirst(); }
+ const TableRow &constFirst() const { return m_rows.constFirst(); }
TableRow &first() { return m_rows.first(); }
TableRow &last() { return m_rows.last(); }
@@ -109,8 +101,12 @@ public:
static void stripPythonQualifiers(QString *s);
+ // For testing
+ static QString readSnippet(QIODevice &inputFile, const QString &identifier,
+ QString *errorMessage);
+
private:
- using StringSharedPtr = QSharedPointer<QString>;
+ using StringSharedPtr = std::shared_ptr<QString>;
QString transform(const QString& doc);
@@ -164,7 +160,7 @@ private:
QStack<StringSharedPtr> m_buffers; // Maintain address stability since it used in TextStream
- Table m_currentTable;
+ QStack<Table> m_tables; // Stack of tables, used for <table><list> with nested <item>
QScopedPointer<QtXmlToSphinxLink> m_linkContext; // for <link>
QScopedPointer<QtXmlToSphinxLink> m_seeAlsoContext; // for <see-also>foo()</see-also>
QString m_context;
@@ -177,6 +173,8 @@ private:
QString m_opened_anchor;
QList<InlineImage> m_inlineImages;
+ bool m_containsAutoTranslations = false;
+
struct Snippet
{
enum Result {
@@ -190,10 +188,12 @@ private:
Result result;
};
+ void setAutoTranslatedNote(QString *str) const;
+
Snippet readSnippetFromLocations(const QString &path,
const QString &identifier,
const QString &fallbackPath,
- QString *errorMessage) const;
+ QString *errorMessage);
static QString readFromLocation(const QString &location, const QString &identifier,
QString *errorMessage);
void pushOutputBuffer();
diff --git a/sources/shiboken6/generator/qtdoc/rstformat.h b/sources/shiboken6/generator/qtdoc/rstformat.h
index 6e97c5fcd..8af7671fb 100644
--- a/sources/shiboken6/generator/qtdoc/rstformat.h
+++ b/sources/shiboken6/generator/qtdoc/rstformat.h
@@ -30,28 +30,6 @@ inline QByteArray rstDeprecationNote(const char *what)
+ what + QByteArrayLiteral(" is deprecated.\n\n");
}
-class Pad
-{
-public:
- explicit Pad(char c, int count) : m_char(c), m_count(count) {}
-
- void write(TextStream &str) const
- {
- for (int i = 0; i < m_count; ++i)
- str << m_char;
- }
-
-private:
- const char m_char;
- const int m_count;
-};
-
-inline TextStream &operator<<(TextStream &str, const Pad &pad)
-{
- pad.write(str);
- return str;
-}
-
template <class String>
inline int writeEscapedRstText(TextStream &str, const String &s)
{