diff options
author | David Boddie <dboddie@trolltech.com> | 2009-06-10 12:34:54 (GMT) |
---|---|---|
committer | David Boddie <dboddie@trolltech.com> | 2009-06-10 12:34:54 (GMT) |
commit | d8a284783b56e2ddbcedff3fc9577ec6038aab57 (patch) | |
tree | 1d49be6cb7de25836957adf30ac113c3a0499911 /tools/qdoc3/htmlgenerator.cpp | |
parent | 4a6548b83a6f1df42b82664efaca26c7efbc1909 (diff) | |
parent | c0bbe44ab6290dee088138c01724779026d2c033 (diff) | |
download | Qt-d8a284783b56e2ddbcedff3fc9577ec6038aab57.zip Qt-d8a284783b56e2ddbcedff3fc9577ec6038aab57.tar.gz Qt-d8a284783b56e2ddbcedff3fc9577ec6038aab57.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
Conflicts:
src/corelib/animation/qanimationgroup.cpp
src/gui/graphicsview/qgraphicsitem.cpp
Diffstat (limited to 'tools/qdoc3/htmlgenerator.cpp')
-rw-r--r-- | tools/qdoc3/htmlgenerator.cpp | 1033 |
1 files changed, 674 insertions, 359 deletions
diff --git a/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp index eb8e65c..335cf66 100644 --- a/tools/qdoc3/htmlgenerator.cpp +++ b/tools/qdoc3/htmlgenerator.cpp @@ -61,6 +61,127 @@ QT_BEGIN_NAMESPACE static bool showBrokenLinks = false; +static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); +static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)"); +static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)"); +static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>"); +static QRegExp unknownTag("</?@[^>]*>"); + +bool parseArg(const QString &src, + const QString &tag, + int *pos, + int n, + QStringRef *contents, + QStringRef *par1 = 0, + bool debug = false) +{ +#define SKIP_CHAR(c) \ + if (debug) \ + qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \ + if (i >= n || src[i] != c) { \ + if (debug) \ + qDebug() << " char '" << c << "' not found"; \ + return false; \ + } \ + ++i; + + +#define SKIP_SPACE \ + while (i < n && src[i] == ' ') \ + ++i; + + int i = *pos; + int j = i; + + // assume "<@" has been parsed outside + //SKIP_CHAR('<'); + //SKIP_CHAR('@'); + + if (tag != QStringRef(&src, i, tag.length())) { + if (0 && debug) + qDebug() << "tag " << tag << " not found at " << i; + return false; + } + + if (debug) + qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i; + + // skip tag + i += tag.length(); + + // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); + if (par1) { + SKIP_SPACE; + // read parameter name + j = i; + while (i < n && src[i].isLetter()) + ++i; + if (src[i] == '=') { + if (debug) + qDebug() << "read parameter" << QString(src.data() + j, i - j); + SKIP_CHAR('='); + SKIP_CHAR('"'); + // skip parameter name + j = i; + while (i < n && src[i] != '"') + ++i; + *par1 = QStringRef(&src, j, i - j); + SKIP_CHAR('"'); + SKIP_SPACE; + } else { + if (debug) + qDebug() << "no optional parameter found"; + } + } + SKIP_SPACE; + SKIP_CHAR('>'); + + // find contents up to closing "</@tag> + j = i; + for (; true; ++i) { + if (i + 4 + tag.length() > n) + return false; + if (src[i] != '<') + continue; + if (src[i + 1] != '/') + continue; + if (src[i + 2] != '@') + continue; + if (tag != QStringRef(&src, i + 3, tag.length())) + continue; + if (src[i + 3 + tag.length()] != '>') + continue; + break; + } + + *contents = QStringRef(&src, j, i - j); + + i += tag.length() + 4; + + *pos = i; + if (debug) + qDebug() << " tag " << tag << " found: pos now: " << i; + return true; +#undef SKIP_CHAR +} + +static void addLink(const QString &linkTarget, + const QStringRef &nestedStuff, + QString *res) +{ + if (!linkTarget.isEmpty()) { + *res += "<a href=\""; + *res += linkTarget; + *res += "\">"; + *res += nestedStuff; + *res += "</a>"; + } + else { + *res += nestedStuff; + } +} + + HtmlGenerator::HtmlGenerator() : helpProjectWriter(0), inLink(false), inContents(false), inSectionHeading(false), inTableHeader(false), numTableRows(0), @@ -349,27 +470,24 @@ int HtmlGenerator::generateAtom(const Atom *atom, out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE]; break; case Atom::Code: - out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()), - marker, relative)) + out() << "<pre>" + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) << "</pre>\n"; break; #ifdef QDOC_QML case Atom::Qml: out() << "<pre>" - << trimmedTrailing(highlightedCode(indent(codeIndent, - atom->string()), - marker, - relative)) + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) << "</pre>\n"; break; #endif case Atom::CodeNew: out() << "<p>you can rewrite it as</p>\n" << "<pre>" - << trimmedTrailing(highlightedCode(indent(codeIndent, - atom->string()), - marker, - relative)) + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) << "</pre>\n"; break; case Atom::CodeOld: @@ -377,7 +495,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, // fallthrough case Atom::CodeBad: out() << "<pre><font color=\"#404040\">" - << trimmedTrailing(protect(plainCode(indent(codeIndent, atom->string())))) + << trimmedTrailing(protect(plainCode(indent(codeIndent,atom->string())))) << "</font></pre>\n"; break; case Atom::FootnoteLeft: @@ -983,12 +1101,11 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, if (s->members.isEmpty()) { if (!s->inherited.isEmpty()) needOtherSection = true; - } - else { - out() << "<a name=\"" << registerRef((*s).name.toLower()) + } else { + out() << "<a name=\"" + << registerRef((*s).name.toLower()) << "\"></a>\n"; - out() << "<h3>" << protect((*s).name) << "</h3>\n"; - + out() << "<h2>" << protect((*s).name) << "</h2>\n"; generateSectionList(*s, inner, marker, CodeMarker::Summary); } ++s; @@ -1037,16 +1154,14 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, QStringList names; names << (*m)->name(); if ((*m)->type() == Node::Function) { - const FunctionNode *func = - reinterpret_cast<const FunctionNode *>(*m); + const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m); if (func->metaness() == FunctionNode::Ctor || - func->metaness() == FunctionNode::Dtor - || func->overloadNumber() != 1) + func->metaness() == FunctionNode::Dtor || + func->overloadNumber() != 1) names.clear(); } else if ((*m)->type() == Node::Property) { - const PropertyNode *prop = - reinterpret_cast<const PropertyNode *>(*m); + const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m); if (!prop->getters().isEmpty() && !names.contains(prop->getters().first()->name())) names << prop->getters().first()->name(); @@ -1056,14 +1171,13 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, names << prop->resetters().first()->name(); } else if ((*m)->type() == Node::Enum) { - const EnumNode *enume = - reinterpret_cast<const EnumNode *>(*m); + const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m); if (enume->flagsType()) names << enume->flagsType()->name(); foreach (const QString &enumName, - enume->doc().enumItemNames().toSet() - - enume->doc().omitEnumItemNames().toSet()) + enume->doc().enumItemNames().toSet() - + enume->doc().omitEnumItemNames().toSet()) names << plainCode(marker->markedUpEnumValue(enumName, enume)); } @@ -1229,7 +1343,7 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) s = sections.begin(); while (s != sections.end()) { out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n"; - out() << "<h3>" << protect((*s).name) << "</h3>\n"; + out() << "<h2>" << protect((*s).name) << "</h2>\n"; generateSectionList(*s, fake, marker, CodeMarker::Summary); ++s; } @@ -1493,17 +1607,19 @@ void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker) { if (!inner->includes().isEmpty()) { - out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, - marker->markedUpIncludes( - inner->includes())), - marker, inner)) + out() << "<pre>" + << trimmedTrailing(highlightedCode(indent(codeIndent, + marker->markedUpIncludes(inner->includes())), + marker,inner)) << "</pre>"; } } -void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker, +void HtmlGenerator::generateTableOfContents(const Node *node, + CodeMarker *marker, Doc::SectioningUnit sectioningUnit, - int numColumns, const Node *relative) + int numColumns, + const Node *relative) { if (!node->doc().hasTableOfContents()) @@ -1686,13 +1802,13 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, } out() << "<p><ul><li><a href=\"" - << linkForNode(inner, 0) - << "\">" << protect(inner->name()) + << linkForNode(inner, 0) << "\">" + << protect(inner->name()) << " class reference</a></li></ul></p>\n"; for (i = 0; i < sections.size(); ++i) { - out() << "<h3>" << protect(sections.at(i).name) << "</h3>\n"; - generateSectionList(sections.at(i),inner,marker,CodeMarker::Summary); + out() << "<h2>" << protect(sections.at(i).name) << "</h2>\n"; + generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary); } sections = marker->sections(inner, CodeMarker::Detailed, status); @@ -1715,8 +1831,7 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, void HtmlGenerator::generateClassHierarchy(const Node *relative, CodeMarker *marker, - const QMap<QString, - const Node *> &classMap) + const QMap<QString,const Node*> &classMap) { if (classMap.isEmpty()) return; @@ -1938,7 +2053,9 @@ void HtmlGenerator::generateCompactList(const Node *relative, out() << "<td align=\"right\">"; if (currentOffsetInParagraph[i] == 0) { // start a new paragraph - out() << "<b>" << paragraphName[currentParagraphNo[i]] << " </b>"; + out() << "<b>" + << paragraphName[currentParagraphNo[i]] + << " </b>"; } out() << "</td>\n"; @@ -1951,7 +2068,9 @@ void HtmlGenerator::generateCompactList(const Node *relative, out() << "<td>"; // Previously, we used generateFullName() for this, but we // require some special formatting. - out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">"; + out() << "<a href=\"" + << linkForNode(it.value(), relative) + << "\">"; QStringList pieces = fullName(it.value(), relative, marker).split("::"); out() << protect(pieces.last()); out() << "</a>"; @@ -2040,7 +2159,7 @@ void HtmlGenerator::generateLegaleseList(const Node *relative, } } -void HtmlGenerator::generateSynopsis(const Node *node, +/*void HtmlGenerator::generateSynopsis(const Node *node, const Node *relative, CodeMarker *marker, CodeMarker::SynopsisStyle style) @@ -2076,7 +2195,7 @@ void HtmlGenerator::generateSynopsis(const Node *node, marked.replace("</@type>", ""); } out() << highlightedCode(marked, marker, relative); -} +}*/ #ifdef QDOC_QML void HtmlGenerator::generateQmlItem(const Node *node, @@ -2111,8 +2230,7 @@ void HtmlGenerator::generateQmlItem(const Node *node, } #endif -void HtmlGenerator::generateOverviewList(const Node *relative, - CodeMarker * /* marker */) +void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */) { QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap; QMap<QString, const FakeNode *> groupTitlesMap; @@ -2226,24 +2344,35 @@ void HtmlGenerator::generateOverviewList(const Node *relative, } } +#ifdef QDOC_NAME_ALIGNMENT void HtmlGenerator::generateSectionList(const Section& section, const Node *relative, CodeMarker *marker, CodeMarker::SynopsisStyle style) { + bool name_alignment = true; if (!section.members.isEmpty()) { bool twoColumn = false; if (style == CodeMarker::SeparateList) { + name_alignment = false; twoColumn = (section.members.count() >= 16); } else if (section.members.first()->type() == Node::Property) { twoColumn = (section.members.count() >= 5); + name_alignment = false; + } + if (name_alignment) { + out() << "<table border=\"0\" cellpadding=\"0\" " + << "cellspacing=\"0\" width=\"100%\">\n"; + } + else { + if (twoColumn) + out() << "<p><table width=\"100%\" " + << "border=\"0\" cellpadding=\"0\"" + << " cellspacing=\"0\">\n" + << "<tr><td width=\"45%\" valign=\"top\">"; + out() << "<ul>\n"; } - if (twoColumn) - out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\"" - " cellspacing=\"0\">\n" - << "<tr><td width=\"45%\" valign=\"top\">"; - out() << "<ul>\n"; int i = 0; NodeList::ConstIterator m = section.members.begin(); @@ -2253,80 +2382,51 @@ void HtmlGenerator::generateSectionList(const Section& section, continue; } - if (twoColumn && i == (int) (section.members.count() + 1) / 2) - out() << "</ul></td><td valign=\"top\"><ul>\n"; + if (name_alignment) { + out() << "<tr><td class=\"memItemLeft\" " + << "align=\"right\" valign=\"top\">"; + } + else { + if (twoColumn && i == (int) (section.members.count() + 1) / 2) + out() << "</ul></td><td valign=\"top\"><ul>\n"; + out() << "<li><div class=\"fn\">"; + } - out() << "<li><div class=\"fn\"></div>"; - if (style == CodeMarker::Accessors) - out() << "<b>"; - generateSynopsis(*m, relative, marker, style); - if (style == CodeMarker::Accessors) - out() << "</b>"; - out() << "</li>\n"; + generateSynopsis(*m, relative, marker, style, name_alignment); + if (name_alignment) + out() << "</td></tr>\n"; + else + out() << "</div></li>\n"; i++; ++m; } - out() << "</ul>\n"; - if (twoColumn) - out() << "</td></tr>\n</table></p>\n"; + if (name_alignment) + out() << "</table>\n"; + else { + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table></p>\n"; + } } if (style == CodeMarker::Summary && !section.inherited.isEmpty()) { out() << "<ul>\n"; - generateSectionInheritedList(section, relative, marker); + generateSectionInheritedList(section, relative, marker, name_alignment); out() << "</ul>\n"; } } -#ifdef QDOC_QML -/*! - Generates the summary for for the \a section. Only used for - sections of QML element documentation. - - Currently handles only the QML property group. - */ -void HtmlGenerator::generateQmlSummary(const Section& section, - const Node *relative, - CodeMarker *marker) -{ - if (!section.members.isEmpty()) { - NodeList::ConstIterator m; - int count = section.members.size(); - bool twoColumn = false; - if (section.members.first()->type() == Node::QmlProperty) { - twoColumn = (count >= 5); - } - if (twoColumn) - out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\"" - " cellspacing=\"0\">\n" - << "<tr><td width=\"45%\" valign=\"top\">"; - out() << "<ul>\n"; - - int row = 0; - m = section.members.begin(); - while (m != section.members.end()) { - if (twoColumn && row == (int) (count + 1) / 2) - out() << "</ul></td><td valign=\"top\"><ul>\n"; - out() << "<li><div class=\"fn\"></div>"; - generateQmlItem(*m,relative,marker,true); - out() << "</li>\n"; - row++; - ++m; - } - out() << "</ul>\n"; - if (twoColumn) - out() << "</td></tr>\n</table></p>\n"; - } -} -#endif - void HtmlGenerator::generateSectionInheritedList(const Section& section, const Node *relative, - CodeMarker *marker) + CodeMarker *marker, + bool nameAlignment) { QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin(); while (p != section.inherited.end()) { - out() << "<li><div class=\"fn\"></div>"; + if (nameAlignment) + out() << "<li><div bar=2 class=\"fn\"></div>"; + else + out() << "<li><div class=\"fn\"></div>"; out() << (*p).second << " "; if ((*p).second == 1) { out() << section.singularMember; @@ -2342,298 +2442,325 @@ void HtmlGenerator::generateSectionInheritedList(const Section& section, } } -void HtmlGenerator::generateLink(const Atom *atom, - const Node * /* relative */, - CodeMarker *marker) +void HtmlGenerator::generateSynopsis(const Node *node, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style, + bool nameAlignment) { - static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_"); + QString marked = marker->markedUpSynopsis(node, relative, style); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protect(marked.mid(templateTag.pos(1), + templateTag.cap(1).length())); + marked.replace(templateTag.pos(1), templateTag.cap(1).length(), + contents); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i>"); + marked.replace("</@param>", "</i>"); - if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) { - // hack for C++: move () outside of link - int k = funcLeftParen.pos(1); - out() << protect(atom->string().left(k)); - if (link.isEmpty()) { - if (showBrokenLinks) - out() << "</i>"; - } - else { - out() << "</a>"; - } - inLink = false; - out() << protect(atom->string().mid(k)); + if (style == CodeMarker::Summary) { + marked.replace("<@name>", ""); // was "<b>" + marked.replace("</@name>", ""); // was "</b>" } - else if (marker->recognizeLanguage("Java")) { - // hack for Java: remove () and use <tt> when appropriate - bool func = atom->string().endsWith("()"); - bool tt = (func || atom->string().contains(camelCase)); - if (tt) - out() << "<tt>"; - if (func) { - out() << protect(atom->string().left(atom->string().length() - 2)); - } - else { - out() << protect(atom->string()); - } - out() << "</tt>"; + + if (style == CodeMarker::SeparateList) { + QRegExp extraRegExp("<@extra>.*</@extra>"); + extraRegExp.setMinimal(true); + marked.replace(extraRegExp, ""); + } else { + marked.replace("<@extra>", " <tt>"); + marked.replace("</@extra>", "</tt>"); } - else { - out() << protect(atom->string()); + + if (style != CodeMarker::Detailed) { + marked.replace("<@type>", ""); + marked.replace("</@type>", ""); } + out() << highlightedCode(marked, marker, relative, style, nameAlignment); } -QString HtmlGenerator::cleanRef(const QString& ref) +QString HtmlGenerator::highlightedCode(const QString& markedCode, + CodeMarker *marker, + const Node *relative, + CodeMarker::SynopsisStyle style, + bool nameAlignment) { - QString clean; - - if (ref.isEmpty()) - return clean; - - clean.reserve(ref.size() + 20); - const QChar c = ref[0]; - const uint u = c.unicode(); + QString src = markedCode; + QString html; + QStringRef arg; + QStringRef par1; - if ((u >= 'a' && u <= 'z') || - (u >= 'A' && u <= 'Z') || - (u >= '0' && u <= '9')) { - clean += c; - } - else if (u == '~') { - clean += "dtor."; - } - else if (u == '_') { - clean += "underscore."; - } - else { - clean += "A"; - } + const QChar charLangle = '<'; + const QChar charAt = '@'; - for (int i = 1; i < (int) ref.length(); i++) { - const QChar c = ref[i]; - const uint u = c.unicode(); - if ((u >= 'a' && u <= 'z') || - (u >= 'A' && u <= 'Z') || - (u >= '0' && u <= '9') || u == '-' || - u == '_' || u == ':' || u == '.') { - clean += c; - } - else if (c.isSpace()) { - clean += "-"; - } - else if (u == '!') { - clean += "-not"; - } - else if (u == '&') { - clean += "-and"; - } - else if (u == '<') { - clean += "-lt"; - } - else if (u == '=') { - clean += "-eq"; - } - else if (u == '>') { - clean += "-gt"; - } - else if (u == '#') { - clean += "#"; + // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" + static const QString linkTag("link"); + if (src.contains("setAcceptDrops")) + qDebug() << "SRC:" << src; + bool done = false; + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1).unicode() == '@') { + if (nameAlignment && !done) {// && (i != 0)) Why was this here? + html += "</td><td class=\"memItemRight\" valign=\"bottom\">"; + done = true; + } + i += 2; + if (parseArg(src, linkTag, &i, n, &arg, &par1)) { + html += "<b>"; + QString link = linkForNode( + CodeMarker::nodeForString(par1.toString()), relative); + addLink(link, arg, &html); + html += "</b>"; + } + else { + html += charLangle; + html += charAt; + } } else { - clean += "-"; - clean += QString::number((int)u, 16); + html += src.at(i++); } } - return clean; -} -QString HtmlGenerator::registerRef(const QString& ref) -{ - QString clean = HtmlGenerator::cleanRef(ref); - for (;;) { - QString& prevRef = refMap[clean.toLower()]; - if (prevRef.isEmpty()) { - prevRef = ref; - break; - } - else if (prevRef == ref) { - break; + if (slow) { + // is this block ever used at all? + // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)" + src = html; + html = QString(); + static const QString funcTag("func"); + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + if (parseArg(src, funcTag, &i, n, &arg, &par1)) { + QString link = linkForNode( + marker->resolveTarget(par1.toString(), + tre, + relative), + relative); + addLink(link, arg, &html); + par1 = QStringRef(); + } + else { + html += charLangle; + html += charAt; + } + } + else { + html += src.at(i++); + } } - clean += "x"; } - return clean; -} -QString HtmlGenerator::protect(const QString& string) -{ -#define APPEND(x) \ - if (html.isEmpty()) { \ - html = string; \ - html.truncate(i); \ - } \ - html += (x); - - QString html; - int n = string.length(); - - for (int i = 0; i < n; ++i) { - QChar ch = string.at(i); - - if (ch == QLatin1Char('&')) { - APPEND("&"); - } - else if (ch == QLatin1Char('<')) { - APPEND("<"); - } - else if (ch == QLatin1Char('>')) { - APPEND(">"); + // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags + src = html; + html = QString(); + static const QString typeTags[] = { "type", "headerfile", "func" }; + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + bool handled = false; + for (int k = 0; k != 3; ++k) { + if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) { + par1 = QStringRef(); + QString link = linkForNode( + marker->resolveTarget(arg.toString(), tre, relative), + relative); + addLink(link, arg, &html); + handled = true; + break; + } + } + if (!handled) { + html += charLangle; + html += charAt; + } } - else if (ch == QLatin1Char('"')) { - APPEND("""); + else { + html += src.at(i++); } - else if (ch.unicode() > 0x007F || - (ch == QLatin1Char('*') && - i + 1 < n && - string.at(i) == QLatin1Char('/')) || - (ch == QLatin1Char('.') && - i > 2 && - string.at(i - 2) == QLatin1Char('.'))) { - // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator - APPEND("&#x"); - html += QString::number(ch.unicode(), 16); - html += QLatin1Char(';'); + } + + // replace all + // "<@comment>" -> "<span class=\"comment\">"; + // "<@preprocessor>" -> "<span class=\"preprocessor\">"; + // "<@string>" -> "<span class=\"string\">"; + // "<@char>" -> "<span class=\"char\">"; + // "</@(?:comment|preprocessor|string|char)>" -> "</span>" + src = html; + html = QString(); + static const QString spanTags[] = { + "<@comment>", "<span class=\"comment\">", + "<@preprocessor>", "<span class=\"preprocessor\">", + "<@string>", "<span class=\"string\">", + "<@char>", "<span class=\"char\">", + "</@comment>", "</span>", + "</@preprocessor>","</span>", + "</@string>", "</span>", + "</@char>", "</span>" + // "<@char>", "<font color=blue>", + // "</@char>", "</font>", + // "<@func>", "<font color=green>", + // "</@func>", "</font>", + // "<@id>", "<i>", + // "</@id>", "</i>", + // "<@keyword>", "<b>", + // "</@keyword>", "</b>", + // "<@number>", "<font color=yellow>", + // "</@number>", "</font>", + // "<@op>", "<b>", + // "</@op>", "</b>", + // "<@param>", "<i>", + // "</@param>", "</i>", + // "<@string>", "<font color=green>", + // "</@string>", "</font>", + }; + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle) { + bool handled = false; + for (int k = 0; k != 8; ++k) { + const QString & tag = spanTags[2 * k]; + if (tag == QStringRef(&src, i, tag.length())) { + html += spanTags[2 * k + 1]; + i += tag.length(); + handled = true; + break; + } + } + if (!handled) { + ++i; + if (src.at(i) == charAt || + (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) { + // drop 'our' unknown tags (the ones still containing '@') + while (i < n && src.at(i) != QLatin1Char('>')) + ++i; + ++i; + } + else { + // retain all others + html += charLangle; + } + } } else { - if (!html.isEmpty()) - html += ch; + html += src.at(i); + ++i; } } - if (!html.isEmpty()) - return html; - return string; - -#undef APPEND + return html; } -static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); -static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)"); -static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)"); -static QRegExp spanTag("</@(?:comment|preprocessor|string|char)>"); -static QRegExp unknownTag("</?@[^>]*>"); - -bool parseArg(const QString &src, - const QString &tag, - int *pos, - int n, - QStringRef *contents, - QStringRef *par1 = 0, - bool debug = false) +#else +void HtmlGenerator::generateSectionList(const Section& section, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style) { -#define SKIP_CHAR(c) \ - if (debug) \ - qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \ - if (i >= n || src[i] != c) { \ - if (debug) \ - qDebug() << " char '" << c << "' not found"; \ - return false; \ - } \ - ++i; - - -#define SKIP_SPACE \ - while (i < n && src[i] == ' ') \ - ++i; + if (!section.members.isEmpty()) { + bool twoColumn = false; + if (style == CodeMarker::SeparateList) { + twoColumn = (section.members.count() >= 16); + } else if (section.members.first()->type() == Node::Property) { + twoColumn = (section.members.count() >= 5); + } + if (twoColumn) + out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\"" + " cellspacing=\"0\">\n" + << "<tr><td width=\"45%\" valign=\"top\">"; + out() << "<ul>\n"; - int i = *pos; - int j = i; + int i = 0; + NodeList::ConstIterator m = section.members.begin(); + while (m != section.members.end()) { + if ((*m)->access() == Node::Private) { + ++m; + continue; + } - // assume "<@" has been parsed outside - //SKIP_CHAR('<'); - //SKIP_CHAR('@'); + if (twoColumn && i == (int) (section.members.count() + 1) / 2) + out() << "</ul></td><td valign=\"top\"><ul>\n"; - if (tag != QStringRef(&src, i, tag.length())) { - if (0 && debug) - qDebug() << "tag " << tag << " not found at " << i; - return false; + out() << "<li><div class=\"fn\"></div>"; + if (style == CodeMarker::Accessors) + out() << "<b>"; + generateSynopsis(*m, relative, marker, style); + if (style == CodeMarker::Accessors) + out() << "</b>"; + out() << "</li>\n"; + i++; + ++m; + } + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table></p>\n"; } - if (debug) - qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i; - - // skip tag - i += tag.length(); + if (style == CodeMarker::Summary && !section.inherited.isEmpty()) { + out() << "<ul>\n"; + generateSectionInheritedList(section, relative, marker); + out() << "</ul>\n"; + } +} - // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); - if (par1) { - SKIP_SPACE; - // read parameter name - j = i; - while (i < n && src[i].isLetter()) - ++i; - if (src[i] == '=') { - if (debug) - qDebug() << "read parameter" << QString(src.data() + j, i - j); - SKIP_CHAR('='); - SKIP_CHAR('"'); - // skip parameter name - j = i; - while (i < n && src[i] != '"') - ++i; - *par1 = QStringRef(&src, j, i - j); - SKIP_CHAR('"'); - SKIP_SPACE; - } - else { - if (debug) - qDebug() << "no optional parameter found"; +void HtmlGenerator::generateSectionInheritedList(const Section& section, + const Node *relative, + CodeMarker *marker) +{ + QList<QPair<ClassNode *, int> >::ConstIterator p = section.inherited.begin(); + while (p != section.inherited.end()) { + out() << "<li><div bar=2 class=\"fn\"></div>"; + out() << (*p).second << " "; + if ((*p).second == 1) { + out() << section.singularMember; + } else { + out() << section.pluralMember; } + out() << " inherited from <a href=\"" << fileName((*p).first) + << "#" << HtmlGenerator::cleanRef(section.name.toLower()) << "\">" + << protect(marker->plainFullName((*p).first, relative)) + << "</a></li>\n"; + ++p; } - SKIP_SPACE; - SKIP_CHAR('>'); +} - // find contents up to closing "</@tag> - j = i; - for (; true; ++i) { - if (i + 4 + tag.length() > n) - return false; - if (src[i] != '<') - continue; - if (src[i + 1] != '/') - continue; - if (src[i + 2] != '@') - continue; - if (tag != QStringRef(&src, i + 3, tag.length())) - continue; - if (src[i + 3 + tag.length()] != '>') - continue; - break; +void HtmlGenerator::generateSynopsis(const Node *node, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style) +{ + QString marked = marker->markedUpSynopsis(node, relative, style); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protect(marked.mid(templateTag.pos(1), + templateTag.cap(1).length())); + marked.replace(templateTag.pos(1), templateTag.cap(1).length(), + contents); } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i>"); + marked.replace("</@param>", "</i>"); - *contents = QStringRef(&src, j, i - j); - - i += tag.length() + 4; - - *pos = i; - if (debug) - qDebug() << " tag " << tag << " found: pos now: " << i; - return true; -#undef SKIP_CHAR -} + if (style == CodeMarker::Summary) + marked.replace("@name>", "b>"); -static void addLink(const QString &linkTarget, - const QStringRef &nestedStuff, - QString *res) -{ - if (!linkTarget.isEmpty()) { - *res += "<a href=\""; - *res += linkTarget; - *res += "\">"; - *res += nestedStuff; - *res += "</a>"; + if (style == CodeMarker::SeparateList) { + QRegExp extraRegExp("<@extra>.*</@extra>"); + extraRegExp.setMinimal(true); + marked.replace(extraRegExp, ""); + } else { + marked.replace("<@extra>", " <tt>"); + marked.replace("</@extra>", "</tt>"); } - else { - *res += nestedStuff; + + if (style != CodeMarker::Detailed) { + marked.replace("<@type>", ""); + marked.replace("</@type>", ""); } + out() << highlightedCode(marked, marker, relative); } QString HtmlGenerator::highlightedCode(const QString& markedCode, @@ -2795,6 +2922,153 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, return html; } +#endif + +void HtmlGenerator::generateLink(const Atom *atom, const Node * /* relative */, CodeMarker *marker) +{ + static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_"); + + if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) { + // hack for C++: move () outside of link + int k = funcLeftParen.pos(1); + out() << protect(atom->string().left(k)); + if (link.isEmpty()) { + if (showBrokenLinks) + out() << "</i>"; + } else { + out() << "</a>"; + } + inLink = false; + out() << protect(atom->string().mid(k)); + } else if (marker->recognizeLanguage("Java")) { + // hack for Java: remove () and use <tt> when appropriate + bool func = atom->string().endsWith("()"); + bool tt = (func || atom->string().contains(camelCase)); + if (tt) + out() << "<tt>"; + if (func) { + out() << protect(atom->string().left(atom->string().length() - 2)); + } else { + out() << protect(atom->string()); + } + out() << "</tt>"; + } else { + out() << protect(atom->string()); + } +} + +QString HtmlGenerator::cleanRef(const QString& ref) +{ + QString clean; + + if (ref.isEmpty()) + return clean; + + clean.reserve(ref.size() + 20); + const QChar c = ref[0]; + const uint u = c.unicode(); + + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) { + clean += c; + } else if (u == '~') { + clean += "dtor."; + } else if (u == '_') { + clean += "underscore."; + } else { + clean += "A"; + } + + for (int i = 1; i < (int) ref.length(); i++) { + const QChar c = ref[i]; + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9') || u == '-' || + u == '_' || u == ':' || u == '.') { + clean += c; + } else if (c.isSpace()) { + clean += "-"; + } else if (u == '!') { + clean += "-not"; + } else if (u == '&') { + clean += "-and"; + } else if (u == '<') { + clean += "-lt"; + } else if (u == '=') { + clean += "-eq"; + } else if (u == '>') { + clean += "-gt"; + } else if (u == '#') { + clean += "#"; + } else { + clean += "-"; + clean += QString::number((int)u, 16); + } + } + return clean; +} + +QString HtmlGenerator::registerRef(const QString& ref) +{ + QString clean = HtmlGenerator::cleanRef(ref); + + for (;;) { + QString& prevRef = refMap[clean.toLower()]; + if (prevRef.isEmpty()) { + prevRef = ref; + break; + } else if (prevRef == ref) { + break; + } + clean += "x"; + } + return clean; +} + +QString HtmlGenerator::protect(const QString& string) +{ +#define APPEND(x) \ + if (html.isEmpty()) { \ + html = string; \ + html.truncate(i); \ + } \ + html += (x); + + QString html; + int n = string.length(); + + for (int i = 0; i < n; ++i) { + QChar ch = string.at(i); + + if (ch == QLatin1Char('&')) { + APPEND("&"); + } else if (ch == QLatin1Char('<')) { + APPEND("<"); + } else if (ch == QLatin1Char('>')) { + APPEND(">"); + } else if (ch == QLatin1Char('"')) { + APPEND("""); + } else if (ch.unicode() > 0x007F + || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/')) + || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) { + // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator + APPEND("&#x"); + html += QString::number(ch.unicode(), 16); + html += QLatin1Char(';'); + } else { + if (!html.isEmpty()) + html += ch; + } + } + + if (!html.isEmpty()) + return html; + return string; + +#undef APPEND +} QString HtmlGenerator::fileBase(const Node *node) { @@ -3020,7 +3294,7 @@ void HtmlGenerator::generateDetailedMember(const Node *node, section.members += property->resetters(); if (!section.members.isEmpty()) { - out() << "<p>Access functions:</p>\n"; + out() << "<p><b>Access functions:</b></p>\n"; generateSectionList(section, node, marker, CodeMarker::Accessors); } } @@ -3046,7 +3320,8 @@ void HtmlGenerator::findAllClasses(const InnerNode *node) if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) { if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) { QString className = (*c)->name(); - if ((*c)->parent() && (*c)->parent()->type() == Node::Namespace && + if ((*c)->parent() && + (*c)->parent()->type() == Node::Namespace && !(*c)->parent()->name().isEmpty()) className = (*c)->parent()->name()+"::"+className; @@ -3094,7 +3369,7 @@ void HtmlGenerator::findAllFunctions(const InnerNode *node) const FunctionNode *func = static_cast<const FunctionNode *>(*c); if (func->status() > Node::Obsolete && func->metaness() != FunctionNode::Ctor && func->metaness() != FunctionNode::Dtor) { - funcIndex[(*c)->name()].insert((*c)->parent()->name(), *c); + funcIndex[(*c)->name()].insert(tre->fullDocumentName((*c)->parent()), *c); } } } @@ -3432,6 +3707,46 @@ QT_END_NAMESPACE #ifdef QDOC_QML /*! + Generates the summary for for the \a section. Only used for + sections of QML element documentation. + + Currently handles only the QML property group. + */ +void HtmlGenerator::generateQmlSummary(const Section& section, + const Node *relative, + CodeMarker *marker) +{ + if (!section.members.isEmpty()) { + NodeList::ConstIterator m; + int count = section.members.size(); + bool twoColumn = false; + if (section.members.first()->type() == Node::QmlProperty) { + twoColumn = (count >= 5); + } + if (twoColumn) + out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\"" + " cellspacing=\"0\">\n" + << "<tr><td width=\"45%\" valign=\"top\">"; + out() << "<ul>\n"; + + int row = 0; + m = section.members.begin(); + while (m != section.members.end()) { + if (twoColumn && row == (int) (count + 1) / 2) + out() << "</ul></td><td valign=\"top\"><ul>\n"; + out() << "<li><div class=\"fn\"></div>"; + generateQmlItem(*m,relative,marker,true); + out() << "</li>\n"; + row++; + ++m; + } + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table></p>\n"; + } +} + +/*! Outputs the html detailed documentation for a section on a QML element reference page. */ |