diff --git a/src/tools/qdoc/atom.cpp b/src/tools/qdoc/atom.cpp index 8816ea5d6f..5c699b0546 100644 --- a/src/tools/qdoc/atom.cpp +++ b/src/tools/qdoc/atom.cpp @@ -374,22 +374,51 @@ void Atom::dump() const contains some search parameters. */ LinkAtom::LinkAtom(const QString& p1, const QString& p2) - : Atom(Link, p1), qml_(false), goal_(Node::NoType), domain_(0) + : Atom(p1), genus_(DontCare), goal_(Node::NoType), domain_(0) { QStringList params = p2.toLower().split(QLatin1Char(' ')); foreach (const QString& p, params) { - if (p == "qml") - qml_ = true; - else { - if (!domain_) { - domain_ = QDocDatabase::qdocDB()->findTree(p); - if (domain_) - continue; - } - if (goal_ == Node::NoType) - goal_ = Node::goal(p); + if (!domain_) { + domain_ = QDocDatabase::qdocDB()->findTree(p); + if (domain_) + continue; } + if (goal_ == Node::NoType) { + goal_ = Node::goal(p); + if (goal_ != Node::NoType) + continue; + } + if (p == "qml") + genus_ = QML; + else if (p == "cpp") + genus_ = CPP; } } +/*! + Standard copy constructor of LinkAtom \a t. + */ +LinkAtom::LinkAtom(const LinkAtom& t) + : Atom(Link, t.string()), + genus_(t.genus_), + goal_(t.goal_), + domain_(t.domain_) +{ + // nothing +} + +/*! + Special copy constructor of LinkAtom \a t, where + where the new LinkAtom will not be the first one + in the list. + */ +LinkAtom::LinkAtom(Atom* previous, const LinkAtom& t) + : Atom(previous, Link, t.string()), + genus_(t.genus_), + goal_(t.goal_), + domain_(t.domain_) +{ + previous->next_ = this; +} + QT_END_NAMESPACE diff --git a/src/tools/qdoc/atom.h b/src/tools/qdoc/atom.h index 999919482b..36a7390ae2 100644 --- a/src/tools/qdoc/atom.h +++ b/src/tools/qdoc/atom.h @@ -44,10 +44,12 @@ #include #include "node.h" +#include QT_BEGIN_NAMESPACE class Tree; +class LinkAtom; class Atom { @@ -63,7 +65,7 @@ public: BriefRight, C, CaptionLeft, - CaptionRight, + CaptionRight, // 10 Code, CodeBad, CodeNew, @@ -73,7 +75,7 @@ public: DivLeft, DivRight, EndQmlText, - FootnoteLeft, + FootnoteLeft, // 20 FootnoteRight, FormatElse, FormatEndif, @@ -83,7 +85,7 @@ public: GeneratedList, GuidLink, HR, - Image, + Image, // 30 ImageText, ImportantLeft, ImportantRight, @@ -93,7 +95,7 @@ public: LegaleseLeft, LegaleseRight, LineBreak, - Link, + Link, // 40 LinkNode, ListLeft, ListItemNumber, @@ -103,7 +105,7 @@ public: ListItemRight, ListRight, Nop, - NoteLeft, + NoteLeft, // 50 NoteRight, ParaLeft, ParaRight, @@ -113,7 +115,7 @@ public: QuotationRight, RawString, SectionLeft, - SectionRight, + SectionRight, // 60 SectionHeadingLeft, SectionHeadingRight, SidebarLeft, @@ -123,7 +125,7 @@ public: SnippetIdentifier, SnippetLocation, String, - TableLeft, + TableLeft, // 70 TableRight, TableHeaderLeft, TableHeaderRight, @@ -133,11 +135,21 @@ public: TableItemRight, TableOfContents, Target, - UnhandledFormat, + UnhandledFormat, // 80 UnknownCommand, Last = UnknownCommand }; + enum NodeGenus { DontCare, CPP, QML }; + + friend class LinkAtom; + + Atom(const QString& string) + : next_(0), type_(Link) + { + strs << string; + } + Atom(Type type, const QString& string = "") : next_(0), type_(type) { @@ -186,8 +198,10 @@ public: const QString& string(int i) const { return strs[i]; } int count() const { return strs.size(); } void dump() const; + const QStringList& strings() const { return strs; } - virtual bool qml() const { return false; } + virtual bool isLinkAtom() const { return false; } + virtual NodeGenus genus() const { return DontCare; } virtual bool specifiesDomain() const { return false; } virtual Tree* domain() const { return 0; } virtual Node::Type goal() const { return Node::NoType; } @@ -202,15 +216,18 @@ class LinkAtom : public Atom { public: LinkAtom(const QString& p1, const QString& p2); + LinkAtom(const LinkAtom& t); + LinkAtom(Atom* previous, const LinkAtom& t); virtual ~LinkAtom() { } - virtual bool qml() const { return qml_; } + virtual bool isLinkAtom() const { return true; } + virtual NodeGenus genus() const { return genus_; } virtual bool specifiesDomain() const { return (domain_ != 0); } virtual Tree* domain() const { return domain_; } virtual Node::Type goal() const { return goal_; } protected: - bool qml_; + NodeGenus genus_; Node::Type goal_; Tree* domain_; }; diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index bbd32204ce..c2a5cdb8b8 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -765,7 +765,7 @@ int DitaXmlGenerator::generateAtom(const Atom *atom, case Atom::AutoLink: if (!noLinks && !inLink_ && !inContents_ && !inSectionHeading_) { const Node* node = 0; - QString link = getLink(atom, relative, &node); + QString link = getAutoLink(atom, relative, &node); if (!link.isEmpty()) { beginLink(link); generateLink(atom, marker); @@ -1308,13 +1308,11 @@ int DitaXmlGenerator::generateAtom(const Atom *atom, case Atom::Link: { const Node *node = 0; - QString myLink = getLink(atom, relative, &node); - //if (myLink.isEmpty()) - //myLink = getCollisionLink(atom); - if (myLink.isEmpty() && !noLinkErrors()) + QString link = getLink(atom, relative, &node); + if (link.isEmpty() && !noLinkErrors()) relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string())); else if (!inSectionHeading_) - beginLink(myLink); + beginLink(link); skipAhead = 1; } break; @@ -3722,6 +3720,129 @@ QString DitaXmlGenerator::fileName(const Node* node) return Generator::fileName(node); } +/*! + This function is called for links, i.e. for words that + are marked with the qdoc link command. For autolinks + that are not marked with the qdoc link command, qdoc + calls getAutoLink(). + + Return the link represented by the \a atom, and set \a node + to point to the target node for that link. \a relative points + to the node holding the qdoc comment where the link command + was found. + */ +QString DitaXmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node) +{ + if (atom->string().contains(QLatin1Char(':')) && (atom->string().startsWith("file:") || + atom->string().startsWith("http:") || + atom->string().startsWith("https:") || + atom->string().startsWith("ftp:") || + atom->string().startsWith("mailto:"))) { + return atom->string(); // It's some kind of protocol. + } + + QString ref; + QString link; + QStringList path = atom->string().split("#"); + QString first = path.first().trimmed(); + + *node = 0; + if (first.isEmpty()) + *node = relative; // search for a target on the current page. + else { + if (first.endsWith(".html")) { // The target is an html file. + *node = qdb_->findNodeByNameAndType(QStringList(first), Node::Document); + } + else if (first.endsWith("()")) { // The target is a C++ function or QML method. + *node = qdb_->resolveFunctionTarget(first, relative); + } + else { + *node = qdb_->resolveTarget(first, relative); + if (!(*node)) + *node = qdb_->findDocNodeByTitle(first); + if (!(*node)) { + *node = qdb_->findUnambiguousTarget(first, ref); + if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { + QString final = (*node)->url() + "#" + ref; + return final; + } + } + } + } + if (!(*node)) + return link; // empty + + if (!(*node)->url().isEmpty()) + return (*node)->url(); + + if (!path.isEmpty()) { + ref = qdb_->findTarget(path.first(), *node); + if (ref.isEmpty()) + return link; // empty + } + + /* + Given that *node is not null, we now cconstruct a link + to the page that *node represents, and then if we found + a target on that page, we connect the target to the link + with '#'. + */ + link = linkForNode(*node, relative); + if (*node && (*node)->subType() == Node::Image) + link = "images/used-in-examples/" + link; + if (!ref.isEmpty()) + link += QLatin1Char('#') + ref; + return link; +} + +/*! + This function is called for autolinks, i.e. for words that + are not marked with the qdoc link command that qdoc has + reason to believe should be links. For links marked with + the qdoc link command, qdoc calls getLink(). + + Return the link represented by the \a atom, and set \a node + to point to the target node for that link. \a relative points + to the node holding the qdoc comment where the link command + was found. + */ +QString DitaXmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node** node) +{ + QString ref; + QString link; + QString target = atom->string().trimmed(); + *node = 0; + + if (target.endsWith("()")) { // The target is a C++ function or QML method. + *node = qdb_->resolveFunctionTarget(target, relative); + } + else { + *node = qdb_->resolveTarget(target, relative); + if (!(*node)) { + *node = qdb_->findDocNodeByTitle(target); + } + if (!(*node)) { + *node = qdb_->findUnambiguousTarget(target, ref); + if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { + QString final = (*node)->url() + "#" + ref; + return final; + } + } + } + + if (!(*node)) + return link; // empty + + if (!(*node)->url().isEmpty()) + return (*node)->url(); + + link = linkForNode(*node, relative); + if (!ref.isEmpty()) + link += QLatin1Char('#') + ref; + return link; +} + + QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative) { if (node == 0 || node == relative) @@ -3832,103 +3953,6 @@ const QPair DitaXmlGenerator::anchorForNode(const Node* node) return anchorPair; } -QString DitaXmlGenerator::getLink(const Atom* atom, const Node* relative, const Node** node) -{ - QString link; - *node = 0; - inObsoleteLink = false; - - if (atom->string().contains(QLatin1Char(':')) && - (atom->string().startsWith("file:") - || atom->string().startsWith("http:") - || atom->string().startsWith("https:") - || atom->string().startsWith("ftp:") - || atom->string().startsWith("mailto:"))) { - - link = atom->string(); - } - else { - QStringList path; - if (atom->string().contains('#')) - path = atom->string().split('#'); - else - path.append(atom->string()); - - QString ref; - QString first = path.first().trimmed(); - - if (first.isEmpty()) - *node = relative; - else if (first.endsWith(".html")) - *node = qdb_->findNodeByNameAndType(QStringList(first), Node::Document); - else if (first.endsWith("()")) // The target is a C++ function or QML method. - *node = qdb_->resolveFunctionTarget(first, relative); - else { - *node = qdb_->resolveTarget(first, relative); - if (!(*node)) - *node = qdb_->findDocNodeByTitle(first); - if (!*node) - *node = qdb_->findUnambiguousTarget(first, ref); - } - - if (*node) { - if (!(*node)->url().isEmpty()) - return (*node)->url(); - else - path.removeFirst(); - } - else - *node = relative; - - if (*node && (*node)->status() == Node::Obsolete) { - if (relative && (relative->parent() != *node) && - (relative->status() != Node::Obsolete)) { - bool porting = false; - if (relative->isDocNode()) { - const DocNode* fake = static_cast(relative); - if (fake->title().startsWith("Porting")) - porting = true; - } - QString name = relative->plainFullName(); - if (!porting && !name.startsWith("Q3")) { - if (obsoleteLinks) { - relative->doc().location().warning(tr("Link to obsolete item '%1' in %2") - .arg(atom->string()) - .arg(name)); - } - inObsoleteLink = true; - } - } - } - - while (!path.isEmpty()) { - ref = qdb_->findTarget(path.first(), *node); - if (ref.isEmpty()) - break; - path.removeFirst(); - } - - if (path.isEmpty()) { - link = linkForNode(*node, relative); - if (*node && (*node)->subType() == Node::Image) - link = "images/used-in-examples/" + link; - if (!ref.isEmpty()) { - if (link.isEmpty()) - link = outFileName(); - QString guid = lookupGuid(link, ref); - link += QLatin1Char('#') + guid; - } - else if (!link.isEmpty() && *node && (link.endsWith(".xml") || link.endsWith(".dita"))) { - link += QLatin1Char('#') + (*node)->guid(); - } - } - } - if (!link.isEmpty() && link[0] == '#') { - link.prepend(outFileName()); - } - return link; -} - void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker) { Text text; diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index 0239151402..d4264d948c 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -425,12 +425,14 @@ private: void generateLink(const Atom* atom, CodeMarker* marker); void generateStatus(const Node* node, CodeMarker* marker); + QString getLink(const Atom *atom, const Node *relative, const Node** node); + QString getAutoLink(const Atom *atom, const Node *relative, const Node** node); + QString registerRef(const QString& ref); virtual QString fileBase(const Node *node) const; QString fileName(const Node *node); static int hOffset(const Node *node); static bool isThreeColumnEnumValueTable(const Atom *atom); - QString getLink(const Atom *atom, const Node *relative, const Node **node); #ifdef GENERATE_MAC_REFS void generateMacRef(const Node* node, CodeMarker* marker); #endif diff --git a/src/tools/qdoc/doc.cpp b/src/tools/qdoc/doc.cpp index 4fe0972d72..5745b094d5 100644 --- a/src/tools/qdoc/doc.cpp +++ b/src/tools/qdoc/doc.cpp @@ -475,6 +475,7 @@ private: void startSection(Doc::Sections unit, int cmd); void endSection(int unit, int endCmd); void parseAlso(); + void append(const QString &string); void append(Atom::Type type, const QString& string = QString()); void append(Atom::Type type, const QString& p1, const QString& p2); void append(const QString& p1, const QString& p2); @@ -968,8 +969,9 @@ void DocParser::parse(const QString& source, break; case CMD_L: enterPara(); - if (isLeftBracketAhead()) + if (isLeftBracketAhead()) { p2 = getBracketedArgument(); + } if (isLeftBraceAhead()) { p1 = getArgument(); append(p1, p2); @@ -990,6 +992,7 @@ void DocParser::parse(const QString& source, append(Atom::String, cleanLink(p1)); append(Atom::FormattingRight, ATOM_FORMATTING_LINK); } + p2.clear(); break; case CMD_LEGALESE: leavePara(); @@ -1001,7 +1004,7 @@ void DocParser::parse(const QString& source, if (openCommand(cmd)) { enterPara(); p1 = getArgument(); - append(Atom::Link, p1); + append(p1); append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); skipSpacesOrOneEndl(); } @@ -1981,6 +1984,14 @@ void DocParser::append(Atom::Type type, const QString &string) priv->text << Atom(type, string); } +void DocParser::append(const QString &string) +{ + Atom::Type lastType = priv->text.lastAtom()->type(); + if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) + priv->text.lastAtom()->chopString(); + priv->text << Atom(string); +} + void DocParser::append(Atom::Type type, const QString& p1, const QString& p2) { Atom::Type lastType = priv->text.lastAtom()->type(); @@ -1995,7 +2006,7 @@ void DocParser::append(const QString& p1, const QString& p2) if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) priv->text.lastAtom()->chopString(); if (p2.isEmpty()) - priv->text << Atom(Atom::Link, p1, p2); + priv->text << Atom(p1); else priv->text << LinkAtom(p1, p2); } diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index b27968982a..f5e2e6309e 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -3648,6 +3648,112 @@ QString HtmlGenerator::refForNode(const Node *node) #define DEBUG_ABSTRACT 0 +/*! + This function is called for links, i.e. for words that + are marked with the qdoc link command. For autolinks + that are not marked with the qdoc link command, the + getAutoLink() function is called + + It returns the string for a link found by using the data + in the \a atom to search the database. It also sets \a node + to point to the target node for that link. \a relative points + to the node holding the qdoc comment where the link command + was found. + */ +QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node) +{ + const QString& t = atom->string(); + if (t.at(0) == QChar('h')) { + if (t.startsWith("http:") || t.startsWith("https:")) + return t; + } + else if (t.at(0) == QChar('f')) { + if (t.startsWith("file:") || t.startsWith("ftp:")) + return t; + } + else if (t.at(0) == QChar('m')) { + if (t.startsWith("mailto:")) + return t; + } + + QString ref; + + *node = qdb_->findNode(atom, relative, ref); + if (!(*node)) + return QString(); + + QString url = (*node)->url(); + if (!url.isEmpty()) { + if (ref.isEmpty()) + return url; + int hashtag = url.lastIndexOf(QChar('#')); + if (hashtag != -1) + url.truncate(hashtag); + return url + "#" + ref; + } + /* + Given that *node is not null, we now cconstruct a link + to the page that *node represents, and then if we found + a target on that page, we connect the target to the link + with '#'. + */ + QString link = linkForNode(*node, relative); + if (*node && (*node)->subType() == Node::Image) + link = "images/used-in-examples/" + link; + if (!ref.isEmpty()) + link += QLatin1Char('#') + ref; + return link; +} + +/*! + This function is called for autolinks, i.e. for words that + are not marked with the qdoc link command that qdoc has + reason to believe should be links. For links marked with + the qdoc link command, the getLink() function is called. + + It returns the string for a link found by using the data + in the \a atom to search the database. It also sets \a node + to point to the target node for that link. \a relative points + to the node holding the qdoc comment where the link command + was found. + */ +QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node** node) +{ + QString ref; + QString link; + QString target = atom->string().trimmed(); + *node = 0; + + if (target.endsWith("()")) { // The target is a C++ function or QML method. + *node = qdb_->resolveFunctionTarget(target, relative); + } + else { + *node = qdb_->resolveTarget(target, relative); + if (!(*node)) { + *node = qdb_->findDocNodeByTitle(target); + } + if (!(*node)) { + *node = qdb_->findUnambiguousTarget(target, ref); + if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { + QString final = (*node)->url() + "#" + ref; + return final; + } + } + } + + if (!(*node)) + return link; // empty + + if (!(*node)->url().isEmpty()) + return (*node)->url(); + + link = linkForNode(*node, relative); + if (!ref.isEmpty()) + link += QLatin1Char('#') + ref; + return link; +} + + /*! Construct the link string for the \a node and return it. The \a relative node is use to decide the link we are @@ -3847,137 +3953,6 @@ const QPair HtmlGenerator::anchorForNode(const Node *node) return anchorPair; } -/*! - This function is called for links, i.e. for words that - are marked with the qdoc link command. For autolinks - that are not marked with the qdoc link command, qdoc - calls getAutoLink(). - - Return the link represented by the \a atom, and set \a node - to point to the target node for that link. \a relative points - to the node holding the qdoc comment where the link command - was found. - */ -QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node) -{ - if (atom->string().contains(QLatin1Char(':')) && (atom->string().startsWith("file:") || - atom->string().startsWith("http:") || - atom->string().startsWith("https:") || - atom->string().startsWith("ftp:") || - atom->string().startsWith("mailto:"))) { - return atom->string(); // It's some kind of protocol. - } - - QString ref; - QString link; - QString first; - QStringList path; - - *node = 0; - if (atom->string().contains('#')) { - path = atom->string().split('#'); - first = path.first().trimmed(); - path.removeFirst(); - } - else - first = atom->string(); - - if (first.isEmpty()) - *node = relative; // search for a target on the current page. - else { - if (first.endsWith(".html")) { // The target is an html file. - *node = qdb_->findNodeByNameAndType(QStringList(first), Node::Document); - //Node* n = qdb_->findHtmlFileNode(atom); - } - else if (first.endsWith("()")) { // The target is a C++ function or QML method. - *node = qdb_->resolveFunctionTarget(first, relative); - } - else { - *node = qdb_->resolveTarget(first, relative); - if (!(*node)) - *node = qdb_->findDocNodeByTitle(first); - if (!(*node)) { - *node = qdb_->findUnambiguousTarget(first, ref); - if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { - QString final = (*node)->url() + "#" + ref; - return final; - } - } - } - } - if (!(*node)) - return link; // empty - - if (!(*node)->url().isEmpty()) - return (*node)->url(); - - if (!path.isEmpty()) { - ref = qdb_->findTarget(path.first(), *node); - if (ref.isEmpty()) - return link; // empty - } - - /* - Given that *node is not null, we now cconstruct a link - to the page that *node represents, and then if we found - a target on that page, we connect the target to the link - with '#'. - */ - link = linkForNode(*node, relative); - if (*node && (*node)->subType() == Node::Image) - link = "images/used-in-examples/" + link; - if (!ref.isEmpty()) - link += QLatin1Char('#') + ref; - return link; -} - -/*! - This function is called for autolinks, i.e. for words that - are not marked with the qdoc link command that qdoc has - reason to believe should be links. For links marked with - the qdoc link command, qdoc calls getLink(). - - Return the link represented by the \a atom, and set \a node - to point to the target node for that link. \a relative points - to the node holding the qdoc comment where the link command - was found. - */ -QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const Node** node) -{ - QString ref; - QString link; - QString path = atom->string().trimmed(); - *node = 0; - - if (path.endsWith("()")) { // The target is a C++ function or QML method. - *node = qdb_->resolveFunctionTarget(path, relative); - } - else { - *node = qdb_->resolveTarget(path, relative); - if (!(*node)) { - *node = qdb_->findDocNodeByTitle(path); - } - if (!(*node)) { - *node = qdb_->findUnambiguousTarget(path, ref); - if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { - QString final = (*node)->url() + "#" + ref; - return final; - } - } - } - - if (!(*node)) - return link; // empty - - if (!(*node)->url().isEmpty()) - return (*node)->url(); - - link = linkForNode(*node, relative); - if (!ref.isEmpty()) - link += QLatin1Char('#') + ref; - return link; -} - void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker) { Text text; diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 787b34daf2..ef569b38ce 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -211,13 +211,14 @@ private: void generateLink(const Atom *atom, CodeMarker *marker); void generateStatus(const Node *node, CodeMarker *marker); + QString getLink(const Atom *atom, const Node *relative, const Node** node); + QString getAutoLink(const Atom *atom, const Node *relative, const Node** node); + QString registerRef(const QString& ref); virtual QString fileBase(const Node *node) const; QString fileName(const Node *node); static int hOffset(const Node *node); static bool isThreeColumnEnumValueTable(const Atom *atom); - QString getLink(const Atom *atom, const Node *relative, const Node** node); - QString getAutoLink(const Atom *atom, const Node *relative, const Node** node); #ifdef GENERATE_MAC_REFS void generateMacRef(const Node *node, CodeMarker *marker); #endif diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp index 21ca2c4820..aba9956604 100644 --- a/src/tools/qdoc/main.cpp +++ b/src/tools/qdoc/main.cpp @@ -292,7 +292,7 @@ static void processQdocconfFile(const QString &fileName) Location::initialize(config); config.load(fileName); QString project = config.getString(CONFIG_PROJECT).toLower(); - //qDebug() << "\nSTART PROJECT:" << project; + //qDebug() << "\nStart project:" << project; /* Add the defines to the configuration variables. */ diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index fd610dedb6..03b798cbb3 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -686,6 +686,14 @@ bool Node::isInternal() const return false; } +/*! + Returns a pointer to the Tree this node is in. + */ +Tree* Node::tree() const +{ + return (parent() ? parent()->tree() : 0); +} + /*! Returns a pointer to the root of the Tree this node is in. */ diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index ebdaad9010..4f9097d774 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -248,7 +248,7 @@ public: virtual bool wasSeen() const { return false; } virtual void appendGroupName(const QString& ) { } virtual QString element() const { return QString(); } - virtual Tree* tree() const { return 0; } + virtual Tree* tree() const; bool isIndexNode() const { return indexNodeFlag_; } Type type() const { return nodeType_; } virtual SubType subType() const { return NoSubType; } @@ -440,7 +440,7 @@ public: NamespaceNode(InnerNode* parent, const QString& name); virtual ~NamespaceNode() { } virtual bool isNamespace() const { return true; } - virtual Tree* tree() const { return tree_; } + virtual Tree* tree() const { return (parent() ? parent()->tree() : tree_); } void setTree(Tree* t) { tree_ = t; } private: diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index 8bf83ca796..8f28e38431 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -384,7 +384,7 @@ void QDocForest::newPrimaryTree(const QString& module) point, but it only makes sense in the primary tree, which is searched first. After the primary tree is searched, \a relative is set to 0 for searching the index trees. When relative is 0, - the root node of the index tree is the starting point. + the root nodes of the index trees are the starting points. */ const Node* QDocForest::resolveTarget(const QString& target, const Node* relative) { @@ -395,11 +395,6 @@ const Node* QDocForest::resolveTarget(const QString& target, const Node* relativ const Node* n = t->findNode(path, relative, flags); if (n) return n; -#if 0 - n = t->findDocNodeByTitle(target); - if (n) - return n; -#endif relative = 0; } return 0; @@ -1567,67 +1562,86 @@ void QDocDatabase::mergeCollections(CollectionNode* cn) } } + +/*! + This function is called when the \a{atom} might be a link + atom. It handles the optional, square bracket parameters + for the link command. + */ +Node* QDocDatabase::findNode(const Atom* atom) +{ + QStringList path(atom->string()); + if (atom->specifiesDomain()) { + return atom->domain()->findNodeByNameAndType(path, atom->goal()); + } + qDebug() << "FINDNODE:" << path << atom->goal(); + return forest_.findNodeByNameAndType(path, atom->goal()); +} + +const DocNode* QDocDatabase::findDocNodeByTitle(const Atom* atom) +{ + return forest_.findDocNodeByTitle(atom->string()); +} + +/*! + Searches for the node that matches the path in \a atom. The + \a relative node is used if the first leg of the path is + empty, i.e. if the path begins with a hashtag. The function + also sets \a ref if there remains an unused leg in the path + after the node is found. The node is returned as well as the + \a ref. If the returned node pointer is null, \a ref is not + valid. + */ +const Node* QDocDatabase::findNode(const Atom* atom, const Node* relative, QString& ref) +{ + const Node* node = 0; + QStringList path = atom->string().split("#"); + QString first = path.first().trimmed(); + path.removeFirst(); + + if (first.isEmpty()) + node = relative; // search for a target on the current page. + else if (atom->specifiesDomain()) { + qDebug() << "Processing LinkAtom"; + if (first.endsWith(".html")) { // The target is an html file. + node = atom->domain()->findNodeByNameAndType(QStringList(first), Node::Document); + } + else if (first.endsWith("()")) { // The target is a C++ function or QML method. + node = atom->domain()->resolveFunctionTarget(first, 0); //relative); + } + else { + node = atom->domain()->resolveTarget(first, 0); // relative); + if (!node) + node = atom->domain()->findUnambiguousTarget(first, ref); // ref + if (!node && path.isEmpty()) + node = atom->domain()->findDocNodeByTitle(first); + } + } + else { + if (first.endsWith(".html")) { // The target is an html file. + node = findNodeByNameAndType(QStringList(first), Node::Document); // ref + } + else if (first.endsWith("()")) { // The target is a C++ function or QML method. + node = resolveFunctionTarget(first, relative); + } + else { + node = resolveTarget(first, relative); // ref + if (!node) + node = findUnambiguousTarget(first, ref); // ref + if (!node && path.isEmpty()) + node = findDocNodeByTitle(first); + } + } + if (node && ref.isEmpty()) { + if (!node->url().isEmpty()) + return node; + if (!path.isEmpty()) { + ref = findTarget(path.first(), node); + if (ref.isEmpty()) + node = 0; + } + } + return node; +} + QT_END_NAMESPACE - -#if 0 - void getAllGroups(CNMM& t); - void getAllModules(CNMM& t); - void getAllQmlModules(CNMM& t); - -/*! - For each tree in the forest, get the group map from the tree. - Insert each pair from the group map into the collection node - multimap \a t. - */ -void QDocForest::getAllGroups(CNMM& t) -{ - foreach (Tree* t, searchOrder()) { - const GroupMap& gm = t->groups(); - if (!gm.isEmpty()) { - GroupMap::const_iterator i = gm.begin(); - while (i != gm.end()) { - t.insert(i.key(), i.value()); - ++i; - } - } - } -} - -/*! - For each tree in the forest, get the module map from the tree. - Insert each pair from the module map into the collection node - multimap \a t. - */ -void QDocForest::getAllModules(CNMM& t) -{ - foreach (Tree* t, searchOrder()) { - const ModuleMap& mm = t->modules(); - if (!mm.isEmpty()) { - ModuleMap::const_iterator i = mm.begin(); - while (i != mm.end()) { - t.insert(i.key(), i.value()); - ++i; - } - } - } -} - -/*! - For each tree in the forest, get the QML module map from the - tree. Insert each pair from the QML module map into the - collection node multimap \a t. - */ -void QDocForest::getAllQmlModules(CNMM& t) -{ - foreach (Tree* t, searchOrder()) { - const QmlModuleMap& qmm = t->groups(); - if (!qmm.isEmpty()) { - QmlModuleMap::const_iterator i = qmm.begin(); - while (i != qmm.end()) { - t.insert(i.key(), i.value()); - ++i; - } - } - } -} -#endif diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 8741443a7c..7c398a9d51 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -249,7 +249,9 @@ class QDocDatabase void findAllSince(InnerNode *node); public: - // special collection access functions + /******************************************************************* + special collection access functions + ********************************************************************/ NodeMap& getCppClasses(); NodeMap& getMainClasses(); NodeMap& getCompatibilityClasses(); @@ -266,9 +268,9 @@ class QDocDatabase const NodeMap& getQmlTypeMap(const QString& key); const NodeMultiMap& getSinceMap(const QString& key); - /* convenience functions - Many of these will be either eliminated or replaced. - */ + /******************************************************************* + Many of these will be either eliminated or replaced. + ********************************************************************/ void resolveInheritance() { primaryTree()->resolveInheritance(); } void resolveQmlInheritance(InnerNode* root); void resolveIssues(); @@ -298,6 +300,14 @@ class QDocDatabase } /*******************************************************************/ + /******************************************************************* + The functions declared below handle the parameters in '[' ']'. + ********************************************************************/ + Node* findNode(const Atom* atom); + const Node* findNode(const Atom* atom, const Node* relative, QString& ref); + const DocNode* findDocNodeByTitle(const Atom* atom); + /*******************************************************************/ + /******************************************************************* The functions declared below are called for all trees. ********************************************************************/ diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp index 25f6b8eec9..317209dd64 100644 --- a/src/tools/qdoc/qdocindexfiles.cpp +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -54,6 +54,8 @@ QT_BEGIN_NAMESPACE +static Node* top = 0; + /*! \class QDocIndexFiles @@ -110,11 +112,13 @@ void QDocIndexFiles::readIndexes(const QStringList& indexFiles) foreach (const QString& indexFile, indexFiles) { QString msg = "Loading index file: " + indexFile; Location::logToStdErr(msg); - //qDebug() << " LOAD INDEX FILE:" << indexFile; + //qDebug() << msg; readIndexFile(indexFile); } } +static bool readingRoot = true; + /*! Reads and parses the index file at \a path. */ @@ -146,6 +150,7 @@ void QDocIndexFiles::readIndexFile(const QString& path) basesList_.clear(); relatedList_.clear(); + readingRoot = true; NamespaceNode* root = qdb_->newIndexTree(project_); // Scan all elements in the XML file, constructing a map that contains @@ -154,6 +159,7 @@ void QDocIndexFiles::readIndexFile(const QString& path) while (!child.isNull()) { readIndexSection(child, root, indexUrl); child = child.nextSiblingElement(); + readingRoot = true; } // Now that all the base classes have been found for this index, @@ -167,13 +173,16 @@ void QDocIndexFiles::readIndexFile(const QString& path) appropriate node(s). */ void QDocIndexFiles::readIndexSection(const QDomElement& element, - InnerNode* parent, + Node* current, const QString& indexUrl) { QString name = element.attribute("name"); QString href = element.attribute("href"); Node* node; Location location; + InnerNode* parent = 0; + if (current->isInnerNode()) + parent = static_cast(current); QString filePath; int lineNo = 0; @@ -462,15 +471,15 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, location = Location(parent->name().toLower() + ".html"); } else if (element.nodeName() == "keyword") { - qdb_->insertTarget(name, TargetRec::Keyword, parent, 1); + qdb_->insertTarget(name, TargetRec::Keyword, current, 1); return; } else if (element.nodeName() == "target") { - qdb_->insertTarget(name, TargetRec::Target, parent, 2); + qdb_->insertTarget(name, TargetRec::Target, current, 2); return; } else if (element.nodeName() == "contents") { - qdb_->insertTarget(name, TargetRec::Contents, parent, 3); + qdb_->insertTarget(name, TargetRec::Contents, current, 3); return; } else @@ -560,26 +569,15 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, node->setReconstitutedBrief(briefAttr); } - if (node->isInnerNode()) { - InnerNode* inner = static_cast(node); + // zzz + bool useParent = (element.nodeName() == "namespace" && name.isEmpty()); + if (element.hasChildNodes()) { QDomElement child = element.firstChildElement(); while (!child.isNull()) { - if (element.nodeName() == "class") { - readIndexSection(child, inner, indexUrl); - } - else if (element.nodeName() == "qmlclass") { - readIndexSection(child, inner, indexUrl); - } - else if (element.nodeName() == "page") { - readIndexSection(child, inner, indexUrl); - } - else if (element.nodeName() == "namespace" && !name.isEmpty()) { - // The root node in the index is a namespace with an empty name. - readIndexSection(child, inner, indexUrl); - } - else { + if (useParent) readIndexSection(child, parent, indexUrl); - } + else + readIndexSection(child, node, indexUrl); child = child.nextSiblingElement(); } } @@ -1143,6 +1141,42 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, break; } + /* + For our pages, we canonicalize the target, keyword and content + item names so that they can be used by qdoc for other sets of + documentation. + + The reason we do this here is that we don't want to ruin + externally composed indexes, containing non-qdoc-style target names + when reading in indexes. + + targets and keywords are now allowed in any node, not just inner nodes. + */ + + if (node->doc().hasTargets()) { + bool external = false; + if (node->type() == Node::Document) { + const DocNode* docNode = static_cast(node); + if (docNode->subType() == Node::ExternalPage) + external = true; + } + foreach (const Atom* target, node->doc().targets()) { + QString targetName = target->string(); + if (!external) + targetName = Doc::canonicalTitle(targetName); + writer.writeStartElement("target"); + writer.writeAttribute("name", targetName); + writer.writeEndElement(); // target + } + } + if (node->doc().hasKeywords()) { + foreach (const Atom* keyword, node->doc().keywords()) { + writer.writeStartElement("keyword"); + writer.writeAttribute("name", Doc::canonicalTitle(keyword->string())); + writer.writeEndElement(); // keyword + } + } + // Inner nodes and function nodes contain child nodes of some sort, either // actual child nodes or function parameters. For these, we close the // opening tag, create child elements, then add a closing tag for the @@ -1151,36 +1185,6 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, if (node->isInnerNode()) { const InnerNode* inner = static_cast(node); - // For internal pages, we canonicalize the target, keyword and content - // item names so that they can be used by qdoc for other sets of - // documentation. - // The reason we do this here is that we don't want to ruin - // externally composed indexes, containing non-qdoc-style target names - // when reading in indexes. - - if (inner->doc().hasTargets()) { - bool external = false; - if (inner->type() == Node::Document) { - const DocNode* docNode = static_cast(inner); - if (docNode->subType() == Node::ExternalPage) - external = true; - } - foreach (const Atom* target, inner->doc().targets()) { - QString targetName = target->string(); - if (!external) - targetName = Doc::canonicalTitle(targetName); - writer.writeStartElement("target"); - writer.writeAttribute("name", targetName); - writer.writeEndElement(); // target - } - } - if (inner->doc().hasKeywords()) { - foreach (const Atom* keyword, inner->doc().keywords()) { - writer.writeStartElement("keyword"); - writer.writeAttribute("name", Doc::canonicalTitle(keyword->string())); - writer.writeEndElement(); // keyword - } - } if (inner->doc().hasTableOfContents()) { for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) { Atom* item = inner->doc().tableOfContents()[i]; @@ -1336,6 +1340,47 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer, generateIndexSections(writer, child, generateInternalNodes); } } + + if (node == top) { + /* + We wait until the end of the index file to output the group, module, + and QML module elements. By outputting them at the end, when we read + the index file back in, all the group, module, and QML module member + elements will have already been created. It is then only necessary to + create the group, module, or QML module element and add each member to + its member list. + */ + const CNMap& groups = qdb_->groups(); + if (!groups.isEmpty()) { + CNMap::ConstIterator g = groups.constBegin(); + while (g != groups.constEnd()) { + if (generateIndexSection(writer, g.value(), generateInternalNodes)) + writer.writeEndElement(); + ++g; + } + } + + const CNMap& modules = qdb_->modules(); + if (!modules.isEmpty()) { + CNMap::ConstIterator g = modules.constBegin(); + while (g != modules.constEnd()) { + if (generateIndexSection(writer, g.value(), generateInternalNodes)) + writer.writeEndElement(); + ++g; + } + } + + const CNMap& qmlModules = qdb_->qmlModules(); + if (!qmlModules.isEmpty()) { + CNMap::ConstIterator g = qmlModules.constBegin(); + while (g != qmlModules.constEnd()) { + if (generateIndexSection(writer, g.value(), generateInternalNodes)) + writer.writeEndElement(); + ++g; + } + } + } + writer.writeEndElement(); } } @@ -1368,45 +1413,8 @@ void QDocIndexFiles::generateIndex(const QString& fileName, writer.writeAttribute("version", qdb_->version()); writer.writeAttribute("project", g->config()->getString(CONFIG_PROJECT)); - generateIndexSections(writer, qdb_->primaryTreeRoot(), generateInternalNodes); - - /* - We wait until the end of the index file to output the group, module, - and QML module elements. By outputting them at the end, when we read - the index file back in, all the group, module, and QML module member - elements will have already been created. It is then only necessary to - create the group, module, or QML module element and add each member to - its member list. - */ - const CNMap& groups = qdb_->groups(); - if (!groups.isEmpty()) { - CNMap::ConstIterator g = groups.constBegin(); - while (g != groups.constEnd()) { - if (generateIndexSection(writer, g.value(), generateInternalNodes)) - writer.writeEndElement(); - ++g; - } - } - - const CNMap& modules = qdb_->modules(); - if (!modules.isEmpty()) { - CNMap::ConstIterator g = modules.constBegin(); - while (g != modules.constEnd()) { - if (generateIndexSection(writer, g.value(), generateInternalNodes)) - writer.writeEndElement(); - ++g; - } - } - - const CNMap& qmlModules = qdb_->qmlModules(); - if (!qmlModules.isEmpty()) { - CNMap::ConstIterator g = qmlModules.constBegin(); - while (g != qmlModules.constEnd()) { - if (generateIndexSection(writer, g.value(), generateInternalNodes)) - writer.writeEndElement(); - ++g; - } - } + top = qdb_->primaryTreeRoot(); + generateIndexSections(writer, top, generateInternalNodes); writer.writeEndElement(); // INDEX writer.writeEndElement(); // QDOCINDEX diff --git a/src/tools/qdoc/qdocindexfiles.h b/src/tools/qdoc/qdocindexfiles.h index bd94fdb215..27ee96ea8d 100644 --- a/src/tools/qdoc/qdocindexfiles.h +++ b/src/tools/qdoc/qdocindexfiles.h @@ -72,7 +72,7 @@ class QDocIndexFiles bool generateInternalNodes = false); void readIndexFile(const QString& path); - void readIndexSection(const QDomElement& element, InnerNode* parent, const QString& indexUrl); + void readIndexSection(const QDomElement& element, Node* current, const QString& indexUrl); void resolveIndex(); bool generateIndexSection(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false); void generateIndexSections(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false); diff --git a/src/tools/qdoc/text.cpp b/src/tools/qdoc/text.cpp index e2f682726d..ae716a0e50 100644 --- a/src/tools/qdoc/text.cpp +++ b/src/tools/qdoc/text.cpp @@ -111,6 +111,22 @@ Text& Text::operator<<(const Atom& atom) return *this; } +/*! + Special output operator for LinkAtom. It makes a copy of + the LinkAtom \a atom and connects the cop;y to the list + in this Text. + */ +Text& Text::operator<<(const LinkAtom& atom) +{ + if (first == 0) { + first = new LinkAtom(atom); + last = first; + } + else + last = new LinkAtom(last, atom); + return *this; +} + Text& Text::operator<<(const Text& text) { const Atom* atom = text.firstAtom(); diff --git a/src/tools/qdoc/text.h b/src/tools/qdoc/text.h index 9274140caa..ca079e3b35 100644 --- a/src/tools/qdoc/text.h +++ b/src/tools/qdoc/text.h @@ -65,6 +65,7 @@ public: Text& operator<<(Atom::Type atomType); Text& operator<<(const QString& string); Text& operator<<(const Atom& atom); + Text& operator<<(const LinkAtom& atom); Text& operator<<(const Text& text); void stripFirstAtom(); void stripLastAtom(); diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index 147abe21af..e689227bf1 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -563,20 +563,6 @@ Node* Tree::findNodeByNameAndType(const QStringList& path, Node::Type type) cons { return findNodeRecursive(path, 0, root(), type); } -#if 0 -/*! - Find the node with the specified \a path name that is of - the specified \a type and \a subtype. Begin the search at - the \a start node. If the \a start node is 0, begin the - search at the tree root. \a subtype is not used unless - \a type is \c{Document}. - */ -Node* Tree::findHtmlFileNode(const QStringList& path) const -{ - return findNodeRecursive(path, 0, root()); -} -#endif -/* internal members */ /*! Recursive search for a node identified by \a path. Each @@ -624,7 +610,7 @@ Node* Tree::findNodeRecursive(const QStringList& path, } else if (n->name() == name) { if (pathIndex+1 >= path.size()) { - if (n->type() == type) + if ((n->type() == type) || (type == Node::NoType)) return n; continue; } @@ -815,6 +801,23 @@ void Tree::insertTarget(const QString& name, TargetRec::Type type, Node* node, i nodesByTarget_.insert(name, target); } +/*! + Searches this tree for a node named \a target and returns + a pointer to it if found. The \a start node is the starting + point, but it only makes sense if \a start is in this tree. + If \a start is not in this tree, \a start is set to 0 before + beginning the search to ensure that the search starts at the + root. + */ +const Node* Tree::resolveTarget(const QString& target, const Node* start) +{ + QStringList path = target.split("::"); + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + if (start && start->tree() != this) + start = 0; + return findNode(path, start, flags); +} + /*! */ void Tree::resolveTargets(InnerNode* root) diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index 7160e321db..a953751968 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -114,6 +114,7 @@ class Tree NameCollisionNode* findCollisionNode(const QString& name) const; QString findTarget(const QString& target, const Node* node) const; void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority); + const Node* resolveTarget(const QString& target, const Node* start); void resolveTargets(InnerNode* root); const Node* findUnambiguousTarget(const QString& target, QString& ref); const DocNode* findDocNodeByTitle(const QString& title) const;