From 5a1983f9240e52be6df4cd3260d13eb4e9338dcd Mon Sep 17 00:00:00 2001
From: Leandro Melo <leandro.melo@nokia.com>
Date: Wed, 21 Jul 2010 17:25:58 +0200
Subject: [PATCH] C++ tooltip: Integration with qdocs now only with 4.7 html
 marks.

---
 src/libs/utils/htmldocextractor.cpp       | 240 +++++-----------------
 src/libs/utils/htmldocextractor.h         |  37 +---
 src/plugins/cppeditor/cpphoverhandler.cpp |  69 +++----
 src/plugins/cppeditor/cpphoverhandler.h   |  10 +-
 4 files changed, 97 insertions(+), 259 deletions(-)

diff --git a/src/libs/utils/htmldocextractor.cpp b/src/libs/utils/htmldocextractor.cpp
index 90d2ee6e0ff..cbaa37fc477 100644
--- a/src/libs/utils/htmldocextractor.cpp
+++ b/src/libs/utils/htmldocextractor.cpp
@@ -59,182 +59,108 @@ void HtmlDocExtractor::setLengthReference(const int length, const bool truncateA
 void HtmlDocExtractor::setFormatContents(const bool format)
 { m_formatContents = format; }
 
-QString HtmlDocExtractor::assemble(const QString &elementAttr,
-                                   const QString &elementTemplate) const
+QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QString &mark) const
 {
-    const QString &cleanAttr = cleanReference(elementAttr);
-    return QString(elementTemplate).arg(cleanAttr);
-}
-
-QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QString &name) const
-{
-    QString contents = getContentsByMarks(html, name + QLatin1String("-brief"), false);
-    if (contents.isEmpty()) {
-        QLatin1String pattern("<h1 class=\"title\">.*</p>");
-        contents = findByPattern(html, pattern);
-        if (!contents.isEmpty())
-            contents.remove(QRegExp(QLatin1String("<h1.*</h1>")));
-    }
-    if (!contents.isEmpty()) {
+    QString contents = getContentsByMarks(html, mark + QLatin1String("-brief"), mark);
+    if (!contents.isEmpty() && m_formatContents) {
         contents.remove(QLatin1String("<a href=\"#details\">More...</a>"));
-        if (m_formatContents) {
-               contents.prepend(QLatin1String("<nobr>"));
-               contents.append(QLatin1String("</nobr>"));
-        }
+        contents.prepend(QLatin1String("<nobr>"));
+        contents.append(QLatin1String("</nobr>"));
+        formatContents(&contents);
     }
 
-    formatContents(&contents);
     return contents;
 }
 
 QString HtmlDocExtractor::getClassOrNamespaceDescription(const QString &html,
-                                                         const QString &name) const
+                                                         const QString &mark) const
 {
-    QString contents = getContentsByMarks(html, name + QLatin1String("-description"), false);
-    if (contents.isEmpty()) {
-        QLatin1String pattern("<a name=\"details\"></a>.*<hr />.*<hr />");
-        contents = findByPattern(html, pattern);
-    }
-    if (!contents.isEmpty())
+    QString contents = getContentsByMarks(html, mark + QLatin1String("-description"), mark);
+    if (!contents.isEmpty() && m_formatContents) {
         contents.remove(QLatin1String("Detailed Description"));
+        formatContents(&contents);
+    }
 
-    formatContents(&contents);
-    return contents;
-}
-
-QString HtmlDocExtractor::getEnumDescription(const QString &html, const QString &name) const
-{
-    const QString &enumm = name + QLatin1String("-enum");
-    QString contents = getClassOrNamespaceMemberDescription(html, name, enumm, false);
-    formatContents(&contents);
     return contents;
 }
 
-QString HtmlDocExtractor::getTypedefDescription(const QString &html, const QString &name) const
+QString HtmlDocExtractor::getEnumDescription(const QString &html, const QString &mark) const
 {
-    const QString &typedeff = name + QLatin1String("-typedef");
-    QString contents = getClassOrNamespaceMemberDescription(html, name, typedeff, false);
-    formatContents(&contents);
-    return contents;
+    return getClassOrNamespaceMemberDescription(html, mark, mark);
 }
 
-QString HtmlDocExtractor::getVarDescription(const QString &html, const QString &name) const
+QString HtmlDocExtractor::getTypedefDescription(const QString &html, const QString &mark) const
 {
-    const QString &var = name + QLatin1String("-var");
-    QString contents = getClassOrNamespaceMemberDescription(html, name, var, false);
-    formatContents(&contents);
-    return contents;
+    return getClassOrNamespaceMemberDescription(html, mark, mark);
 }
 
 QString HtmlDocExtractor::getMacroDescription(const QString &html,
-                                              const QString &mark,
-                                              const QString &anchorName) const
+                                              const QString &mark) const
 {
-    QString contents = getClassOrNamespaceMemberDescription(html, mark, anchorName, false, true);
-    formatContents(&contents);
-    return contents;
+    return getClassOrNamespaceMemberDescription(html, mark, mark);
 }
 
 QString HtmlDocExtractor::getFunctionDescription(const QString &html,
                                                  const QString &mark,
-                                                 const QString &anchorName,
                                                  const bool mainOverload) const
 {
-    QString contents = getClassOrNamespaceMemberDescription(html, mark, anchorName, mainOverload);
+    QString cleanMark = mark;
+    QString startMark = mark;
+    const int parenthesis = mark.indexOf(QLatin1Char('('));
+    if (parenthesis != -1) {
+        startMark = mark.left(parenthesis);
+        cleanMark = startMark;
+        if (mainOverload) {
+            startMark.append(QLatin1String("[overload1]"));
+        } else {
+            QString complement = mark.right(mark.length() - parenthesis);
+            complement.remove(QRegExp(QLatin1String("[\\(\\), ]")));
+            startMark.append(complement);
+        }
+    }
+
+    QString contents = getClassOrNamespaceMemberDescription(html, startMark, cleanMark);
     if (contents.isEmpty()) {
-        // Maybe marks are not present and/or this is a property. Besides setX/isX/hasX there are
-        // other (not so usual) names for property based functions. A few examples of those:
+        // Maybe this is a property function, which is documented differently. Besides
+        // setX/isX/hasX there are other (not so usual) names for them. A few examples of those:
         //   - toPlainText / Prop. plainText from QPlainTextEdit.
         //   - resize / Prop. size from QWidget.
         //   - move / Prop. pos from QWidget (nothing similar in the names in this case).
         // So I try to find the link to this property in the list of properties, extract its
         // anchor and then follow by the name found.
-        QString pattern = assemble(anchorName,
-                                   QLatin1String("<a href=\"[a-z\\.]+#([A-Za-z]+-prop)\">%1</a>"));
+        const QString &pattern =
+            QString(QLatin1String("<a href=\"[a-z\\.]+#([A-Za-z]+)-prop\">%1</a>")).arg(cleanMark);
         QRegExp exp = createMinimalExp(pattern);
         if (exp.indexIn(html) != -1) {
             const QString &prop = exp.cap(1);
-            contents = getClassOrNamespaceMemberDescription(html, prop, prop, false);
+            contents = getClassOrNamespaceMemberDescription(html,
+                                                            prop + QLatin1String("-prop"),
+                                                            prop);
         }
     }
-    formatContents(&contents);
+
     return contents;
 }
 
 QString HtmlDocExtractor::getClassOrNamespaceMemberDescription(const QString &html,
-                                                               const QString &mark,
-                                                               const QString &anchorName,
-                                                               const bool mainOverload,
-                                                               const bool relaxedMatch) const
+                                                               const QString &startMark,
+                                                               const QString &endMark) const
 {
-    // Try with extraction marks (present in newer verions of the docs). If nothing is found try
-    // with the anchor.
-    QString contents;
-    if (!mark.isEmpty())
-        contents = getContentsByMarks(html, mark, mainOverload);
-    if (contents.isEmpty())
-        contents = getContentsByAnchor(html, anchorName, relaxedMatch);
-
-    return contents;
-}
+    QString contents = getContentsByMarks(html, startMark, endMark);
 
-QString HtmlDocExtractor::getContentsByAnchor(const QString &html,
-                                              const QString &name,
-                                              const bool relaxedMatch) const
-{
-    // This approach is not very accurate.
-    QString pattern;
-    if (relaxedMatch) {
-        pattern = QLatin1String(
-            "(?:<h3 class=\"[a-z]+\">)?<a name=\"%1.*(?:<h3 class|<p />|<hr />|</div>)");
-    } else {
-        // When there are duplicates the HTML generator incrementally appends 'x' to references.
-        pattern = QLatin1String(
-            "(?:<h3 class=\"[a-z]+\">)?<a name=\"%1x*\">.*(?:<h3 class|<p />|<hr />|</div>)");
-    }
+    if (!contents.isEmpty())
+        formatContents(&contents);
 
-    return findByPattern(html, assemble(name, pattern));
+    return contents;
 }
 
 QString HtmlDocExtractor::getContentsByMarks(const QString &html,
-                                             const QString &id,
-                                             const bool mainOverload) const
+                                             QString startMark,
+                                             QString endMark) const
 {
-    QString endMark;
-    QString startMark;
-    if (id.contains(QLatin1Char('('))) {
-        const int index = id.indexOf(QLatin1Char('('));
-        startMark = id.left(index);
-        endMark = startMark;
-        if (mainOverload) {
-            startMark.append(QLatin1String("[overload1]"));
-        } else {
-            QString complementaryId = id.right(id.length() - index);
-            complementaryId.remove(QRegExp(QLatin1String("[\\(\\), ]")));
-            startMark.append(complementaryId);
-        }
-    } else {
-        startMark = id;
-    }
     startMark.prepend(QLatin1String("$$$"));
-
-    if (endMark.isEmpty()) {
-        if (id.contains(QLatin1Char('-'))) {
-            const int index = id.indexOf(QLatin1Char('-'));
-            endMark = id.left(index);
-        } else {
-            endMark = id;
-        }
-    }
     endMark.prepend(QLatin1String("<!-- @@@"));
 
-    return findByMarks(html, startMark, endMark);
-}
-
-QString HtmlDocExtractor::findByMarks(const QString &html,
-                                      const QString &startMark,
-                                      const QString &endMark) const
-{
     QString contents;
     int start = html.indexOf(startMark);
     if (start != -1) {
@@ -250,16 +176,6 @@ QString HtmlDocExtractor::findByMarks(const QString &html,
     return contents;
 }
 
-QString HtmlDocExtractor::findByPattern(const QString &html, const QString &pattern) const
-{
-    QRegExp exp(pattern);
-    exp.setMinimal(true);
-    const int match = exp.indexIn(html);
-    if (match != -1)
-        return html.mid(match, exp.matchedLength());
-    return QString();
-}
-
 void HtmlDocExtractor::formatContents(QString *html) const
 {
     if (html->isEmpty())
@@ -379,61 +295,3 @@ void HtmlDocExtractor::replaceListsForSimpleLines(QString *html)
     html->replace(QLatin1String("<li>"), QLatin1String("&nbsp;&nbsp;&nbsp;&nbsp;"));
     html->replace(QLatin1String("</li>"), QLatin1String("<br />"));
 }
-
-/*
- @todo: We need to clean the anchor in the same way qtdoc does. Currently, this method is a
- duplicate of HtmlGenerator::cleanRef. It would be good to reuse the same code either by exposing
- parts of qtdocs or by refactoring the behavior to use some Qt component for example.
- */
-QString HtmlDocExtractor::cleanReference(const QString &reference)
-{
-    QString clean;
-
-    if (reference.isEmpty())
-        return clean;
-
-    clean.reserve(reference.size() + 20);
-    const QChar c = reference[0];
-    const uint u = c.unicode();
-
-    if ((u >= QLatin1Char('a') && u <= QLatin1Char('z')) ||
-         (u >= QLatin1Char('A') && u <= QLatin1Char('Z')) ||
-         (u >= QLatin1Char('0') && u <= QLatin1Char('9'))) {
-        clean += c;
-    } else if (u == QLatin1Char('~')) {
-        clean += QLatin1String("dtor.");
-    } else if (u == QLatin1Char('_')) {
-        clean += QLatin1String("underscore.");
-    } else {
-        clean += QLatin1String("A");
-    }
-
-    for (int i = 1; i < (int) reference.length(); i++) {
-        const QChar c = reference[i];
-        const uint u = c.unicode();
-        if ((u >= QLatin1Char('a') && u <= QLatin1Char('z')) ||
-             (u >= QLatin1Char('A') && u <= QLatin1Char('Z')) ||
-             (u >= QLatin1Char('0') && u <= QLatin1Char('9')) || u == QLatin1Char('-') ||
-             u == QLatin1Char('_') || u == QLatin1Char(':') || u == QLatin1Char('.')) {
-            clean += c;
-        } else if (c.isSpace()) {
-            clean += QLatin1String("-");
-        } else if (u == QLatin1Char('!')) {
-            clean += QLatin1String("-not");
-        } else if (u == QLatin1Char('&')) {
-            clean += QLatin1String("-and");
-        } else if (u == QLatin1Char('<')) {
-            clean += QLatin1String("-lt");
-        } else if (u == QLatin1Char('=')) {
-            clean += QLatin1String("-eq");
-        } else if (u == QLatin1Char('>')) {
-            clean += QLatin1String("-gt");
-        } else if (u == QLatin1Char('#')) {
-            clean += QLatin1String("#");
-        } else {
-            clean += QLatin1String("-");
-            clean += QString::number((int)u, 16);
-        }
-    }
-    return clean;
-}
diff --git a/src/libs/utils/htmldocextractor.h b/src/libs/utils/htmldocextractor.h
index 819e7a5bc9c..67f23df369a 100644
--- a/src/libs/utils/htmldocextractor.h
+++ b/src/libs/utils/htmldocextractor.h
@@ -44,37 +44,22 @@ public:
     void setLengthReference(const int reference, const bool truncateAtParagraph);
     void setFormatContents(const bool format);
 
-    QString getClassOrNamespaceBrief(const QString &html, const QString &name) const;
-    QString getClassOrNamespaceDescription(const QString &html, const QString &name) const;
-    QString getEnumDescription(const QString &html, const QString &name) const;
-    QString getTypedefDescription(const QString &html, const QString &name) const;
-    QString getVarDescription(const QString &html, const QString &name) const;
-    QString getMacroDescription(const QString &html,
-                                const QString &mark,
-                                const QString &anchorName) const;
+    QString getClassOrNamespaceBrief(const QString &html, const QString &mark) const;
+    QString getClassOrNamespaceDescription(const QString &html, const QString &mark) const;
+    QString getEnumDescription(const QString &html, const QString &mark) const;
+    QString getTypedefDescription(const QString &html, const QString &mark) const;
+    QString getMacroDescription(const QString &html, const QString &mark) const;
     QString getFunctionDescription(const QString &html,
                                    const QString &mark,
-                                   const QString &anchorName,
                                    const bool mainOverload = true) const;
 
 private:
-    QString assemble(const QString& elementAttr, const QString &elementTemplate) const;
-    QString getContentsByAnchor(const QString &html,
-                                const QString &name,
-                                const bool relaxedMatch) const;
-    QString getContentsByMarks(const QString &html,
-                               const QString &id,
-                               const bool mainOverload) const;
     QString getClassOrNamespaceMemberDescription(const QString &html,
-                                                 const QString &mark,
-                                                 const QString &anchorName,
-                                                 const bool mainOverload,
-                                                 const bool relaxedMatch = false) const;
-
-    QString findByMarks(const QString &html,
-                        const QString &startMark,
-                        const QString &endMark) const;
-    QString findByPattern(const QString &html, const QString &pattern) const;
+                                                 const QString &startMark,
+                                                 const QString &endMark) const;
+    QString getContentsByMarks(const QString &html,
+                               QString startMark,
+                               QString endMark) const;
 
     void formatContents(QString *html) const;
 
@@ -91,8 +76,6 @@ private:
     static void replaceTablesForSimpleLines(QString *html);
     static void replaceListsForSimpleLines(QString *html);
 
-    static QString cleanReference(const QString &reference);
-
     int m_lengthReference;
     bool m_truncateAtParagraph;
     bool m_formatContents;
diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp
index ecb2cecc957..a8848e8752b 100644
--- a/src/plugins/cppeditor/cpphoverhandler.cpp
+++ b/src/plugins/cppeditor/cpphoverhandler.cpp
@@ -63,14 +63,6 @@ using namespace CPlusPlus;
 using namespace Core;
 
 namespace {
-    QString removeQualificationIfAny(const QString &name) {
-        int index = name.lastIndexOf(QLatin1Char(':'));
-        if (index == -1)
-            return name;
-        else
-            return name.right(name.length() - index - 1);
-    }
-
     void moveCursorToEndOfName(QTextCursor *tc) {
         QTextDocument *doc = tc->document();
         if (!doc)
@@ -334,33 +326,43 @@ void CppHoverHandler::handleLookupItemMatch(const LookupItem &lookupItem,
             }
         }
 
-        HelpCandidate::Category helpCategory;
+        HelpCandidate::Category helpCategory = HelpCandidate::Unknown;
         if (matchingDeclaration->isNamespace() ||
             matchingDeclaration->isClass() ||
             matchingDeclaration->isForwardClassDeclaration()) {
             helpCategory = HelpCandidate::ClassOrNamespace;
-        } else if (matchingDeclaration->isEnum()) {
+        } else if (matchingDeclaration->isEnum() ||
+                   matchingDeclaration->enclosingSymbol()->isEnum()) {
             helpCategory = HelpCandidate::Enum;
         } else if (matchingDeclaration->isTypedef()) {
             helpCategory = HelpCandidate::Typedef;
-        } else if (matchingDeclaration->isStatic() &&
-                   !matchingDeclaration->type()->isFunctionType()) {
-            helpCategory = HelpCandidate::Var;
-        } else {
+        } else if (matchingDeclaration->isFunction() ||
+                  (matchingType.isValid() && matchingType->isFunctionType())){
             helpCategory = HelpCandidate::Function;
         }
 
-        // Help identifiers are simply the name with no signature, arguments or return type.
-        // They might or might not include a qualification. This is why two candidates are created.
-        overview.setShowArgumentNames(false);
-        overview.setShowReturnTypes(false);
-        overview.setShowFunctionSignatures(false);
-        const QString &simpleName = overview.prettyName(matchingDeclaration->name());
-        overview.setShowFunctionSignatures(true);
-        const QString &specifierId = overview.prettyType(matchingType, simpleName);
-
-        m_helpCandidates.append(HelpCandidate(simpleName, specifierId, helpCategory));
-        m_helpCandidates.append(HelpCandidate(qualifiedName, specifierId, helpCategory));
+        if (helpCategory != HelpCandidate::Unknown) {
+            // Help identifiers are simply the name with no signature, arguments or return type.
+            // They might or might not include a qualification. So two candidates are created.
+            overview.setShowArgumentNames(false);
+            overview.setShowReturnTypes(false);
+            overview.setShowFunctionSignatures(false);
+            const QString &simpleName = overview.prettyName(matchingDeclaration->name());
+
+            QString mark;
+            if (matchingType.isValid() && matchingType->isFunctionType()) {
+                overview.setShowFunctionSignatures(true);
+                mark = overview.prettyType(matchingType, simpleName);
+            } else if (matchingDeclaration->enclosingSymbol()->isEnum()) {
+                Symbol *enumSymbol = matchingDeclaration->enclosingSymbol()->asEnum();
+                mark = overview.prettyName(enumSymbol->name());
+            } else {
+                mark = simpleName;
+            }
+
+            m_helpCandidates.append(HelpCandidate(simpleName, mark, helpCategory));
+            m_helpCandidates.append(HelpCandidate(qualifiedName, mark, helpCategory));
+        }
     }
 }
 
@@ -395,30 +397,25 @@ QString CppHoverHandler::getDocContents(const HelpCandidate &help) const
     QMap<QString, QUrl> helpLinks =
         Core::HelpManager::instance()->linksForIdentifier(help.m_helpId);
     foreach (const QUrl &url, helpLinks) {
-        // The help id might or might not be qualified. But anchors and marks are not qualified.
-        const QString &name = removeQualificationIfAny(help.m_helpId);
         const QByteArray &html = Core::HelpManager::instance()->fileData(url);
         switch (help.m_category) {
         case HelpCandidate::Brief:
-            contents = m_htmlDocExtractor.getClassOrNamespaceBrief(html, name);
+            contents = m_htmlDocExtractor.getClassOrNamespaceBrief(html, help.m_docMark);
             break;
         case HelpCandidate::ClassOrNamespace:
-            contents = m_htmlDocExtractor.getClassOrNamespaceDescription(html, name);
+            contents = m_htmlDocExtractor.getClassOrNamespaceDescription(html, help.m_docMark);
             break;
         case HelpCandidate::Function:
-            contents = m_htmlDocExtractor.getFunctionDescription(html, help.m_markId, name);
+            contents = m_htmlDocExtractor.getFunctionDescription(html, help.m_docMark);
             break;
         case HelpCandidate::Enum:
-            contents = m_htmlDocExtractor.getEnumDescription(html, name);
+            contents = m_htmlDocExtractor.getEnumDescription(html, help.m_docMark);
             break;
         case HelpCandidate::Typedef:
-            contents = m_htmlDocExtractor.getTypedefDescription(html, name);
-            break;
-        case HelpCandidate::Var:
-            contents = m_htmlDocExtractor.getVarDescription(html, name);
+            contents = m_htmlDocExtractor.getTypedefDescription(html, help.m_docMark);
             break;
         case HelpCandidate::Macro:
-            contents = m_htmlDocExtractor.getMacroDescription(html, help.m_markId, name);
+            contents = m_htmlDocExtractor.getMacroDescription(html, help.m_docMark);
             break;
         default:
             break;
diff --git a/src/plugins/cppeditor/cpphoverhandler.h b/src/plugins/cppeditor/cpphoverhandler.h
index 59c987d6d5f..464fda0e4f9 100644
--- a/src/plugins/cppeditor/cpphoverhandler.h
+++ b/src/plugins/cppeditor/cpphoverhandler.h
@@ -82,17 +82,17 @@ private:
             ClassOrNamespace,
             Enum,
             Typedef,
-            Var,
             Macro,
             Brief,
-            Function
+            Function,
+            Unknown
         };
 
-        HelpCandidate(const QString &helpId, const QString &markId, Category category) :
-            m_helpId(helpId), m_markId(markId), m_category(category)
+        HelpCandidate(const QString &helpId, const QString &docMark, Category category) :
+            m_helpId(helpId), m_docMark(docMark), m_category(category)
         {}
         QString m_helpId;
-        QString m_markId;
+        QString m_docMark;
         Category m_category;
     };
 
-- 
GitLab