aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp')
-rw-r--r--sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp2408
1 files changed, 0 insertions, 2408 deletions
diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp
deleted file mode 100644
index 014cc948e..000000000
--- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp
+++ /dev/null
@@ -1,2408 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt for Python.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qtdocgenerator.h"
-#include <abstractmetalang.h>
-#include <messages.h>
-#include <reporthandler.h>
-#include <typesystem.h>
-#include <qtdocparser.h>
-#include <doxygenparser.h>
-#include <typedatabase.h>
-#include <algorithm>
-#include <QtCore/QStack>
-#include <QtCore/QRegularExpression>
-#include <QtCore/QTextStream>
-#include <QtCore/QXmlStreamReader>
-#include <QtCore/QFile>
-#include <QtCore/QDir>
-#include <fileout.h>
-#include <limits>
-
-static Indentor INDENT;
-
-static inline QString additionalDocumentationOption() { return QStringLiteral("additional-documentation"); }
-
-static inline QString nameAttribute() { return QStringLiteral("name"); }
-static inline QString titleAttribute() { return QStringLiteral("title"); }
-static inline QString fullTitleAttribute() { return QStringLiteral("fulltitle"); }
-static inline QString briefAttribute() { return QStringLiteral("brief"); }
-static inline QString briefStartElement() { return QStringLiteral("<brief>"); }
-static inline QString briefEndElement() { return QStringLiteral("</brief>"); }
-
-static inline QString none() { return QStringLiteral("None"); }
-
-static void stripPythonQualifiers(QString *s)
-{
- const int lastSep = s->lastIndexOf(QLatin1Char('.'));
- if (lastSep != -1)
- s->remove(0, lastSep + 1);
-}
-
-static bool shouldSkip(const AbstractMetaFunction* func)
-{
- // Constructors go to separate section
- if (DocParser::skipForQuery(func) || func->isConstructor())
- return true;
-
- // Search a const clone (QImage::bits() vs QImage::bits() const)
- if (func->isConstant())
- return false;
-
- const AbstractMetaArgumentList funcArgs = func->arguments();
- const AbstractMetaFunctionList &ownerFunctions = func->ownerClass()->functions();
- for (AbstractMetaFunction *f : ownerFunctions) {
- if (f != func
- && f->isConstant()
- && f->name() == func->name()
- && f->arguments().count() == funcArgs.count()) {
- // Compare each argument
- bool cloneFound = true;
-
- const AbstractMetaArgumentList fargs = f->arguments();
- for (int i = 0, max = funcArgs.count(); i < max; ++i) {
- if (funcArgs.at(i)->type()->typeEntry() != fargs.at(i)->type()->typeEntry()) {
- cloneFound = false;
- break;
- }
- }
- if (cloneFound)
- return true;
- }
- }
- return false;
-}
-
-static bool functionSort(const AbstractMetaFunction* func1, const AbstractMetaFunction* func2)
-{
- return func1->name() < func2->name();
-}
-
-class Pad
-{
-public:
- explicit Pad(char c, int count) : m_char(c), m_count(count) {}
-
- void write(QTextStream &str) const
- {
- for (int i = 0; i < m_count; ++i)
- str << m_char;
- }
-
-private:
- const char m_char;
- const int m_count;
-};
-
-inline QTextStream &operator<<(QTextStream &str, const Pad &pad)
-{
- pad.write(str);
- return str;
-}
-
-template <class String>
-static int writeEscapedRstText(QTextStream &str, const String &s)
-{
- int escaped = 0;
- for (const QChar &c : s) {
- switch (c.unicode()) {
- case '*':
- case '`':
- case '_':
- case '\\':
- str << '\\';
- ++escaped;
- break;
- }
- str << c;
- }
- return s.size() + escaped;
-}
-
-class escape
-{
-public:
- explicit escape(const QStringRef &s) : m_string(s) {}
-
- void write(QTextStream &str) const { writeEscapedRstText(str, m_string); }
-
-private:
- const QStringRef m_string;
-};
-
-inline QTextStream &operator<<(QTextStream &str, const escape &e)
-{
- e.write(str);
- return str;
-}
-
-// Return last character of a QString-buffered stream.
-static QChar lastChar(const QTextStream &str)
-{
- const QString *string = str.string();
- Q_ASSERT(string);
- return string->isEmpty() ? QChar() : *(string->crbegin());
-}
-
-static QTextStream &ensureEndl(QTextStream &s)
-{
- if (lastChar(s) != QLatin1Char('\n'))
- s << Qt::endl;
- return s;
-}
-
-static inline QVersionNumber versionOf(const TypeEntry *te)
-{
- if (te) {
- const auto version = te->version();
- if (!version.isNull() && version > QVersionNumber(0, 0))
- return version;
- }
- return QVersionNumber();
-}
-
-struct rstVersionAdded
-{
- explicit rstVersionAdded(const QVersionNumber &v) : m_version(v) {}
-
- const QVersionNumber m_version;
-};
-
-static QTextStream &operator<<(QTextStream &s, const rstVersionAdded &v)
-{
- s << ".. versionadded:: "<< v.m_version.toString() << "\n\n";
- return s;
-}
-
-static QByteArray rstDeprecationNote(const char *what)
-{
- return QByteArrayLiteral(".. note:: This ")
- + what + QByteArrayLiteral(" is deprecated.\n\n");
-}
-
-// RST anchor string: Anything else but letters, numbers, '_' or '.' replaced by '-'
-static inline bool isValidRstLabelChar(QChar c)
-{
- return c.isLetterOrNumber() || c == QLatin1Char('_') || c == QLatin1Char('.');
-}
-
-static QString toRstLabel(QString s)
-{
- for (int i = 0, size = s.size(); i < size; ++i) {
- if (!isValidRstLabelChar(s.at(i)))
- s[i] = QLatin1Char('-');
- }
- return s;
-}
-
-class rstLabel
-{
-public:
- explicit rstLabel(const QString &l) : m_label(l) {}
-
- friend QTextStream &operator<<(QTextStream &str, const rstLabel &a)
- {
- str << ".. _" << toRstLabel(a.m_label) << ":\n\n";
- return str;
- }
-
-private:
- const QString &m_label;
-};
-
-struct QtXmlToSphinx::LinkContext
-{
- enum Type
- {
- Method = 0x1, Function = 0x2,
- FunctionMask = Method | Function,
- Class = 0x4, Attribute = 0x8, Module = 0x10,
- Reference = 0x20, External= 0x40
- };
-
- enum Flags { InsideBold = 0x1, InsideItalic = 0x2 };
-
- explicit LinkContext(const QString &ref) : linkRef(ref) {}
-
- QString linkRef;
- QString linkText;
- Type type = Reference;
- int flags = 0;
-};
-
-static const char *linkKeyWord(QtXmlToSphinx::LinkContext::Type type)
-{
- switch (type) {
- case QtXmlToSphinx::LinkContext::Method:
- return ":meth:";
- case QtXmlToSphinx::LinkContext::Function:
- return ":func:";
- case QtXmlToSphinx::LinkContext::Class:
- return ":class:";
- case QtXmlToSphinx::LinkContext::Attribute:
- return ":attr:";
- case QtXmlToSphinx::LinkContext::Module:
- return ":mod:";
- case QtXmlToSphinx::LinkContext::Reference:
- return ":ref:";
- case QtXmlToSphinx::LinkContext::External:
- break;
- case QtXmlToSphinx::LinkContext::FunctionMask:
- break;
- }
- return "";
-}
-
-QTextStream &operator<<(QTextStream &str, const QtXmlToSphinx::LinkContext &linkContext)
-{
- // Temporarily turn off bold/italic since links do not work within
- if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideBold)
- str << "**";
- else if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideItalic)
- str << '*';
- str << ' ' << linkKeyWord(linkContext.type) << '`';
- const bool isExternal = linkContext.type == QtXmlToSphinx::LinkContext::External;
- if (!linkContext.linkText.isEmpty()) {
- writeEscapedRstText(str, linkContext.linkText);
- if (isExternal && !linkContext.linkText.endsWith(QLatin1Char(' ')))
- str << ' ';
- str << '<';
- }
- // Convert page titles to RST labels
- str << (linkContext.type == QtXmlToSphinx::LinkContext::Reference
- ? toRstLabel(linkContext.linkRef) : linkContext.linkRef);
- if (!linkContext.linkText.isEmpty())
- str << '>';
- str << '`';
- if (isExternal)
- str << '_';
- str << ' ';
- if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideBold)
- str << "**";
- else if (linkContext.flags & QtXmlToSphinx::LinkContext::InsideItalic)
- str << '*';
- return str;
-}
-
-QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context)
- : m_tableHasHeader(false), m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false)
-{
- m_handlerMap.insert(QLatin1String("heading"), &QtXmlToSphinx::handleHeadingTag);
- m_handlerMap.insert(QLatin1String("brief"), &QtXmlToSphinx::handleParaTag);
- m_handlerMap.insert(QLatin1String("para"), &QtXmlToSphinx::handleParaTag);
- m_handlerMap.insert(QLatin1String("italic"), &QtXmlToSphinx::handleItalicTag);
- m_handlerMap.insert(QLatin1String("bold"), &QtXmlToSphinx::handleBoldTag);
- m_handlerMap.insert(QLatin1String("see-also"), &QtXmlToSphinx::handleSeeAlsoTag);
- m_handlerMap.insert(QLatin1String("snippet"), &QtXmlToSphinx::handleSnippetTag);
- m_handlerMap.insert(QLatin1String("dots"), &QtXmlToSphinx::handleDotsTag);
- m_handlerMap.insert(QLatin1String("codeline"), &QtXmlToSphinx::handleDotsTag);
- m_handlerMap.insert(QLatin1String("table"), &QtXmlToSphinx::handleTableTag);
- m_handlerMap.insert(QLatin1String("header"), &QtXmlToSphinx::handleRowTag);
- m_handlerMap.insert(QLatin1String("row"), &QtXmlToSphinx::handleRowTag);
- m_handlerMap.insert(QLatin1String("item"), &QtXmlToSphinx::handleItemTag);
- m_handlerMap.insert(QLatin1String("argument"), &QtXmlToSphinx::handleArgumentTag);
- m_handlerMap.insert(QLatin1String("teletype"), &QtXmlToSphinx::handleArgumentTag);
- m_handlerMap.insert(QLatin1String("link"), &QtXmlToSphinx::handleLinkTag);
- m_handlerMap.insert(QLatin1String("inlineimage"), &QtXmlToSphinx::handleInlineImageTag);
- m_handlerMap.insert(QLatin1String("image"), &QtXmlToSphinx::handleImageTag);
- m_handlerMap.insert(QLatin1String("list"), &QtXmlToSphinx::handleListTag);
- m_handlerMap.insert(QLatin1String("term"), &QtXmlToSphinx::handleTermTag);
- m_handlerMap.insert(QLatin1String("raw"), &QtXmlToSphinx::handleRawTag);
- m_handlerMap.insert(QLatin1String("underline"), &QtXmlToSphinx::handleItalicTag);
- m_handlerMap.insert(QLatin1String("superscript"), &QtXmlToSphinx::handleSuperScriptTag);
- m_handlerMap.insert(QLatin1String("code"), &QtXmlToSphinx::handleCodeTag);
- m_handlerMap.insert(QLatin1String("badcode"), &QtXmlToSphinx::handleCodeTag);
- m_handlerMap.insert(QLatin1String("legalese"), &QtXmlToSphinx::handleCodeTag);
- m_handlerMap.insert(QLatin1String("rst"), &QtXmlToSphinx::handleRstPassTroughTag);
- m_handlerMap.insert(QLatin1String("section"), &QtXmlToSphinx::handleAnchorTag);
- m_handlerMap.insert(QLatin1String("quotefile"), &QtXmlToSphinx::handleQuoteFileTag);
-
- // ignored tags
- m_handlerMap.insert(QLatin1String("generatedlist"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("tableofcontents"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("quotefromfile"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("skipto"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("target"), &QtXmlToSphinx::handleTargetTag);
- m_handlerMap.insert(QLatin1String("page"), &QtXmlToSphinx::handlePageTag);
- m_handlerMap.insert(QLatin1String("group"), &QtXmlToSphinx::handlePageTag);
-
- // useless tags
- m_handlerMap.insert(QLatin1String("description"), &QtXmlToSphinx::handleUselessTag);
- m_handlerMap.insert(QLatin1String("definition"), &QtXmlToSphinx::handleUselessTag);
- m_handlerMap.insert(QLatin1String("printuntil"), &QtXmlToSphinx::handleUselessTag);
- m_handlerMap.insert(QLatin1String("relation"), &QtXmlToSphinx::handleUselessTag);
-
- // Doxygen tags
- m_handlerMap.insert(QLatin1String("title"), &QtXmlToSphinx::handleHeadingTag);
- m_handlerMap.insert(QLatin1String("ref"), &QtXmlToSphinx::handleParaTag);
- m_handlerMap.insert(QLatin1String("computeroutput"), &QtXmlToSphinx::handleParaTag);
- m_handlerMap.insert(QLatin1String("detaileddescription"), &QtXmlToSphinx::handleParaTag);
- m_handlerMap.insert(QLatin1String("name"), &QtXmlToSphinx::handleParaTag);
- m_handlerMap.insert(QLatin1String("listitem"), &QtXmlToSphinx::handleItemTag);
- m_handlerMap.insert(QLatin1String("parametername"), &QtXmlToSphinx::handleItemTag);
- m_handlerMap.insert(QLatin1String("parameteritem"), &QtXmlToSphinx::handleItemTag);
- m_handlerMap.insert(QLatin1String("ulink"), &QtXmlToSphinx::handleLinkTag);
- m_handlerMap.insert(QLatin1String("itemizedlist"), &QtXmlToSphinx::handleListTag);
- m_handlerMap.insert(QLatin1String("parameternamelist"), &QtXmlToSphinx::handleListTag);
- m_handlerMap.insert(QLatin1String("parameterlist"), &QtXmlToSphinx::handleListTag);
-
- // Doxygen ignored tags
- m_handlerMap.insert(QLatin1String("highlight"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("linebreak"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("programlisting"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("xreftitle"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("sp"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("entry"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("simplesect"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("verbatim"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("xrefsect"), &QtXmlToSphinx::handleIgnoredTag);
- m_handlerMap.insert(QLatin1String("xrefdescription"), &QtXmlToSphinx::handleIgnoredTag);
-
- m_result = transform(doc);
-}
-
-void QtXmlToSphinx::pushOutputBuffer()
-{
- auto *buffer = new QString();
- m_buffers << buffer;
- m_output.setString(buffer);
-}
-
-QString QtXmlToSphinx::popOutputBuffer()
-{
- Q_ASSERT(!m_buffers.isEmpty());
- QString* str = m_buffers.pop();
- QString strcpy(*str);
- delete str;
- m_output.setString(m_buffers.isEmpty() ? 0 : m_buffers.top());
- return strcpy;
-}
-
-QString QtXmlToSphinx::expandFunction(const QString& function) const
-{
- const int firstDot = function.indexOf(QLatin1Char('.'));
- const AbstractMetaClass *metaClass = nullptr;
- if (firstDot != -1) {
- const QStringRef className = function.leftRef(firstDot);
- const AbstractMetaClassList &classes = m_generator->classes();
- for (const AbstractMetaClass *cls : classes) {
- if (cls->name() == className) {
- metaClass = cls;
- break;
- }
- }
- }
-
- return metaClass
- ? metaClass->typeEntry()->qualifiedTargetLangName()
- + function.right(function.size() - firstDot)
- : function;
-}
-
-QString QtXmlToSphinx::resolveContextForMethod(const QString& methodName) const
-{
- const QStringRef currentClass = m_context.splitRef(QLatin1Char('.')).constLast();
-
- const AbstractMetaClass *metaClass = nullptr;
- const AbstractMetaClassList &classes = m_generator->classes();
- for (const AbstractMetaClass *cls : classes) {
- if (cls->name() == currentClass) {
- metaClass = cls;
- break;
- }
- }
-
- if (metaClass) {
- AbstractMetaFunctionList funcList;
- const AbstractMetaFunctionList &methods = metaClass->queryFunctionsByName(methodName);
- for (AbstractMetaFunction *func : methods) {
- if (methodName == func->name())
- funcList.append(func);
- }
-
- const AbstractMetaClass *implementingClass = nullptr;
- for (AbstractMetaFunction *func : qAsConst(funcList)) {
- implementingClass = func->implementingClass();
- if (implementingClass->name() == currentClass)
- break;
- }
-
- if (implementingClass)
- return implementingClass->typeEntry()->qualifiedTargetLangName();
- }
-
- return QLatin1Char('~') + m_context;
-}
-
-QString QtXmlToSphinx::transform(const QString& doc)
-{
- Q_ASSERT(m_buffers.isEmpty());
- Indentation indentation(INDENT);
- if (doc.trimmed().isEmpty())
- return doc;
-
- pushOutputBuffer();
-
- QXmlStreamReader reader(doc);
-
- while (!reader.atEnd()) {
- QXmlStreamReader::TokenType token = reader.readNext();
- if (reader.hasError()) {
- QString message;
- QTextStream(&message) << "XML Error "
- << reader.errorString() << " at " << reader.lineNumber()
- << ':' << reader.columnNumber() << '\n' << doc;
- m_output << INDENT << message;
- qCWarning(lcShibokenDoc).noquote().nospace() << message;
- break;
- }
-
- if (token == QXmlStreamReader::StartElement) {
- QStringRef tagName = reader.name();
- TagHandler handler = m_handlerMap.value(tagName.toString(), &QtXmlToSphinx::handleUnknownTag);
- if (!m_handlers.isEmpty() && ( (m_handlers.top() == &QtXmlToSphinx::handleIgnoredTag) ||
- (m_handlers.top() == &QtXmlToSphinx::handleRawTag)) )
- handler = &QtXmlToSphinx::handleIgnoredTag;
-
- m_handlers.push(handler);
- }
- if (!m_handlers.isEmpty())
- (this->*(m_handlers.top()))(reader);
-
- if (token == QXmlStreamReader::EndElement) {
- m_handlers.pop();
- m_lastTagName = reader.name().toString();
- }
- }
-
- if (!m_inlineImages.isEmpty()) {
- // Write out inline image definitions stored in handleInlineImageTag().
- m_output << Qt::endl;
- for (const InlineImage &img : qAsConst(m_inlineImages))
- m_output << ".. |" << img.tag << "| image:: " << img.href << Qt::endl;
- m_output << Qt::endl;
- m_inlineImages.clear();
- }
-
- m_output.flush();
- QString retval = popOutputBuffer();
- Q_ASSERT(m_buffers.isEmpty());
- return retval;
-}
-
-static QString resolveFile(const QStringList &locations, const QString &path)
-{
- for (QString location : locations) {
- location.append(QLatin1Char('/'));
- location.append(path);
- if (QFileInfo::exists(location))
- return location;
- }
- return QString();
-}
-
-QString QtXmlToSphinx::readFromLocations(const QStringList &locations, const QString &path,
- const QString &identifier, QString *errorMessage)
-{
- QString resolvedPath;
- if (path.endsWith(QLatin1String(".cpp"))) {
- const QString pySnippet = path.left(path.size() - 3) + QLatin1String("py");
- resolvedPath = resolveFile(locations, pySnippet);
- }
- if (resolvedPath.isEmpty())
- resolvedPath = resolveFile(locations, path);
- if (resolvedPath.isEmpty()) {
- QTextStream(errorMessage) << "Could not resolve \"" << path << "\" in \""
- << locations.join(QLatin1String("\", \""));
- return QString(); // null
- }
- qCDebug(lcShibokenDoc).noquote().nospace() << "snippet file " << path
- << " [" << identifier << ']' << " resolved to " << resolvedPath;
- return readFromLocation(resolvedPath, identifier, errorMessage);
-}
-
-QString QtXmlToSphinx::readFromLocation(const QString &location, const QString &identifier,
- QString *errorMessage)
-{
- QFile inputFile;
- inputFile.setFileName(location);
- if (!inputFile.open(QIODevice::ReadOnly)) {
- QTextStream(errorMessage) << "Could not read code snippet file: "
- << QDir::toNativeSeparators(inputFile.fileName())
- << ": " << inputFile.errorString();
- return QString(); // null
- }
-
- QString code = QLatin1String(""); // non-null
- if (identifier.isEmpty()) {
- while (!inputFile.atEnd())
- code += QString::fromUtf8(inputFile.readLine());
- return code;
- }
-
- const QRegularExpression searchString(QLatin1String("//!\\s*\\[")
- + identifier + QLatin1String("\\]"));
- Q_ASSERT(searchString.isValid());
- static const QRegularExpression codeSnippetCode(QLatin1String("//!\\s*\\[[\\w\\d\\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 code;
-}
-
-void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader)
-{
- static int headingSize = 0;
- static char type;
- static char types[] = { '-', '^' };
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- uint typeIdx = reader.attributes().value(QLatin1String("level")).toUInt();
- if (typeIdx >= sizeof(types))
- type = types[sizeof(types)-1];
- else
- type = types[typeIdx];
- } else if (token == QXmlStreamReader::EndElement) {
- m_output << Pad(type, headingSize) << Qt::endl << Qt::endl;
- } else if (token == QXmlStreamReader::Characters) {
- m_output << Qt::endl << Qt::endl;
- headingSize = writeEscapedRstText(m_output, reader.text().trimmed());
- m_output << Qt::endl;
- }
-}
-
-void QtXmlToSphinx::handleParaTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- pushOutputBuffer();
- } else if (token == QXmlStreamReader::EndElement) {
- QString result = popOutputBuffer().simplified();
- if (result.startsWith(QLatin1String("**Warning:**")))
- result.replace(0, 12, QLatin1String(".. warning:: "));
- else if (result.startsWith(QLatin1String("**Note:**")))
- result.replace(0, 9, QLatin1String(".. note:: "));
-
- m_output << INDENT << result << Qt::endl << Qt::endl;
- } else if (token == QXmlStreamReader::Characters) {
- const QStringRef text = reader.text();
- const QChar end = lastChar(m_output);
- if (!text.isEmpty() && INDENT.indent == 0 && !end.isNull()) {
- QChar start = text[0];
- if ((end == QLatin1Char('*') || end == QLatin1Char('`')) && start != QLatin1Char(' ') && !start.isPunct())
- m_output << '\\';
- }
- m_output << INDENT << escape(text);
- }
-}
-
-void QtXmlToSphinx::handleItalicTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) {
- m_insideItalic = !m_insideItalic;
- m_output << '*';
- } else if (token == QXmlStreamReader::Characters) {
- m_output << escape(reader.text().trimmed());
- }
-}
-
-void QtXmlToSphinx::handleBoldTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) {
- m_insideBold = !m_insideBold;
- m_output << "**";
- } else if (token == QXmlStreamReader::Characters) {
- m_output << escape(reader.text().trimmed());
- }
-}
-
-void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement)
- m_output << "``";
- else if (token == QXmlStreamReader::Characters)
- m_output << reader.text().trimmed();
-}
-
-static inline QString functionLinkType() { return QStringLiteral("function"); }
-static inline QString classLinkType() { return QStringLiteral("class"); }
-
-static inline QString fixLinkType(const QStringRef &type)
-{
- // TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties
- // are recognized as such or not in the binding
- if (type == QLatin1String("property"))
- return functionLinkType();
- if (type == QLatin1String("typedef"))
- return classLinkType();
- return type.toString();
-}
-
-static inline QString linkSourceAttribute(const QString &type)
-{
- if (type == functionLinkType() || type == classLinkType())
- return QLatin1String("raw");
- return type == QLatin1String("enum") || type == QLatin1String("page")
- ? type : QLatin1String("href");
-}
-
-// "See also" links may appear as nested links:
-// <see-also>QAbstractXmlReceiver<link raw="isValid()" href="qxmlquery.html#isValid" type="function">isValid()</link>
-// which is handled in handleLinkTag
-// or direct text:
-// <see-also>rootIsDecorated()</see-also>
-// which is handled here.
-
-void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader)
-{
- switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- m_output << INDENT << ".. seealso:: ";
- break;
- case QXmlStreamReader::Characters: {
- // Direct embedded link: <see-also>rootIsDecorated()</see-also>
- const QStringRef textR = reader.text().trimmed();
- if (!textR.isEmpty()) {
- const QString text = textR.toString();
- if (m_seeAlsoContext.isNull()) {
- const QString type = text.endsWith(QLatin1String("()"))
- ? functionLinkType() : classLinkType();
- m_seeAlsoContext.reset(handleLinkStart(type, text));
- }
- handleLinkText(m_seeAlsoContext.data(), text);
- }
- }
- break;
- case QXmlStreamReader::EndElement:
- if (!m_seeAlsoContext.isNull()) { // direct, no nested </link> seen
- handleLinkEnd(m_seeAlsoContext.data());
- m_seeAlsoContext.reset();
- }
- m_output << Qt::endl << Qt::endl;
- break;
- default:
- break;
- }
-}
-
-static inline QString fallbackPathAttribute() { return QStringLiteral("path"); }
-
-static inline bool snippetComparison()
-{
- return ReportHandler::debugLevel() >= ReportHandler::FullDebug;
-}
-
-template <class Indent> // const char*/class Indentor
-void formatSnippet(QTextStream &str, Indent indent, const QString &snippet)
-{
- const QVector<QStringRef> lines = snippet.splitRef(QLatin1Char('\n'));
- for (const QStringRef &line : lines) {
- if (!line.trimmed().isEmpty())
- str << indent << line;
- str << Qt::endl;
- }
-}
-
-static QString msgSnippetComparison(const QString &location, const QString &identifier,
- const QString &pythonCode, const QString &fallbackCode)
-{
- QString result;
- QTextStream str(&result);
- str << "Python snippet " << location;
- if (!identifier.isEmpty())
- str << " [" << identifier << ']';
- str << ":\n";
- formatSnippet(str, " ", pythonCode);
- str << "Corresponding fallback snippet:\n";
- formatSnippet(str, " ", fallbackCode);
- str << "-- end --\n";
- return result;
-}
-
-void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- const bool consecutiveSnippet = m_lastTagName == QLatin1String("snippet")
- || m_lastTagName == QLatin1String("dots") || m_lastTagName == QLatin1String("codeline");
- if (consecutiveSnippet) {
- m_output.flush();
- m_output.string()->chop(2);
- }
- QString location = reader.attributes().value(QLatin1String("location")).toString();
- QString identifier = reader.attributes().value(QLatin1String("identifier")).toString();
- QString errorMessage;
- const QString pythonCode =
- readFromLocations(m_generator->codeSnippetDirs(), location, identifier, &errorMessage);
- if (!errorMessage.isEmpty())
- qCWarning(lcShibokenDoc, "%s", qPrintable(msgTagWarning(reader, m_context, m_lastTagName, errorMessage)));
- // Fall back to C++ snippet when "path" attribute is present.
- // Also read fallback snippet when comparison is desired.
- QString fallbackCode;
- if ((pythonCode.isEmpty() || snippetComparison())
- && reader.attributes().hasAttribute(fallbackPathAttribute())) {
- const QString fallback = reader.attributes().value(fallbackPathAttribute()).toString();
- if (QFileInfo::exists(fallback)) {
- if (pythonCode.isEmpty())
- qCWarning(lcShibokenDoc, "%s", qPrintable(msgFallbackWarning(reader, m_context, m_lastTagName, location, identifier, fallback)));
- fallbackCode = readFromLocation(fallback, identifier, &errorMessage);
- if (!errorMessage.isEmpty())
- qCWarning(lcShibokenDoc, "%s", qPrintable(msgTagWarning(reader, m_context, m_lastTagName, errorMessage)));
- }
- }
-
- if (!pythonCode.isEmpty() && !fallbackCode.isEmpty() && snippetComparison())
- qCDebug(lcShibokenDoc, "%s", qPrintable(msgSnippetComparison(location, identifier, pythonCode, fallbackCode)));
-
- if (!consecutiveSnippet)
- m_output << INDENT << "::\n\n";
-
- Indentation indentation(INDENT);
- const QString code = pythonCode.isEmpty() ? fallbackCode : pythonCode;
- if (code.isEmpty())
- m_output << INDENT << "<Code snippet \"" << location << ':' << identifier << "\" not found>\n";
- else
- formatSnippet(m_output, INDENT, code);
- m_output << Qt::endl;
- }
-}
-void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- const bool consecutiveSnippet = m_lastTagName == QLatin1String("snippet")
- || m_lastTagName == QLatin1String("dots") || m_lastTagName == QLatin1String("codeline");
- if (consecutiveSnippet) {
- m_output.flush();
- m_output.string()->chop(2);
- } else {
- m_output << INDENT << "::\n\n";
- }
- Indentation indentation(INDENT);
- pushOutputBuffer();
- m_output << INDENT;
- int indent = reader.attributes().value(QLatin1String("indent")).toInt();
- for (int i = 0; i < indent; ++i)
- m_output << ' ';
- } else if (token == QXmlStreamReader::Characters) {
- m_output << reader.text().toString();
- } else if (token == QXmlStreamReader::EndElement) {
- m_output << popOutputBuffer() << "\n\n\n";
- }
-}
-
-void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- m_currentTable.clear();
- m_tableHasHeader = false;
- } else if (token == QXmlStreamReader::EndElement) {
- // write the table on m_output
- m_currentTable.setHeaderEnabled(m_tableHasHeader);
- m_currentTable.normalize();
- m_output << ensureEndl << m_currentTable;
- m_currentTable.clear();
- }
-}
-
-void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- pushOutputBuffer();
- } else if (token == QXmlStreamReader::Characters) {
- m_output << reader.text().toString().replace(QLatin1String("::"), QLatin1String("."));
- } else if (token == QXmlStreamReader::EndElement) {
- TableCell cell;
- cell.data = popOutputBuffer().trimmed();
- m_currentTable.appendRow(TableRow(1, cell));
- }
-}
-
-
-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();
- TableCell cell;
- cell.colSpan = reader.attributes().value(QLatin1String("colspan")).toShort();
- cell.rowSpan = reader.attributes().value(QLatin1String("rowspan")).toShort();
- row << cell;
- pushOutputBuffer();
- } else if (token == QXmlStreamReader::EndElement) {
- QString data = popOutputBuffer().trimmed();
- if (!m_currentTable.isEmpty()) {
- TableRow& row = m_currentTable.last();
- if (!row.isEmpty())
- row.last().data = data;
- }
- }
-}
-
-void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- m_tableHasHeader = reader.name() == QLatin1String("header");
- m_currentTable.appendRow({});
- }
-}
-
-enum ListType { BulletList, OrderedList, EnumeratedList };
-
-static inline ListType webXmlListType(const QStringRef &t)
-{
- if (t == QLatin1String("enum"))
- return EnumeratedList;
- if (t == QLatin1String("ordered"))
- return OrderedList;
- return BulletList;
-}
-
-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) {
- listType = webXmlListType(reader.attributes().value(QLatin1String("type")));
- if (listType == EnumeratedList) {
- m_currentTable.appendRow(TableRow{TableCell(QLatin1String("Constant")),
- TableCell(QLatin1String("Description"))});
- m_tableHasHeader = true;
- }
- INDENT.indent--;
- } else if (token == QXmlStreamReader::EndElement) {
- INDENT.indent++;
- if (!m_currentTable.isEmpty()) {
- switch (listType) {
- case BulletList:
- case OrderedList: {
- m_output << Qt::endl;
- const char *separator = listType == BulletList ? "* " : "#. ";
- const char *indent = listType == BulletList ? " " : " ";
- for (const TableCell &cell : m_currentTable.constFirst()) {
- const QVector<QStringRef> itemLines = cell.data.splitRef(QLatin1Char('\n'));
- m_output << INDENT << separator << itemLines.constFirst() << Qt::endl;
- for (int i = 1, max = itemLines.count(); i < max; ++i)
- m_output << INDENT << indent << itemLines[i] << Qt::endl;
- }
- m_output << Qt::endl;
- }
- break;
- case EnumeratedList:
- m_currentTable.setHeaderEnabled(m_tableHasHeader);
- m_currentTable.normalize();
- m_output << ensureEndl << m_currentTable;
- break;
- }
- }
- m_currentTable.clear();
- }
-}
-
-void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader)
-{
- switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement: {
- // <link> embedded in <see-also> means the characters of <see-also> are no link.
- m_seeAlsoContext.reset();
- const QString type = fixLinkType(reader.attributes().value(QLatin1String("type")));
- const QString ref = reader.attributes().value(linkSourceAttribute(type)).toString();
- m_linkContext.reset(handleLinkStart(type, ref));
- }
- break;
- case QXmlStreamReader::Characters:
- Q_ASSERT(!m_linkContext.isNull());
- handleLinkText(m_linkContext.data(), reader.text().toString());
- break;
- case QXmlStreamReader::EndElement:
- Q_ASSERT(!m_linkContext.isNull());
- handleLinkEnd(m_linkContext.data());
- m_linkContext.reset();
- break;
- default:
- break;
- }
-}
-
-QtXmlToSphinx::LinkContext *QtXmlToSphinx::handleLinkStart(const QString &type, QString ref) const
-{
- ref.replace(QLatin1String("::"), QLatin1String("."));
- ref.remove(QLatin1String("()"));
- auto *result = new LinkContext(ref);
-
- if (m_insideBold)
- result->flags |= LinkContext::InsideBold;
- else if (m_insideItalic)
- result->flags |= LinkContext::InsideItalic;
-
- if (type == functionLinkType() && !m_context.isEmpty()) {
- result->type = LinkContext::Method;
- const QVector<QStringRef> rawlinklist = result->linkRef.splitRef(QLatin1Char('.'));
- if (rawlinklist.size() == 1 || rawlinklist.constFirst() == m_context) {
- QString context = resolveContextForMethod(rawlinklist.constLast().toString());
- if (!result->linkRef.startsWith(context))
- result->linkRef.prepend(context + QLatin1Char('.'));
- } else {
- result->linkRef = expandFunction(result->linkRef);
- }
- } else if (type == functionLinkType() && m_context.isEmpty()) {
- result->type = LinkContext::Function;
- } else if (type == classLinkType()) {
- result->type = LinkContext::Class;
- if (const TypeEntry *type = TypeDatabase::instance()->findType(result->linkRef)) {
- result->linkRef = type->qualifiedTargetLangName();
- } else { // fall back to the old heuristic if the type wasn't found.
- const QVector<QStringRef> rawlinklist = result->linkRef.splitRef(QLatin1Char('.'));
- QStringList splittedContext = m_context.split(QLatin1Char('.'));
- if (rawlinklist.size() == 1 || rawlinklist.constFirst() == splittedContext.constLast()) {
- splittedContext.removeLast();
- result->linkRef.prepend(QLatin1Char('~') + splittedContext.join(QLatin1Char('.'))
- + QLatin1Char('.'));
- }
- }
- } else if (type == QLatin1String("enum")) {
- result->type = LinkContext::Attribute;
- } else if (type == QLatin1String("page")) {
- // Module, external web page or reference
- if (result->linkRef == m_generator->moduleName())
- result->type = LinkContext::Module;
- else if (result->linkRef.startsWith(QLatin1String("http")))
- result->type = LinkContext::External;
- else
- result->type = LinkContext::Reference;
- } else if (type == QLatin1String("external")) {
- result->type = LinkContext::External;
- } else {
- result->type = LinkContext::Reference;
- }
- return result;
-}
-
-// <link raw="Model/View Classes" href="model-view-programming.html#model-view-classes"
-// type="page" page="Model/View Programming">Model/View Classes</link>
-// <link type="page" page="http://doc.qt.io/qt-5/class.html">QML types</link>
-// <link raw="Qt Quick" href="qtquick-index.html" type="page" page="Qt Quick">Qt Quick</link>
-// <link raw="QObject" href="qobject.html" type="class">QObject</link>
-// <link raw="Qt::Window" href="qt.html#WindowType-enum" type="enum" enum="Qt::WindowType">Qt::Window</link>
-// <link raw="QNetworkSession::reject()" href="qnetworksession.html#reject" type="function">QNetworkSession::reject()</link>
-
-static QString fixLinkText(const QtXmlToSphinx::LinkContext *linkContext,
- QString linktext)
-{
- if (linkContext->type == QtXmlToSphinx::LinkContext::External
- || linkContext->type == QtXmlToSphinx::LinkContext::Reference) {
- return linktext;
- }
- // For the language reference documentation, strip the module name.
- // Clear the link text if that matches the function/class/enumeration name.
- const int lastSep = linktext.lastIndexOf(QLatin1String("::"));
- if (lastSep != -1)
- linktext.remove(0, lastSep + 2);
- else
- stripPythonQualifiers(&linktext);
- if (linkContext->linkRef == linktext)
- return QString();
- if ((linkContext->type & QtXmlToSphinx::LinkContext::FunctionMask) != 0
- && (linkContext->linkRef + QLatin1String("()")) == linktext) {
- return QString();
- }
- return linktext;
-}
-
-void QtXmlToSphinx::handleLinkText(LinkContext *linkContext, const QString &linktext) const
-{
- linkContext->linkText = fixLinkText(linkContext, linktext);
-}
-
-void QtXmlToSphinx::handleLinkEnd(LinkContext *linkContext)
-{
- m_output << *linkContext;
-}
-
-// Copy images that are placed in a subdirectory "images" under the webxml files
-// by qdoc to a matching subdirectory under the "rst/PySide2/<module>" directory
-static bool copyImage(const QString &href, const QString &docDataDir,
- const QString &context, const QString &outputDir,
- QString *errorMessage)
-{
- const QChar slash = QLatin1Char('/');
- const int 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);
- if (!imageSource.exists()) {
- QTextStream(errorMessage) << "Image " << href << " does not exist in "
- << QDir::toNativeSeparators(docDataDir);
- return false;
- }
- // Determine directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui".
- // 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(QLatin1Char('.'));
- if (lastDot != -1)
- relativeTargetDir.truncate(lastDot);
- relativeTargetDir.replace(QLatin1Char('.'), slash);
- if (!imagePath.isEmpty())
- relativeTargetDir += slash + imagePath;
-
- const QString targetDir = outputDir + slash + relativeTargetDir;
- const QString targetFileName = targetDir + slash + imageFileName;
- if (QFileInfo::exists(targetFileName))
- return true;
- if (!QFileInfo::exists(targetDir)) {
- const QDir outDir(outputDir);
- if (!outDir.mkpath(relativeTargetDir)) {
- QTextStream(errorMessage) << "Cannot create " << QDir::toNativeSeparators(relativeTargetDir)
- << " under " << QDir::toNativeSeparators(outputDir);
- return false;
- }
- }
-
- QFile source(imageSource.absoluteFilePath());
- if (!source.copy(targetFileName)) {
- QTextStream(errorMessage) << "Cannot copy " << QDir::toNativeSeparators(source.fileName())
- << " to " << QDir::toNativeSeparators(targetFileName) << ": "
- << source.errorString();
- return false;
- }
- qCDebug(lcShibokenDoc()).noquote().nospace() << __FUNCTION__ << " href=\""
- << href << "\", context=\"" << context << "\", docDataDir=\""
- << docDataDir << "\", outputDir=\"" << outputDir << "\", copied \""
- << source.fileName() << "\"->\"" << targetFileName << '"';
- return true;
-}
-
-bool QtXmlToSphinx::copyImage(const QString &href) const
-{
- QString errorMessage;
- const bool result =
- ::copyImage(href, m_generator->docDataDir(), m_context,
- m_generator->outputDirectory(), &errorMessage);
- if (!result)
- qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
- return result;
-}
-
-void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader)
-{
- if (reader.tokenType() != QXmlStreamReader::StartElement)
- return;
- const QString href = reader.attributes().value(QLatin1String("href")).toString();
- if (copyImage(href))
- m_output << INDENT << ".. image:: " << href << Qt::endl << Qt::endl;
-}
-
-void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
-{
- if (reader.tokenType() != QXmlStreamReader::StartElement)
- return;
- const QString href = reader.attributes().value(QLatin1String("href")).toString();
- if (!copyImage(href))
- return;
- // Handle inline images by substitution references. Insert a unique tag
- // enclosed by '|' and define it further down. Determine tag from the base
- //file name with number.
- QString tag = href;
- int pos = tag.lastIndexOf(QLatin1Char('/'));
- if (pos != -1)
- tag.remove(0, pos + 1);
- pos = tag.indexOf(QLatin1Char('.'));
- if (pos != -1)
- tag.truncate(pos);
- tag += QString::number(m_inlineImages.size() + 1);
- m_inlineImages.append(InlineImage{tag, href});
- m_output << '|' << tag << '|' << ' ';
-}
-
-void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- QString format = reader.attributes().value(QLatin1String("format")).toString();
- m_output << INDENT << ".. raw:: " << format.toLower() << Qt::endl << Qt::endl;
- } else if (token == QXmlStreamReader::Characters) {
- const QVector<QStringRef> lst(reader.text().split(QLatin1Char('\n')));
- for (const QStringRef &row : lst)
- m_output << INDENT << INDENT << row << Qt::endl;
- } else if (token == QXmlStreamReader::EndElement) {
- m_output << Qt::endl << Qt::endl;
- }
-}
-
-void QtXmlToSphinx::handleCodeTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- m_output << INDENT << "::\n\n";
- INDENT.indent++;
- } else if (token == QXmlStreamReader::Characters) {
- const QVector<QStringRef> lst(reader.text().split(QLatin1Char('\n')));
- for (const QStringRef &row : lst)
- m_output << INDENT << INDENT << row << Qt::endl;
- } else if (token == QXmlStreamReader::EndElement) {
- m_output << Qt::endl << Qt::endl;
- INDENT.indent--;
- }
-}
-
-void QtXmlToSphinx::handleUnknownTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement)
- qCDebug(lcShibokenDoc).noquote().nospace() << "Unknown QtDoc tag: \"" << reader.name().toString() << "\".";
-}
-
-void QtXmlToSphinx::handleSuperScriptTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- m_output << " :sup:`";
- pushOutputBuffer();
- } else if (token == QXmlStreamReader::Characters) {
- m_output << reader.text().toString();
- } else if (token == QXmlStreamReader::EndElement) {
- m_output << popOutputBuffer();
- m_output << '`';
- }
-}
-
-void QtXmlToSphinx::handlePageTag(QXmlStreamReader &reader)
-{
- if (reader.tokenType() != QXmlStreamReader::StartElement)
- return;
-
- const QStringRef title = reader.attributes().value(titleAttribute());
- if (!title.isEmpty())
- m_output << rstLabel(title.toString());
-
- const QStringRef fullTitle = reader.attributes().value(fullTitleAttribute());
- const int size = fullTitle.isEmpty()
- ? writeEscapedRstText(m_output, title)
- : writeEscapedRstText(m_output, fullTitle);
-
- m_output << Qt::endl << Pad('*', size) << Qt::endl << Qt::endl;
-}
-
-void QtXmlToSphinx::handleTargetTag(QXmlStreamReader &reader)
-{
- if (reader.tokenType() != QXmlStreamReader::StartElement)
- return;
- const QStringRef name = reader.attributes().value(nameAttribute());
- if (!name.isEmpty())
- m_output << INDENT << rstLabel(name.toString());
-}
-
-void QtXmlToSphinx::handleIgnoredTag(QXmlStreamReader&)
-{
-}
-
-void QtXmlToSphinx::handleUselessTag(QXmlStreamReader&)
-{
- // Tag "description" just marks the init of "Detailed description" title.
- // Tag "definition" just marks enums. We have a different way to process them.
-}
-
-void QtXmlToSphinx::handleAnchorTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- QString anchor;
- if (reader.attributes().hasAttribute(QLatin1String("id")))
- anchor = reader.attributes().value(QLatin1String("id")).toString();
- else if (reader.attributes().hasAttribute(QLatin1String("name")))
- anchor = reader.attributes().value(QLatin1String("name")).toString();
- if (!anchor.isEmpty() && m_opened_anchor != anchor) {
- m_opened_anchor = anchor;
- if (!m_context.isEmpty())
- anchor.prepend(m_context + QLatin1Char('_'));
- m_output << INDENT << rstLabel(anchor);
- }
- } else if (token == QXmlStreamReader::EndElement) {
- m_opened_anchor.clear();
- }
-}
-
-void QtXmlToSphinx::handleRstPassTroughTag(QXmlStreamReader& reader)
-{
- if (reader.tokenType() == QXmlStreamReader::Characters)
- m_output << reader.text();
-}
-
-void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader)
-{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::Characters) {
- QString location = reader.text().toString();
- location.prepend(m_generator->libSourceDir() + QLatin1Char('/'));
- QString errorMessage;
- QString code = readFromLocation(location, QString(), &errorMessage);
- if (!errorMessage.isEmpty())
- qCWarning(lcShibokenDoc, "%s", qPrintable(msgTagWarning(reader, m_context, m_lastTagName, errorMessage)));
- m_output << INDENT << "::\n\n";
- Indentation indentation(INDENT);
- if (code.isEmpty())
- m_output << INDENT << "<Code snippet \"" << location << "\" not found>\n";
- else
- formatCode(m_output, code, INDENT);
- m_output << Qt::endl;
- }
-}
-
-bool QtXmlToSphinx::convertToRst(QtDocGenerator *generator,
- const QString &sourceFileName,
- const QString &targetFileName,
- const QString &context, QString *errorMessage)
-{
- QFile sourceFile(sourceFileName);
- if (!sourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- if (errorMessage)
- *errorMessage = msgCannotOpenForReading(sourceFile);
- return false;
- }
- const QString doc = QString::fromUtf8(sourceFile.readAll());
- sourceFile.close();
-
- FileOut targetFile(targetFileName);
- QtXmlToSphinx x(generator, doc, context);
- targetFile.stream << x;
- return targetFile.done(errorMessage) != FileOut::Failure;
-}
-
-void QtXmlToSphinx::Table::normalize()
-{
- if (m_normalized || isEmpty())
- return;
-
- //QDoc3 generates tables with wrong number of columns. We have to
- //check and if necessary, merge the last columns.
- int maxCols = -1;
- for (const auto &row : qAsConst(m_rows)) {
- if (row.count() > maxCols)
- maxCols = row.count();
- }
- if (maxCols <= 0)
- return;
- // add col spans
- for (int row = 0; row < m_rows.count(); ++row) {
- for (int col = 0; col < m_rows.at(row).count(); ++col) {
- QtXmlToSphinx::TableCell& cell = m_rows[row][col];
- bool mergeCols = (col >= maxCols);
- if (cell.colSpan > 0) {
- QtXmlToSphinx::TableCell newCell;
- newCell.colSpan = -1;
- for (int i = 0, max = cell.colSpan-1; i < max; ++i) {
- m_rows[row].insert(col + 1, newCell);
- }
- cell.colSpan = 0;
- col++;
- } else if (mergeCols) {
- m_rows[row][maxCols - 1].data += QLatin1Char(' ') + cell.data;
- }
- }
- }
-
- // row spans
- const int numCols = m_rows.constFirst().count();
- for (int col = 0; col < numCols; ++col) {
- for (int row = 0; row < m_rows.count(); ++row) {
- if (col < m_rows[row].count()) {
- QtXmlToSphinx::TableCell& cell = m_rows[row][col];
- if (cell.rowSpan > 0) {
- QtXmlToSphinx::TableCell newCell;
- newCell.rowSpan = -1;
- int targetRow = row + 1;
- const int targetEndRow =
- std::min(targetRow + cell.rowSpan - 1, m_rows.count());
- cell.rowSpan = 0;
- for ( ; targetRow < targetEndRow; ++targetRow)
- m_rows[targetRow].insert(col, newCell);
- row++;
- }
- }
- }
- }
- m_normalized = true;
-}
-
-QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table)
-{
- table.format(s);
- return s;
-}
-
-void QtXmlToSphinx::Table::format (QTextStream& s) const
-{
- if (isEmpty())
- return;
-
- if (!isNormalized()) {
- qCDebug(lcShibokenDoc) << "Attempt to print an unnormalized table!";
- return;
- }
-
- // calc width and height of each column and row
- const int headerColumnCount = m_rows.constFirst().count();
- QVector<int> colWidths(headerColumnCount);
- QVector<int> rowHeights(m_rows.count());
- for (int i = 0, maxI = m_rows.count(); i < maxI; ++i) {
- const QtXmlToSphinx::TableRow& row = m_rows.at(i);
- for (int j = 0, maxJ = std::min(row.count(), colWidths.size()); j < maxJ; ++j) {
- const QVector<QStringRef> rowLines = row[j].data.splitRef(QLatin1Char('\n')); // cache this would be a good idea
- for (const QStringRef &str : rowLines)
- colWidths[j] = std::max(colWidths[j], str.count());
- rowHeights[i] = std::max(rowHeights[i], row[j].data.count(QLatin1Char('\n')) + 1);
- }
- }
-
- if (!*std::max_element(colWidths.begin(), colWidths.end()))
- return; // empty table (table with empty cells)
-
- // create a horizontal line to be used later.
- QString horizontalLine = QLatin1String("+");
- for (int i = 0, max = colWidths.count(); i < max; ++i) {
- horizontalLine += QString(colWidths.at(i), QLatin1Char('-'));
- horizontalLine += QLatin1Char('+');
- }
-
- // write table rows
- for (int i = 0, maxI = m_rows.count(); i < maxI; ++i) { // for each row
- const QtXmlToSphinx::TableRow& row = m_rows.at(i);
-
- // print line
- s << INDENT << '+';
- for (int col = 0; col < headerColumnCount; ++col) {
- char c;
- if (col >= row.length() || row[col].rowSpan == -1)
- c = ' ';
- else if (i == 1 && hasHeader())
- c = '=';
- else
- c = '-';
- s << Pad(c, colWidths.at(col)) << '+';
- }
- s << Qt::endl;
-
-
- // Print the table cells
- for (int rowLine = 0; rowLine < rowHeights[i]; ++rowLine) { // for each line in a row
- int j = 0;
- for (int maxJ = std::min(row.count(), headerColumnCount); j < maxJ; ++j) { // for each column
- const QtXmlToSphinx::TableCell& cell = row[j];
- const QVector<QStringRef> rowLines = cell.data.splitRef(QLatin1Char('\n')); // FIXME: Cache this!!!
- if (!j) // First column, so we need print the identation
- s << INDENT;
-
- if (!j || !cell.colSpan)
- s << '|';
- else
- s << ' ';
- if (rowLine < rowLines.count())
- s << qSetFieldWidth(colWidths[j]) << Qt::left << rowLines.at(rowLine) << qSetFieldWidth(0);
- else
- s << Pad(' ', colWidths.at(j));
- }
- for ( ; j < headerColumnCount; ++j) // pad
- s << '|' << Pad(' ', colWidths.at(j));
- s << "|\n";
- }
- }
- s << INDENT << horizontalLine << Qt::endl << Qt::endl;
-}
-
-static QString getFuncName(const AbstractMetaFunction* cppFunc) {
- static bool hashInitialized = false;
- static QHash<QString, QString> operatorsHash;
- if (!hashInitialized) {
- operatorsHash.insert(QLatin1String("operator+"), QLatin1String("__add__"));
- operatorsHash.insert(QLatin1String("operator+="), QLatin1String("__iadd__"));
- operatorsHash.insert(QLatin1String("operator-"), QLatin1String("__sub__"));
- operatorsHash.insert(QLatin1String("operator-="), QLatin1String("__isub__"));
- operatorsHash.insert(QLatin1String("operator*"), QLatin1String("__mul__"));
- operatorsHash.insert(QLatin1String("operator*="), QLatin1String("__imul__"));
- operatorsHash.insert(QLatin1String("operator/"), QLatin1String("__div__"));
- operatorsHash.insert(QLatin1String("operator/="), QLatin1String("__idiv__"));
- operatorsHash.insert(QLatin1String("operator%"), QLatin1String("__mod__"));
- operatorsHash.insert(QLatin1String("operator%="), QLatin1String("__imod__"));
- operatorsHash.insert(QLatin1String("operator<<"), QLatin1String("__lshift__"));
- operatorsHash.insert(QLatin1String("operator<<="), QLatin1String("__ilshift__"));
- operatorsHash.insert(QLatin1String("operator>>"), QLatin1String("__rshift__"));
- operatorsHash.insert(QLatin1String("operator>>="), QLatin1String("__irshift__"));
- operatorsHash.insert(QLatin1String("operator&"), QLatin1String("__and__"));
- operatorsHash.insert(QLatin1String("operator&="), QLatin1String("__iand__"));
- operatorsHash.insert(QLatin1String("operator|"), QLatin1String("__or__"));
- operatorsHash.insert(QLatin1String("operator|="), QLatin1String("__ior__"));
- operatorsHash.insert(QLatin1String("operator^"), QLatin1String("__xor__"));
- operatorsHash.insert(QLatin1String("operator^="), QLatin1String("__ixor__"));
- operatorsHash.insert(QLatin1String("operator=="), QLatin1String("__eq__"));
- operatorsHash.insert(QLatin1String("operator!="), QLatin1String("__ne__"));
- operatorsHash.insert(QLatin1String("operator<"), QLatin1String("__lt__"));
- operatorsHash.insert(QLatin1String("operator<="), QLatin1String("__le__"));
- operatorsHash.insert(QLatin1String("operator>"), QLatin1String("__gt__"));
- operatorsHash.insert(QLatin1String("operator>="), QLatin1String("__ge__"));
- hashInitialized = true;
- }
-
- QHash<QString, QString>::const_iterator it = operatorsHash.constFind(cppFunc->name());
- QString result = it != operatorsHash.cend() ? it.value() : cppFunc->name();
- result.replace(QLatin1String("::"), QLatin1String("."));
- return result;
-}
-
-QtDocGenerator::QtDocGenerator() : m_docParser(nullptr)
-{
-}
-
-QtDocGenerator::~QtDocGenerator()
-{
- delete m_docParser;
-}
-
-QString QtDocGenerator::fileNameSuffix() const
-{
- return QLatin1String(".rst");
-}
-
-bool QtDocGenerator::shouldGenerate(const AbstractMetaClass *cls) const
-{
- return Generator::shouldGenerate(cls)
- && cls->typeEntry()->type() != TypeEntry::SmartPointerType;
-}
-
-QString QtDocGenerator::fileNameForContext(const GeneratorContext &context) const
-{
- const AbstractMetaClass *metaClass = context.metaClass();
- if (!context.forSmartPointer()) {
- return getClassTargetFullName(metaClass, false) + fileNameSuffix();
- }
- const AbstractMetaType *smartPointerType = context.preciseType();
- QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass);
- return fileNameBase + fileNameSuffix();
-}
-
-void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc,
- const AbstractMetaClass *metaClass)
-{
- QString metaClassName;
-
- if (metaClass)
- metaClassName = getClassTargetFullName(metaClass);
-
- if (doc.format() == Documentation::Native) {
- QtXmlToSphinx x(this, doc.value(), metaClassName);
- s << x;
- } else {
- const QString &value = doc.value();
- const QVector<QStringRef> lines = value.splitRef(QLatin1Char('\n'));
- int typesystemIndentation = std::numeric_limits<int>::max();
- // check how many spaces must be removed from the beginning of each line
- for (const QStringRef &line : lines) {
- const auto it = std::find_if(line.cbegin(), line.cend(),
- [] (QChar c) { return !c.isSpace(); });
- if (it != line.cend())
- typesystemIndentation = qMin(typesystemIndentation, int(it - line.cbegin()));
- }
- if (typesystemIndentation == std::numeric_limits<int>::max())
- typesystemIndentation = 0;
- for (const QStringRef &line : lines) {
- s << INDENT
- << (typesystemIndentation > 0 && typesystemIndentation < line.size()
- ? line.right(line.size() - typesystemIndentation) : line)
- << Qt::endl;
- }
- }
-
- s << Qt::endl;
-}
-
-static void writeInheritedByList(QTextStream& s, const AbstractMetaClass* metaClass, const AbstractMetaClassList& allClasses)
-{
- AbstractMetaClassList res;
- for (AbstractMetaClass *c : allClasses) {
- if (c != metaClass && c->inheritsFrom(metaClass))
- res << c;
- }
-
- if (res.isEmpty())
- return;
-
- s << "**Inherited by:** ";
- QStringList classes;
- for (AbstractMetaClass *c : qAsConst(res))
- classes << QLatin1String(":ref:`") + getClassTargetFullName(c, false) + QLatin1Char('`');
- s << classes.join(QLatin1String(", ")) << Qt::endl << Qt::endl;
-}
-
-// Extract the <brief> section from a WebXML (class) documentation and remove it
-// from the source.
-static bool extractBrief(Documentation *sourceDoc, Documentation *brief)
-{
- if (sourceDoc->format() != Documentation::Native)
- return false;
- QString value = sourceDoc->value();
- const int briefStart = value.indexOf(briefStartElement());
- if (briefStart < 0)
- return false;
- const int briefEnd = value.indexOf(briefEndElement(), briefStart + briefStartElement().size());
- if (briefEnd < briefStart)
- return false;
- const int briefLength = briefEnd + briefEndElement().size() - briefStart;
- brief->setFormat(Documentation::Native);
- QString briefValue = value.mid(briefStart, briefLength);
- briefValue.insert(briefValue.size() - briefEndElement().size(),
- QLatin1String("<rst> More_...</rst>"));
- brief->setValue(briefValue);
- value.remove(briefStart, briefLength);
- sourceDoc->setValue(value);
- return true;
-}
-
-void QtDocGenerator::generateClass(QTextStream &s, const GeneratorContext &classContext)
-{
- const AbstractMetaClass *metaClass = classContext.metaClass();
- qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
-
- m_packages[metaClass->package()] << fileNameForContext(classContext);
-
- m_docParser->setPackageName(metaClass->package());
- m_docParser->fillDocumentation(const_cast<AbstractMetaClass*>(metaClass));
-
- QString className = getClassTargetFullName(metaClass, false);
- s << ".. _" << className << ":" << "\n\n";
- s << ".. currentmodule:: " << metaClass->package() << "\n\n\n";
-
- s << className << Qt::endl;
- s << Pad('*', className.count()) << Qt::endl << Qt::endl;
-
- auto documentation = metaClass->documentation();
- Documentation brief;
- if (extractBrief(&documentation, &brief))
- writeFormattedText(s, brief, metaClass);
-
- s << ".. inheritance-diagram:: " << getClassTargetFullName(metaClass, true) << Qt::endl
- << " :parts: 2\n\n"; // TODO: This would be a parameter in the future...
-
-
- writeInheritedByList(s, metaClass, classes());
-
- const auto version = versionOf(metaClass->typeEntry());
- if (!version.isNull())
- s << rstVersionAdded(version);
- if (metaClass->attributes().testFlag(AbstractMetaAttributes::Deprecated))
- s << rstDeprecationNote("class");
-
- writeFunctionList(s, metaClass);
-
- //Function list
- AbstractMetaFunctionList functionList = metaClass->functions();
- std::sort(functionList.begin(), functionList.end(), functionSort);
-
- s << "\nDetailed Description\n"
- "--------------------\n\n"
- << ".. _More:\n";
-
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass, nullptr);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, nullptr))
- writeFormattedText(s, documentation, metaClass);
-
- if (!metaClass->isNamespace())
- writeConstructors(s, metaClass);
- writeEnums(s, metaClass);
- if (!metaClass->isNamespace())
- writeFields(s, metaClass);
-
-
- for (AbstractMetaFunction *func : qAsConst(functionList)) {
- if (shouldSkip(func))
- continue;
-
- if (func->isStatic())
- s << ".. staticmethod:: ";
- else
- s << ".. method:: ";
-
- writeFunction(s, metaClass, func);
- }
-
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, nullptr);
-}
-
-void QtDocGenerator::writeFunctionList(QTextStream& s, const AbstractMetaClass* cppClass)
-{
- QStringList functionList;
- QStringList virtualList;
- QStringList signalList;
- QStringList slotList;
- QStringList staticFunctionList;
-
- const AbstractMetaFunctionList &classFunctions = cppClass->functions();
- for (AbstractMetaFunction *func : classFunctions) {
- if (shouldSkip(func))
- continue;
-
- QString className;
- if (!func->isConstructor())
- className = getClassTargetFullName(cppClass) + QLatin1Char('.');
- else if (func->implementingClass() && func->implementingClass()->enclosingClass())
- className = getClassTargetFullName(func->implementingClass()->enclosingClass()) + QLatin1Char('.');
- QString funcName = getFuncName(func);
-
- QString str = QLatin1String("def :meth:`");
-
- str += funcName;
- str += QLatin1Char('<');
- if (!funcName.startsWith(className))
- str += className;
- str += funcName;
- str += QLatin1String(">` (");
- str += parseArgDocStyle(cppClass, func);
- str += QLatin1Char(')');
-
- 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 (!functionList.isEmpty() || !staticFunctionList.isEmpty()) {
- QtXmlToSphinx::Table functionTable;
-
- s << "\nSynopsis\n--------\n\n";
-
- writeFunctionBlock(s, QLatin1String("Functions"), functionList);
- writeFunctionBlock(s, QLatin1String("Virtual functions"), virtualList);
- writeFunctionBlock(s, QLatin1String("Slots"), slotList);
- writeFunctionBlock(s, QLatin1String("Signals"), signalList);
- writeFunctionBlock(s, QLatin1String("Static functions"), staticFunctionList);
- }
-}
-
-void QtDocGenerator::writeFunctionBlock(QTextStream& s, const QString& title, QStringList& functions)
-{
- if (!functions.isEmpty()) {
- s << title << Qt::endl
- << QString(title.size(), QLatin1Char('^')) << Qt::endl;
-
- std::sort(functions.begin(), functions.end());
-
- s << ".. container:: function_list\n\n";
- Indentation indentation(INDENT);
- for (const QString &func : qAsConst(functions))
- s << INDENT << '*' << ' ' << func << Qt::endl;
-
- s << Qt::endl << Qt::endl;
- }
-}
-
-void QtDocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass)
-{
- static const QString section_title = QLatin1String(".. attribute:: ");
-
- const AbstractMetaEnumList &enums = cppClass->enums();
- for (AbstractMetaEnum *en : enums) {
- s << section_title << getClassTargetFullName(cppClass) << '.' << en->name() << Qt::endl << Qt::endl;
- writeFormattedText(s, en->documentation(), cppClass);
- const auto version = versionOf(en->typeEntry());
- if (!version.isNull())
- s << rstVersionAdded(version);
- }
-
-}
-
-void QtDocGenerator::writeFields(QTextStream& s, const AbstractMetaClass* cppClass)
-{
- static const QString section_title = QLatin1String(".. attribute:: ");
-
- const AbstractMetaFieldList &fields = cppClass->fields();
- for (AbstractMetaField *field : fields) {
- s << section_title << getClassTargetFullName(cppClass) << "." << field->name() << Qt::endl << Qt::endl;
- //TODO: request for member ‘documentation’ is ambiguous
- writeFormattedText(s, field->AbstractMetaAttributes::documentation(), cppClass);
- }
-}
-
-void QtDocGenerator::writeConstructors(QTextStream& s, const AbstractMetaClass* cppClass)
-{
- static const QString sectionTitle = QLatin1String(".. class:: ");
-
- AbstractMetaFunctionList lst = cppClass->queryFunctions(AbstractMetaClass::Constructors | AbstractMetaClass::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;
-
- IndentorBase<1> indent1;
- indent1.indent = INDENT.total();
- for (AbstractMetaFunction *func : qAsConst(lst)) {
- s << indent1;
- if (first) {
- first = false;
- s << sectionTitle;
- indent1.indent += sectionTitle.size();
- }
- s << functionSignature(cppClass, func) << "\n\n";
-
- const auto version = versionOf(func->typeEntry());
- if (!version.isNull())
- s << indent1 << rstVersionAdded(version);
- if (func->attributes().testFlag(AbstractMetaAttributes::Deprecated))
- s << indent1 << rstDeprecationNote("constructor");
-
- const AbstractMetaArgumentList &arguments = func->arguments();
- for (AbstractMetaArgument *arg : arguments) {
- if (!arg_map.contains(arg->name())) {
- arg_map.insert(arg->name(), arg);
- }
- }
- }
-
- s << Qt::endl;
-
- for (QHash<QString, AbstractMetaArgument*>::const_iterator it = arg_map.cbegin(), end = arg_map.cend(); it != end; ++it) {
- Indentation indentation(INDENT, 2);
- writeParameterType(s, cppClass, it.value());
- }
-
- s << Qt::endl;
-
- for (AbstractMetaFunction *func : qAsConst(lst))
- writeFormattedText(s, func->documentation(), cppClass);
-}
-
-QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */,
- const AbstractMetaFunction* func)
-{
- QString ret;
- int optArgs = 0;
-
- const AbstractMetaArgumentList &arguments = func->arguments();
- for (AbstractMetaArgument *arg : arguments) {
-
- if (func->argumentRemoved(arg->argumentIndex() + 1))
- continue;
-
- bool thisIsoptional = !arg->defaultValueExpression().isEmpty();
- if (optArgs || thisIsoptional) {
- ret += QLatin1Char('[');
- optArgs++;
- }
-
- if (arg->argumentIndex() > 0)
- ret += QLatin1String(", ");
-
- ret += arg->name();
-
- if (thisIsoptional) {
- QString defValue = arg->defaultValueExpression();
- if (defValue == QLatin1String("QString()")) {
- defValue = QLatin1String("\"\"");
- } else if (defValue == QLatin1String("QStringList()")
- || defValue.startsWith(QLatin1String("QVector"))
- || defValue.startsWith(QLatin1String("QList"))) {
- defValue = QLatin1String("list()");
- } else if (defValue == QLatin1String("QVariant()")) {
- defValue = none();
- } else {
- defValue.replace(QLatin1String("::"), QLatin1String("."));
- if (defValue == QLatin1String("nullptr"))
- defValue = none();
- else if (defValue == QLatin1String("0") && arg->type()->isObject())
- defValue = none();
- }
- ret += QLatin1Char('=') + defValue;
- }
- }
-
- ret += QString(optArgs, QLatin1Char(']'));
- return ret;
-}
-
-void QtDocGenerator::writeDocSnips(QTextStream &s,
- const CodeSnipList &codeSnips,
- TypeSystem::CodeSnipPosition position,
- TypeSystem::Language language)
-{
- Indentation indentation(INDENT);
- QStringList invalidStrings;
- const static QString startMarkup = QLatin1String("[sphinx-begin]");
- const static QString endMarkup = QLatin1String("[sphinx-end]");
-
- invalidStrings << QLatin1String("*") << QLatin1String("//") << QLatin1String("/*") << QLatin1String("*/");
-
- for (const CodeSnip &snip : codeSnips) {
- if ((snip.position != position) ||
- !(snip.language & language))
- continue;
-
- QString code = snip.code();
- while (code.contains(startMarkup) && code.contains(endMarkup)) {
- int startBlock = code.indexOf(startMarkup) + startMarkup.size();
- int endBlock = code.indexOf(endMarkup);
-
- if ((startBlock == -1) || (endBlock == -1))
- break;
-
- QString codeBlock = code.mid(startBlock, endBlock - startBlock);
- const QStringList rows = codeBlock.split(QLatin1Char('\n'));
- int currentRow = 0;
- int offset = 0;
-
- for (QString row : rows) {
- for (const QString &invalidString : qAsConst(invalidStrings))
- row.remove(invalidString);
-
- if (row.trimmed().size() == 0) {
- if (currentRow == 0)
- continue;
- s << Qt::endl;
- }
-
- if (currentRow == 0) {
- //find offset
- for (auto c : row) {
- if (c == QLatin1Char(' '))
- offset++;
- else if (c == QLatin1Char('\n'))
- offset = 0;
- else
- break;
- }
- }
- s << row.midRef(offset) << Qt::endl;
- currentRow++;
- }
-
- code = code.mid(endBlock+endMarkup.size());
- }
- }
-}
-
-bool QtDocGenerator::writeInjectDocumentation(QTextStream& s,
- TypeSystem::DocModificationMode mode,
- const AbstractMetaClass* cppClass,
- const AbstractMetaFunction* func)
-{
- Indentation indentation(INDENT);
- 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 doc;
- Documentation::Format fmt;
-
- if (mod.format() == TypeSystem::NativeCode)
- fmt = Documentation::Native;
- else if (mod.format() == TypeSystem::TargetLangCode)
- fmt = Documentation::Target;
- else
- continue;
-
- doc.setValue(mod.code() , fmt);
- writeFormattedText(s, doc, cppClass);
- didSomething = true;
- }
- }
- }
-
- s << Qt::endl;
-
- // TODO: 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);
- return didSomething;
-}
-
-QString QtDocGenerator::functionSignature(const AbstractMetaClass* cppClass, const AbstractMetaFunction* func)
-{
- QString className;
- if (!func->isConstructor())
- className = getClassTargetFullName(cppClass) + QLatin1Char('.');
- else if (func->implementingClass() && func->implementingClass()->enclosingClass())
- className = getClassTargetFullName(func->implementingClass()->enclosingClass()) + QLatin1Char('.');
-
- QString funcName = getFuncName(func);
- if (!funcName.startsWith(className))
- funcName = className + funcName;
-
- return funcName + QLatin1Char('(') + parseArgDocStyle(cppClass, func)
- + QLatin1Char(')');
-}
-
-QString QtDocGenerator::translateToPythonType(const AbstractMetaType* type, const AbstractMetaClass* cppClass)
-{
- QString strType;
- const QString name = type->name();
- if (name == QLatin1String("QString")) {
- strType = QLatin1String("unicode");
- } else if (name == QLatin1String("QVariant")) {
- strType = QLatin1String("object");
- } else if (name == QLatin1String("QStringList")) {
- strType = QLatin1String("list of strings");
- } else if (type->isConstant() && name == QLatin1String("char") && type->indirections() == 1) {
- strType = QLatin1String("str");
- } else if (name.startsWith(QLatin1String("unsigned short"))) {
- strType = QLatin1String("int");
- } else if (name.startsWith(QLatin1String("unsigned "))) { // uint and ulong
- strType = QLatin1String("long");
- } else if (type->isContainer()) {
- QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference);
- strType.remove(QLatin1Char('*'));
- strType.remove(QLatin1Char('>'));
- strType.remove(QLatin1Char('<'));
- strType.replace(QLatin1String("::"), QLatin1String("."));
- if (strType.contains(QLatin1String("QList")) || strType.contains(QLatin1String("QVector"))) {
- strType.replace(QLatin1String("QList"), QLatin1String("list of "));
- strType.replace(QLatin1String("QVector"), QLatin1String("list of "));
- } else if (strType.contains(QLatin1String("QHash")) || strType.contains(QLatin1String("QMap"))) {
- strType.remove(QLatin1String("QHash"));
- strType.remove(QLatin1String("QMap"));
- QStringList types = strType.split(QLatin1Char(','));
- strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.")
- .arg(types[0], types[1]);
- }
- } else {
- QString refTag;
- if (type->isEnum())
- refTag = QLatin1String("attr");
- else
- refTag = QLatin1String("class");
- strType = QLatin1Char(':') + refTag + QLatin1String(":`") + name + QLatin1Char('`');
- }
- return strType;
-}
-
-void QtDocGenerator::writeParameterType(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaArgument* arg)
-{
- s << INDENT << ":param " << arg->name() << ": "
- << translateToPythonType(arg->type(), cppClass) << Qt::endl;
-}
-
-void QtDocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass,
- const AbstractMetaFunction *func)
-{
- s << Qt::endl;
- const AbstractMetaArgumentList &funcArgs = func->arguments();
- for (AbstractMetaArgument *arg : funcArgs) {
-
- if (func->argumentRemoved(arg->argumentIndex() + 1))
- continue;
-
- writeParameterType(s, cppClass, arg);
- }
-
- if (!func->isConstructor() && func->type()) {
-
- QString retType;
- // check if the return type was modified
- const FunctionModificationList &mods = func->modifications();
- for (const FunctionModification &mod : mods) {
- for (const ArgumentModification &argMod : mod.argument_mods) {
- if (argMod.index == 0) {
- retType = argMod.modified_type;
- break;
- }
- }
- }
-
- if (retType.isEmpty())
- retType = translateToPythonType(func->type(), cppClass);
- s << INDENT << ":rtype: " << retType << Qt::endl;
- }
- s << Qt::endl;
-}
-
-void QtDocGenerator::writeFunction(QTextStream& s, const AbstractMetaClass* cppClass,
- const AbstractMetaFunction* func)
-{
- s << functionSignature(cppClass, func) << "\n\n";
-
- {
- Indentation indentation(INDENT);
- writeFunctionParametersType(s, cppClass, func);
- const auto version = versionOf(func->typeEntry());
- if (!version.isNull())
- s << INDENT << rstVersionAdded(version);
- if (func->attributes().testFlag(AbstractMetaAttributes::Deprecated))
- s << INDENT << rstDeprecationNote("function");
- }
-
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, cppClass, func);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, cppClass, func))
- writeFormattedText(s, func->documentation(), cppClass);
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, cppClass, func);
-}
-
-static void writeFancyToc(QTextStream& s, const QStringList& items, int cols = 4)
-{
- using TocMap = QMap<QChar, QStringList>;
- TocMap tocMap;
- QChar Q = QLatin1Char('Q');
- QChar idx;
- for (QString item : items) {
- if (item.isEmpty())
- continue;
- if (item.startsWith(Q) && item.length() > 1)
- idx = item[1];
- else
- idx = item[0]; // To group classes without the 'Q' prefix
-
- item.chop(4); // Remove the .rst extension
- tocMap[idx] << item;
- }
- QtXmlToSphinx::Table table;
- QtXmlToSphinx::TableRow row;
-
- int itemsPerCol = (items.size() + tocMap.size()*2) / cols;
- QString currentColData;
- int i = 0;
- QTextStream ss(&currentColData);
- QMutableMapIterator<QChar, QStringList> it(tocMap);
- while (it.hasNext()) {
- it.next();
- std::sort(it.value().begin(), it.value().end());
-
- if (i)
- ss << Qt::endl;
-
- ss << "**" << it.key() << "**\n\n";
- i += 2; // a letter title is equivalent to two entries in space
- for (const QString &item : qAsConst(it.value())) {
- ss << "* :doc:`" << item << "`\n";
- ++i;
-
- // end of column detected!
- if (i > itemsPerCol) {
- ss.flush();
- QtXmlToSphinx::TableCell cell(currentColData);
- row << cell;
- currentColData.clear();
- i = 0;
- }
- }
- }
- if (i) {
- ss.flush();
- QtXmlToSphinx::TableCell cell(currentColData);
- row << cell;
- currentColData.clear();
- i = 0;
- }
- table.appendRow(row);
- table.normalize();
- s << ".. container:: pysidetoc\n\n";
- s << table;
-}
-
-bool QtDocGenerator::finishGeneration()
-{
- if (!classes().isEmpty())
- writeModuleDocumentation();
- if (!m_additionalDocumentationList.isEmpty())
- writeAdditionalDocumentation();
- return true;
-}
-
-void QtDocGenerator::writeModuleDocumentation()
-{
- QMap<QString, QStringList>::iterator it = m_packages.begin();
- for (; it != m_packages.end(); ++it) {
- QString key = it.key();
- key.replace(QLatin1Char('.'), QLatin1Char('/'));
- QString outputDir = outputDirectory() + QLatin1Char('/') + key;
- FileOut output(outputDir + QLatin1String("/index.rst"));
- QTextStream& s = output.stream;
-
- s << ".. module:: " << it.key() << Qt::endl << Qt::endl;
-
- const QString &title = it.key();
- s << title << Qt::endl;
- s << Pad('*', title.length()) << Qt::endl << Qt::endl;
-
- /* Avoid showing "Detailed Description for *every* class in toc tree */
- Indentation indentation(INDENT);
- // 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
- // doesn't include the PySide# prefix in their names.
- const QString moduleName = it.key();
- const int lastIndex = moduleName.lastIndexOf(QLatin1Char('.'));
-
- // Search for extra-sections
- if (!m_extraSectionDir.isEmpty()) {
- QDir extraSectionDir(m_extraSectionDir);
- if (!extraSectionDir.exists())
- qCWarning(lcShibokenDoc) << m_extraSectionDir << "doesn't exist";
-
- QStringList fileList = extraSectionDir.entryList(QStringList() << (moduleName.mid(lastIndex + 1) + QLatin1String("?*.rst")), QDir::Files);
- QStringList::iterator it2 = fileList.begin();
- for (; it2 != fileList.end(); ++it2) {
- QString origFileName(*it2);
- it2->remove(0, moduleName.indexOf(QLatin1Char('.')));
- QString newFilePath = outputDir + QLatin1Char('/') + *it2;
- if (QFile::exists(newFilePath))
- QFile::remove(newFilePath);
- if (!QFile::copy(m_extraSectionDir + QLatin1Char('/') + origFileName, newFilePath)) {
- qCDebug(lcShibokenDoc).noquote().nospace() << "Error copying extra doc "
- << QDir::toNativeSeparators(m_extraSectionDir + QLatin1Char('/') + origFileName)
- << " to " << QDir::toNativeSeparators(newFilePath);
- }
- }
- it.value().append(fileList);
- }
-
- writeFancyToc(s, it.value());
-
- s << INDENT << ".. container:: hide\n\n";
- {
- Indentation indentation(INDENT);
- s << INDENT << ".. toctree::\n";
- Indentation deeperIndentation(INDENT);
- s << INDENT << ":maxdepth: 1\n\n";
- for (const QString &className : qAsConst(it.value()))
- s << INDENT << className << Qt::endl;
- s << Qt::endl << Qt::endl;
- }
-
- s << "Detailed Description\n--------------------\n\n";
-
- // module doc is always wrong and C++istic, so go straight to the extra directory!
- QFile moduleDoc(m_extraSectionDir + QLatin1Char('/') + moduleName.mid(lastIndex + 1) + QLatin1String(".rst"));
- if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) {
- s << moduleDoc.readAll();
- moduleDoc.close();
- } else {
- // try the normal way
- Documentation moduleDoc = m_docParser->retrieveModuleDocumentation(it.key());
- if (moduleDoc.format() == Documentation::Native) {
- QString context = it.key();
- stripPythonQualifiers(&context);
- QtXmlToSphinx x(this, moduleDoc.value(), context);
- s << x;
- } else {
- s << moduleDoc.value();
- }
- }
- }
-}
-
-static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
- const QString &fileName)
-{
- const QString result = QLatin1Char('"') + fileName
- + QLatin1String("\" does not exist in ")
- + QDir::toNativeSeparators(dir) + QLatin1Char('.');
- return result;
-}
-
-void QtDocGenerator::writeAdditionalDocumentation()
-{
- QFile additionalDocumentationFile(m_additionalDocumentationList);
- if (!additionalDocumentationFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- qCWarning(lcShibokenDoc, "%s",
- qPrintable(msgCannotOpenForReading(additionalDocumentationFile)));
- return;
- }
-
- QDir outDir(outputDirectory());
- const QString rstSuffix = fileNameSuffix();
-
- QString errorMessage;
- int successCount = 0;
- int count = 0;
-
- QString targetDir = outDir.absolutePath();
-
- while (!additionalDocumentationFile.atEnd()) {
- const QByteArray lineBA = additionalDocumentationFile.readLine().trimmed();
- if (lineBA.isEmpty() || lineBA.startsWith('#'))
- continue;
- const QString line = QFile::decodeName(lineBA);
- // Parse "[directory]" specification
- if (line.size() > 2 && line.startsWith(QLatin1Char('[')) && line.endsWith(QLatin1Char(']'))) {
- const QString dir = line.mid(1, line.size() - 2);
- if (dir.isEmpty() || dir == QLatin1String(".")) {
- targetDir = outDir.absolutePath();
- } else {
- if (!outDir.exists(dir) && !outDir.mkdir(dir)) {
- qCWarning(lcShibokenDoc, "Cannot create directory %s under %s",
- qPrintable(dir),
- qPrintable(QDir::toNativeSeparators(outputDirectory())));
- break;
- }
- targetDir = outDir.absoluteFilePath(dir);
- }
- } else {
- // Normal file entry
- QFileInfo fi(m_docDataDir + QLatin1Char('/') + line);
- if (fi.isFile()) {
- const QString rstFileName = fi.baseName() + rstSuffix;
- const QString rstFile = targetDir + QLatin1Char('/') + rstFileName;
- const QString context = targetDir.mid(targetDir.lastIndexOf(QLatin1Char('/')) + 1);
- if (QtXmlToSphinx::convertToRst(this, fi.absoluteFilePath(),
- rstFile, context, &errorMessage)) {
- ++successCount;
- qCDebug(lcShibokenDoc).nospace().noquote() << __FUNCTION__
- << " converted " << fi.fileName()
- << ' ' << rstFileName;
- } else {
- qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
- }
- } else {
- qCWarning(lcShibokenDoc, "%s",
- qPrintable(msgNonExistentAdditionalDocFile(m_docDataDir, line)));
- }
- ++count;
- }
- }
- additionalDocumentationFile.close();
-
- qCInfo(lcShibokenDoc, "Created %d/%d additional documentation files.",
- successCount, count);
-}
-
-#ifdef __WIN32__
-# define PATH_SEP ';'
-#else
-# define PATH_SEP ':'
-#endif
-
-bool QtDocGenerator::doSetup()
-{
- if (m_codeSnippetDirs.isEmpty())
- m_codeSnippetDirs = m_libSourceDir.split(QLatin1Char(PATH_SEP));
-
- if (!m_docParser)
- m_docParser = new QtDocParser;
-
- if (m_libSourceDir.isEmpty() || m_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_docDataDir);
- m_docParser->setLibrarySourceDirectory(m_libSourceDir);
- return true;
-}
-
-
-Generator::OptionDescriptions QtDocGenerator::options() const
-{
- return OptionDescriptions()
- << qMakePair(QLatin1String("doc-parser=<parser>"),
- QLatin1String("The documentation parser used to interpret the documentation\n"
- "input files (qdoc|doxygen)"))
- << qMakePair(QLatin1String("documentation-code-snippets-dir=<dir>"),
- QLatin1String("Directory used to search code snippets used by the documentation"))
- << qMakePair(QLatin1String("documentation-data-dir=<dir>"),
- QLatin1String("Directory with XML files generated by documentation tool"))
- << qMakePair(QLatin1String("documentation-extra-sections-dir=<dir>"),
- QLatin1String("Directory used to search for extra documentation sections"))
- << qMakePair(QLatin1String("library-source-dir=<dir>"),
- QLatin1String("Directory where library source code is located"))
- << qMakePair(additionalDocumentationOption() + QLatin1String("=<file>"),
- QLatin1String("List of additional XML files to be converted to .rst files\n"
- "(for example, tutorials)."));
-}
-
-bool QtDocGenerator::handleOption(const QString &key, const QString &value)
-{
- if (key == QLatin1String("library-source-dir")) {
- m_libSourceDir = value;
- return true;
- }
- if (key == QLatin1String("documentation-data-dir")) {
- m_docDataDir = value;
- return true;
- }
- if (key == QLatin1String("documentation-code-snippets-dir")) {
- m_codeSnippetDirs = value.split(QLatin1Char(PATH_SEP));
- return true;
- }
- if (key == QLatin1String("documentation-extra-sections-dir")) {
- m_extraSectionDir = value;
- return true;
- }
- if (key == QLatin1String("doc-parser")) {
- qCDebug(lcShibokenDoc).noquote().nospace() << "doc-parser: " << value;
- if (value == QLatin1String("doxygen"))
- m_docParser = new DoxygenParser;
- return true;
- }
- if (key == additionalDocumentationOption()) {
- m_additionalDocumentationList = value;
- return true;
- }
- return false;
-}