diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index a25d67c60d01e100163231f6eef7eeef7012b2ab..9913d2a2897542c015d40d474467ae26fd8d2db8 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -1755,6 +1755,9 @@ void CPPEditor::unCommentSelection() CPPEditor::Link CPPEditor::linkToSymbol(CPlusPlus::Symbol *symbol) { + if (!symbol) + return Link(); + const QString fileName = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()); unsigned line = symbol->line(); diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 40fe6b1e6cc5df9c3bdf8200bcc3860e0dc7a333..994bfacb675f809896cd75b30845581770110934 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -180,6 +180,10 @@ public: void setObjCEnabled(bool onoff); bool isObjCEnabled() const; + bool openLink(const Link &link) { return openCppEditorAt(link); } + + static Link linkToSymbol(CPlusPlus::Symbol *symbol); + Q_SIGNALS: void outlineModelIndexChanged(const QModelIndex &index); @@ -267,13 +271,10 @@ private: void abortRename(); Link findLinkAt(const QTextCursor &, bool resolveTarget = true); - bool openLink(const Link &link) { return openCppEditorAt(link); } bool openCppEditorAt(const Link &); QModelIndex indexForPosition(int line, int column, const QModelIndex &rootIndex = QModelIndex()) const; - static Link linkToSymbol(CPlusPlus::Symbol *symbol); - CppTools::CppModelManagerInterface *m_modelManager; QComboBox *m_outlineCombo; diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index be74639661a83382bbc9eb0a575ac8fd8c8b1772..4e37070300310c130753b89786f49a27a9d14d9c 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -20,8 +20,9 @@ HEADERS += cppplugin.h \ cppsemanticinfo.h \ cppoutline.h \ cppdeclfromdef.h \ - cpplocalsymbols.h - + cpplocalsymbols.h \ + cpptypehierarchy.h \ + cppelementevaluator.h SOURCES += cppplugin.cpp \ cppeditor.cpp \ cpphighlighter.cpp \ @@ -35,8 +36,9 @@ SOURCES += cppplugin.cpp \ cppsemanticinfo.cpp \ cppoutline.cpp \ cppdeclfromdef.cpp \ - cpplocalsymbols.cpp - + cpplocalsymbols.cpp \ + cpptypehierarchy.cpp \ + cppelementevaluator.cpp RESOURCES += cppeditor.qrc - -OTHER_FILES += CppEditor.pluginspec CppEditor.mimetypes.xml +OTHER_FILES += CppEditor.pluginspec \ + CppEditor.mimetypes.xml diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h index 192cd83df5c9448e45eb09b715d3fac6652b2079..8993e03a28df4f9f19cdc7114da6474940c83c21 100644 --- a/src/plugins/cppeditor/cppeditorconstants.h +++ b/src/plugins/cppeditor/cppeditorconstants.h @@ -48,6 +48,9 @@ const char * const FIND_REFERENCES = "CppEditor.FindReferences"; const char * const JUMP_TO_DEFINITION = "CppEditor.JumpToDefinition"; const char * const UPDATE_CODEMODEL = "CppEditor.UpdateCodeModel"; +const char * const TYPE_HIERARCHY_ID = "CppEditor.TypeHierarchy"; +const char * const OPEN_TYPE_HIERARCHY = "CppEditor.OpenTypeHierarchy"; + const char * const HEADER_FILE_TYPE = "CppHeaderFiles"; const char * const SOURCE_FILE_TYPE = "CppSourceFiles"; diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2902051d6acce4384789306cddc1a7be2e93b02b --- /dev/null +++ b/src/plugins/cppeditor/cppelementevaluator.cpp @@ -0,0 +1,500 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cppelementevaluator.h" + +#include <coreplugin/ifile.h> +#include <cpptools/cppmodelmanagerinterface.h> + +#include <FullySpecifiedType.h> +#include <Names.h> +#include <CoreTypes.h> +#include <Scope.h> +#include <Symbol.h> +#include <Symbols.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/Overview.h> +#include <cplusplus/TypeOfExpression.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/LookupItem.h> +#include <cplusplus/Icons.h> + +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QSet> +#include <QtCore/QQueue> + +using namespace CppEditor; +using namespace Internal; +using namespace CPlusPlus; + +namespace { + void moveCursorToEndOfName(QTextCursor *tc) { + QTextDocument *doc = tc->document(); + if (!doc) + return; + + QChar ch = doc->characterAt(tc->position()); + while (ch.isLetterOrNumber() || ch == QLatin1Char('_')) { + tc->movePosition(QTextCursor::NextCharacter); + ch = doc->characterAt(tc->position()); + } + } +} + +CppElementEvaluator::CppElementEvaluator(CPPEditor *editor) : + m_editor(editor), + m_modelManager(CppTools::CppModelManagerInterface::instance()), + m_tc(editor->textCursor()), + m_lookupBaseClasses(false) +{} + +void CppElementEvaluator::setTextCursor(const QTextCursor &tc) +{ m_tc = tc; } + +void CppElementEvaluator::setLookupBaseClasses(const bool lookup) +{ m_lookupBaseClasses = lookup; } + +QSharedPointer<CppElement> CppElementEvaluator::identifyCppElement() +{ + m_element.clear(); + evaluate(); + return m_element; +} + +// @todo: Consider refactoring code from CPPEditor::findLinkAt into here. +void CppElementEvaluator::evaluate() +{ + if (!m_modelManager) + return; + + const Snapshot &snapshot = m_modelManager->snapshot(); + Document::Ptr doc = snapshot.document(m_editor->file()->fileName()); + if (!doc) + return; + + int line = 0; + int column = 0; + const int pos = m_tc.position(); + m_editor->convertPosition(pos, &line, &column); + + if (!matchDiagnosticMessage(doc, line)) { + if (!matchIncludeFile(doc, line) && !matchMacroInUse(doc, pos)) { + moveCursorToEndOfName(&m_tc); + + // Fetch the expression's code + ExpressionUnderCursor expressionUnderCursor; + const QString &expression = expressionUnderCursor(m_tc); + Scope *scope = doc->scopeAt(line, column); + + TypeOfExpression typeOfExpression; + typeOfExpression.init(doc, snapshot); + const QList<LookupItem> &lookupItems = typeOfExpression(expression, scope); + if (lookupItems.isEmpty()) + return; + + const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select best candidate. + handleLookupItemMatch(snapshot, lookupItem, typeOfExpression.context()); + } + } +} + +bool CppElementEvaluator::matchDiagnosticMessage(const CPlusPlus::Document::Ptr &document, + unsigned line) +{ + foreach (const Document::DiagnosticMessage &m, document->diagnosticMessages()) { + if (m.line() == line) { + m_element = QSharedPointer<CppElement>(new CppDiagnosis(m)); + return true; + } + } + return false; +} + +bool CppElementEvaluator::matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line) +{ + foreach (const Document::Include &includeFile, document->includes()) { + if (includeFile.line() == line) { + m_element = QSharedPointer<CppElement>(new CppInclude(includeFile)); + return true; + } + } + return false; +} + +bool CppElementEvaluator::matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos) +{ + foreach (const Document::MacroUse &use, document->macroUses()) { + if (use.contains(pos)) { + const unsigned begin = use.begin(); + const QString &name = use.macro().name(); + if (pos < begin + name.length()) { + m_element = QSharedPointer<CppElement>(new CppMacro(use.macro())); + return true; + } + } + } + return false; +} + +void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot, + const LookupItem &lookupItem, + const LookupContext &context) +{ + Symbol *declaration = lookupItem.declaration(); + if (!declaration) { + const QString &type = Overview().prettyType(lookupItem.type(), QString()); + m_element = QSharedPointer<CppElement>(new Unknown(type)); + } else { + const FullySpecifiedType &type = declaration->type(); + if (declaration->isNamespace()) { + m_element = QSharedPointer<CppElement>(new CppNamespace(declaration)); + } else if (declaration->isClass() || declaration->isForwardClassDeclaration()) { + if (declaration->isForwardClassDeclaration()) + if (Symbol *classDeclaration = snapshot.findMatchingClassDeclaration(declaration)) + declaration = classDeclaration; + CppClass *cppClass = new CppClass(declaration); + if (m_lookupBaseClasses) + cppClass->lookupBases(declaration, context); + m_element = QSharedPointer<CppElement>(cppClass); + } else if (declaration->isEnum() || declaration->scope()->isEnum()) { + m_element = QSharedPointer<CppElement>(new CppEnum(declaration)); + } else if (declaration->isTypedef()) { + m_element = QSharedPointer<CppElement>(new CppTypedef(declaration)); + } else if (declaration->isFunction() || (type.isValid() && type->isFunctionType())) { + m_element = QSharedPointer<CppElement>(new CppFunction(declaration)); + } else if (declaration->isDeclaration() && type.isValid()) { + m_element = QSharedPointer<CppElement>( + new CppVariable(declaration, context, lookupItem.scope())); + } else { + m_element = QSharedPointer<CppElement>(new CppDeclarableElement(declaration)); + } + } +} + +// CppElement +CppElement::CppElement() : m_helpCategory(CppHoverHandler::HelpCandidate::Unknown) +{} + +CppElement::~CppElement() +{} + +void CppElement::setHelpCategory(const CppHoverHandler::HelpCandidate::Category &cat) +{ m_helpCategory = cat; } + +const CppHoverHandler::HelpCandidate::Category &CppElement::helpCategory() const +{ return m_helpCategory; } + +void CppElement::setHelpIdCandidates(const QStringList &candidates) +{ m_helpIdCandidates = candidates; } + +void CppElement::addHelpIdCandidate(const QString &candidate) +{ m_helpIdCandidates.append(candidate); } + +const QStringList &CppElement::helpIdCandidates() const +{ return m_helpIdCandidates; } + +void CppElement::setHelpMark(const QString &mark) +{ m_helpMark = mark; } + +const QString &CppElement::helpMark() const +{ return m_helpMark; } + +void CppElement::setLink(const CPPEditor::Link &link) +{ m_link = link; } + +const CPPEditor::Link &CppElement::link() const +{ return m_link; } + +void CppElement::setTooltip(const QString &tooltip) +{ m_tooltip = tooltip; } + +const QString &CppElement::tooltip() const +{ return m_tooltip; } + + +// Unknown +Unknown::Unknown(const QString &type) : CppElement(), m_type(type) +{ + setTooltip(m_type); +} + +Unknown::~Unknown() +{} + +const QString &Unknown::type() const +{ return m_type; } + +// CppDiagnosis +CppDiagnosis::CppDiagnosis(const Document::DiagnosticMessage &message) : + CppElement(), m_text(message.text()) +{ + setTooltip(m_text); +} + +CppDiagnosis::~CppDiagnosis() +{} + +const QString &CppDiagnosis::text() const +{ return m_text; } + +// CppInclude +CppInclude::~CppInclude() +{} + +CppInclude::CppInclude(const Document::Include &includeFile) : + CppElement(), + m_path(QDir::toNativeSeparators(includeFile.fileName())), + m_fileName(QFileInfo(includeFile.fileName()).fileName()) +{ + setHelpCategory(CppHoverHandler::HelpCandidate::Brief); + setHelpIdCandidates(QStringList(m_fileName)); + setHelpMark(m_fileName); + setLink(CPPEditor::Link(m_path)); + setTooltip(m_path); +} + +const QString &CppInclude::path() const +{ return m_path; } + +const QString &CppInclude::fileName() const +{ return m_fileName; } + +// CppMacro +CppMacro::CppMacro(const Macro ¯o) : CppElement() +{ + setHelpCategory(CppHoverHandler::HelpCandidate::Macro); + setHelpIdCandidates(QStringList(macro.name())); + setHelpMark(macro.name()); + setLink(CPPEditor::Link(macro.fileName(), macro.line())); + setTooltip(macro.toString()); +} + +CppMacro::~CppMacro() +{} + +// CppDeclarableElement +CppDeclarableElement::CppDeclarableElement(Symbol *declaration) : CppElement() +{ + const FullySpecifiedType &type = declaration->type(); + + Overview overview; + overview.setShowArgumentNames(true); + overview.setShowReturnTypes(true); + + m_icon = Icons().iconForSymbol(declaration); + m_name = overview.prettyName(declaration->name()); + if (declaration->scope()->isClass() || + declaration->scope()->isNamespace() || + declaration->scope()->isEnum()) { + m_qualifiedName = overview.prettyName(LookupContext::fullyQualifiedName(declaration)); + } else { + m_qualifiedName = m_name; + } + + if (declaration->isClass() || + declaration->isNamespace() || + declaration->isForwardClassDeclaration() || + declaration->isEnum()) { + m_type = m_qualifiedName; + } else { + m_type = overview.prettyType(type, m_qualifiedName); + } + + setTooltip(m_type); + setLink(CPPEditor::linkToSymbol(declaration)); + + QStringList helpIds; + helpIds << m_name << m_qualifiedName; + setHelpIdCandidates(helpIds); + setHelpMark(m_name); +} + +CppDeclarableElement::~CppDeclarableElement() +{} + +void CppDeclarableElement::setName(const QString &name) +{ m_name = name; } + +const QString &CppDeclarableElement::name() const +{ return m_name; } + +void CppDeclarableElement::setQualifiedName(const QString &name) +{ m_qualifiedName = name; } + +const QString &CppDeclarableElement::qualifiedName() const +{ return m_qualifiedName; } + +void CppDeclarableElement::setType(const QString &type) +{ m_type = type; } + +const QString &CppDeclarableElement::type() const +{ return m_type; } + +void CppDeclarableElement::setIcon(const QIcon &icon) +{ m_icon = icon; } + +const QIcon &CppDeclarableElement::icon() const +{ return m_icon; } + +// CppNamespace +CppNamespace::CppNamespace(Symbol *declaration) : CppDeclarableElement(declaration) +{ + setHelpCategory(CppHoverHandler::HelpCandidate::ClassOrNamespace); +} + +CppNamespace::~CppNamespace() +{} + +// CppClass +CppClass::CppClass(Symbol *declaration) : CppDeclarableElement(declaration) +{ + setHelpCategory(CppHoverHandler::HelpCandidate::ClassOrNamespace); +} + +CppClass::~CppClass() +{} + +void CppClass::lookupBases(Symbol *declaration, const CPlusPlus::LookupContext &context) +{ + typedef QPair<ClassOrNamespace *, CppClass *> Data; + + if (ClassOrNamespace *clazz = context.lookupType(declaration)) { + QSet<ClassOrNamespace *> visited; + + QQueue<Data> q; + q.enqueue(qMakePair(clazz, this)); + while (!q.isEmpty()) { + Data current = q.dequeue(); + clazz = current.first; + visited.insert(clazz); + const QList<ClassOrNamespace *> &bases = clazz->usings(); + foreach (ClassOrNamespace *baseClass, bases) { + const QList<Symbol *> &symbols = baseClass->symbols(); + foreach (Symbol *symbol, symbols) { + if (symbol->isClass() && ( + clazz = context.lookupType(symbol)) && + !visited.contains(clazz)) { + CppClass baseCppClass(symbol); + CppClass *cppClass = current.second; + cppClass->m_bases.append(baseCppClass); + q.enqueue(qMakePair(clazz, &cppClass->m_bases.last())); + } + } + } + } + } +} + +const QList<CppClass> &CppClass::bases() const +{ return m_bases; } + +// CppFunction +CppFunction::CppFunction(Symbol *declaration) : CppDeclarableElement(declaration) +{ + setHelpCategory(CppHoverHandler::HelpCandidate::Function); + + const FullySpecifiedType &type = declaration->type(); + + // Functions marks can be found either by the main overload or signature based + // (with no argument names and no return). Help ids have no signature at all. + Overview overview; + overview.setShowDefaultArguments(false); + setHelpMark(overview.prettyType(type, name())); + + overview.setShowFunctionSignatures(false); + addHelpIdCandidate(overview.prettyName(declaration->name())); +} + +CppFunction::~CppFunction() +{} + +// CppEnum +CppEnum::CppEnum(Symbol *declaration) : CppDeclarableElement(declaration) +{ + setHelpCategory(CppHoverHandler::HelpCandidate::Enum); + + if (declaration->scope()->isEnum()) { + Symbol *enumSymbol = declaration->scope()->asEnum(); + Overview overview; + setHelpMark(overview.prettyName(enumSymbol->name())); + setTooltip(overview.prettyName(LookupContext::fullyQualifiedName(enumSymbol))); + } +} + +CppEnum::~CppEnum() +{} + +// CppTypedef +CppTypedef::CppTypedef(Symbol *declaration) : + CppDeclarableElement(declaration) +{ + setHelpCategory(CppHoverHandler::HelpCandidate::Typedef); +} + +CppTypedef::~CppTypedef() +{} + +// CppVariable +CppVariable::CppVariable(Symbol *declaration, const LookupContext &context, Scope *scope) : + CppDeclarableElement(declaration) +{ + const FullySpecifiedType &type = declaration->type(); + + const Name *typeName = 0; + if (type->isNamedType()) { + typeName = type->asNamedType()->name(); + } else if (type->isPointerType() || type->isReferenceType()) { + FullySpecifiedType associatedType; + if (type->isPointerType()) + associatedType = type->asPointerType()->elementType(); + else + associatedType = type->asReferenceType()->elementType(); + if (associatedType->isNamedType()) + typeName = associatedType->asNamedType()->name(); + } + + if (typeName) { + if (ClassOrNamespace *clazz = context.lookupType(typeName, scope)) { + if (!clazz->symbols().isEmpty()) { + Overview overview; + Symbol *symbol = clazz->symbols().at(0); + const QString &name = + overview.prettyName(LookupContext::fullyQualifiedName(symbol)); + setTooltip(name); + setHelpCategory(CppHoverHandler::HelpCandidate::ClassOrNamespace); + setHelpMark(name); + setHelpIdCandidates(QStringList(name)); + } + } + } +} + +CppVariable::~CppVariable() +{} diff --git a/src/plugins/cppeditor/cppelementevaluator.h b/src/plugins/cppeditor/cppelementevaluator.h new file mode 100644 index 0000000000000000000000000000000000000000..92a454877b25786d682c95668f29ac39ba66f5f6 --- /dev/null +++ b/src/plugins/cppeditor/cppelementevaluator.h @@ -0,0 +1,238 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CPPHIGHLEVELMODEL_H +#define CPPHIGHLEVELMODEL_H + +#include "cppeditor.h" +#include "cpphoverhandler.h" + +#include <cplusplus/CppDocument.h> +#include <cplusplus/Overview.h> + +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QSharedPointer> +#include <QtGui/QTextCursor> +#include <QtGui/QIcon> + +namespace CPlusPlus { +class LookupItem; +class LookupContext; +} + +namespace CppTools { +class CppModelManagerInterface; +} + +namespace CppEditor { +namespace Internal { + +class CPPEditor; +class CppElement; + +class CppElementEvaluator +{ +public: + CppElementEvaluator(CPPEditor *editor); + + void setTextCursor(const QTextCursor &tc); + void setLookupBaseClasses(const bool lookup); + + QSharedPointer<CppElement> identifyCppElement(); + +private: + void evaluate(); + bool matchDiagnosticMessage(const CPlusPlus::Document::Ptr &document, unsigned line); + bool matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line); + bool matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos); + void handleLookupItemMatch(const CPlusPlus::Snapshot &snapshot, + const CPlusPlus::LookupItem &lookupItem, + const CPlusPlus::LookupContext &lookupContext); + + CPPEditor *m_editor; + CppTools::CppModelManagerInterface *m_modelManager; + QTextCursor m_tc; + bool m_lookupBaseClasses; + QSharedPointer<CppElement> m_element; +}; + +class CppElement +{ +public: + virtual ~CppElement(); + + const CppHoverHandler::HelpCandidate::Category &helpCategory() const; + const QStringList &helpIdCandidates() const; + const QString &helpMark() const; + const CPPEditor::Link &link() const; + const QString &tooltip() const; + +protected: + CppElement(); + + void setHelpCategory(const CppHoverHandler::HelpCandidate::Category &category); + void setLink(const CPPEditor::Link &link); + void setTooltip(const QString &tooltip); + void setHelpIdCandidates(const QStringList &candidates); + void addHelpIdCandidate(const QString &candidate); + void setHelpMark(const QString &mark); + +private: + CppHoverHandler::HelpCandidate::Category m_helpCategory; + QStringList m_helpIdCandidates; + QString m_helpMark; + CPPEditor::Link m_link; + QString m_tooltip; +}; + +class Unknown : public CppElement +{ +public: + Unknown(const QString &type); + virtual ~Unknown(); + + const QString &type() const; + +private: + QString m_type; +}; + +class CppDiagnosis : public CppElement +{ +public: + CppDiagnosis(const CPlusPlus::Document::DiagnosticMessage &message); + virtual ~CppDiagnosis(); + + const QString &text() const; + +private: + QString m_text; +}; + +class CppInclude : public CppElement +{ +public: + CppInclude(const CPlusPlus::Document::Include &includeFile); + virtual ~CppInclude(); + + const QString &path() const; + const QString &fileName() const; + +private: + QString m_path; + QString m_fileName; +}; + +class CppMacro : public CppElement +{ +public: + CppMacro(const CPlusPlus::Macro ¯o); + virtual ~CppMacro(); +}; + +class CppDeclarableElement : public CppElement +{ +public: + CppDeclarableElement(CPlusPlus::Symbol *declaration); + virtual ~CppDeclarableElement(); + + const QString &name() const; + const QString &qualifiedName() const; + const QString &type() const; + const QIcon &icon() const; + +protected: + void setName(const QString &name); + void setQualifiedName(const QString &name); + void setType(const QString &type); + void setIcon(const QIcon &icon); + +private: + QString m_name; + QString m_qualifiedName; + QString m_type; + QIcon m_icon; +}; + +class CppNamespace : public CppDeclarableElement +{ +public: + CppNamespace(CPlusPlus::Symbol *declaration); + virtual ~CppNamespace(); +}; + +class CppClass : public CppDeclarableElement +{ +public: + CppClass(CPlusPlus::Symbol *declaration); + virtual ~CppClass(); + + void lookupBases(CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context); + + const QList<CppClass> &bases() const; + +private: + QList<CppClass> m_bases; +}; + +class CppFunction : public CppDeclarableElement +{ +public: + CppFunction(CPlusPlus::Symbol *declaration); + virtual ~CppFunction(); +}; + +class CppEnum : public CppDeclarableElement +{ +public: + CppEnum(CPlusPlus::Symbol *declaration); + virtual ~CppEnum(); +}; + +class CppTypedef : public CppDeclarableElement +{ +public: + CppTypedef(CPlusPlus::Symbol *declaration); + virtual ~CppTypedef(); +}; + +class CppVariable : public CppDeclarableElement +{ +public: + CppVariable(CPlusPlus::Symbol *declaration, + const CPlusPlus::LookupContext &context, + CPlusPlus::Scope *scope); + virtual ~CppVariable(); +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPHIGHLEVELMODEL_H diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp index 0d4d24241b7a65f562c12d3e92e0040fe11b9a54..15922d906ed9d780ddf276709e23e048027eaffe 100644 --- a/src/plugins/cppeditor/cpphoverhandler.cpp +++ b/src/plugins/cppeditor/cpphoverhandler.cpp @@ -29,6 +29,7 @@ #include "cpphoverhandler.h" #include "cppeditor.h" +#include "cppelementevaluator.h" #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/editormanager/editormanager.h> @@ -37,29 +38,9 @@ #include <texteditor/itexteditor.h> #include <texteditor/basetexteditor.h> -#include <FullySpecifiedType.h> -#include <Names.h> -#include <CoreTypes.h> -#include <Scope.h> -#include <Symbol.h> -#include <Symbols.h> -#include <cplusplus/ExpressionUnderCursor.h> -#include <cplusplus/Overview.h> -#include <cplusplus/TypeOfExpression.h> -#include <cplusplus/LookupContext.h> -#include <cplusplus/LookupItem.h> - -#include <QtCore/QSet> -#include <QtCore/QDir> -#include <QtCore/QFileInfo> -#include <QtCore/QtAlgorithms> -#include <QtCore/QStringBuilder> #include <QtGui/QTextCursor> -#include <algorithm> - using namespace CppEditor::Internal; -using namespace CPlusPlus; using namespace Core; namespace { @@ -70,73 +51,13 @@ namespace { else return name.right(name.length() - index - 1); } - - void moveCursorToEndOfName(QTextCursor *tc) { - QTextDocument *doc = tc->document(); - if (!doc) - return; - - QChar ch = doc->characterAt(tc->position()); - while (ch.isLetterOrNumber() || ch == QLatin1Char('_')) { - tc->movePosition(QTextCursor::NextCharacter); - ch = doc->characterAt(tc->position()); - } - } - - void buildClassHierarchyHelper(ClassOrNamespace *classSymbol, - const LookupContext &context, - const Overview &overview, - QList<QStringList> *hierarchy, - QSet<ClassOrNamespace *> *visited) { - visited->insert(classSymbol); - const QList<ClassOrNamespace *> &bases = classSymbol->usings(); - foreach (ClassOrNamespace *baseClass, bases) { - const QList<Symbol *> &symbols = baseClass->symbols(); - foreach (Symbol *baseSymbol, symbols) { - if (baseSymbol->isClass() && ( - classSymbol = context.lookupType(baseSymbol)) && - !visited->contains(classSymbol)) { - const QString &qualifiedName = overview.prettyName( - LookupContext::fullyQualifiedName(baseSymbol)); - if (!qualifiedName.isEmpty()) { - hierarchy->back().append(qualifiedName); - buildClassHierarchyHelper(classSymbol, - context, - overview, - hierarchy, - visited); - hierarchy->append(hierarchy->back()); - hierarchy->back().removeLast(); - } - } - } - } - } - - void buildClassHierarchy(Symbol *symbol, - const LookupContext &context, - const Overview &overview, - QList<QStringList> *hierarchy) { - if (ClassOrNamespace *classSymbol = context.lookupType(symbol)) { - hierarchy->append(QStringList()); - QSet<ClassOrNamespace *> visited; - buildClassHierarchyHelper(classSymbol, context, overview, hierarchy, &visited); - hierarchy->removeLast(); - } - } - - struct ClassHierarchyComp - { - bool operator()(const QStringList &a, const QStringList &b) - { return a.size() < b.size(); } - }; } -CppHoverHandler::CppHoverHandler(QObject *parent) : BaseHoverHandler(parent), m_modelManager(0) -{ - m_modelManager = - ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>(); -} +CppHoverHandler::CppHoverHandler(QObject *parent) : BaseHoverHandler(parent) +{} + +CppHoverHandler::~CppHoverHandler() +{} bool CppHoverHandler::acceptEditor(IEditor *editor) { @@ -148,191 +69,25 @@ bool CppHoverHandler::acceptEditor(IEditor *editor) void CppHoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) { - if (!m_modelManager) - return; - - const Snapshot &snapshot = m_modelManager->snapshot(); - Document::Ptr doc = snapshot.document(editor->file()->fileName()); - if (!doc) + CPPEditor *cppEditor = qobject_cast<CPPEditor *>(editor->widget()); + if (!cppEditor) return; - int line = 0; - int column = 0; - editor->convertPosition(pos, &line, &column); - - if (!matchDiagnosticMessage(doc, line)) { - if (!matchIncludeFile(doc, line) && !matchMacroInUse(doc, pos)) { - TextEditor::BaseTextEditor *baseEditor = baseTextEditor(editor); - if (!baseEditor) - return; - - bool extraSelectionTooltip = false; - if (!baseEditor->extraSelectionTooltip(pos).isEmpty()) { - setToolTip(baseEditor->extraSelectionTooltip(pos)); - extraSelectionTooltip = true; - } - - QTextCursor tc(baseEditor->document()); - tc.setPosition(pos); - moveCursorToEndOfName(&tc); - - // Fetch the expression's code - ExpressionUnderCursor expressionUnderCursor; - const QString &expression = expressionUnderCursor(tc); - Scope *scope = doc->scopeAt(line, column); - - TypeOfExpression typeOfExpression; - typeOfExpression.init(doc, snapshot); - const QList<LookupItem> &lookupItems = typeOfExpression(expression, scope); - if (lookupItems.isEmpty()) - return; - - const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select best candidate. - handleLookupItemMatch(lookupItem, typeOfExpression.context(), !extraSelectionTooltip); - } - } -} - -bool CppHoverHandler::matchDiagnosticMessage(const CPlusPlus::Document::Ptr &document, - unsigned line) -{ - foreach (const Document::DiagnosticMessage &m, document->diagnosticMessages()) { - if (m.line() == line) { - setToolTip(m.text()); - return true; - } - } - return false; -} - -bool CppHoverHandler::matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line) -{ - foreach (const Document::Include &includeFile, document->includes()) { - if (includeFile.line() == line) { - setToolTip(QDir::toNativeSeparators(includeFile.fileName())); - const QString &fileName = QFileInfo(includeFile.fileName()).fileName(); - addHelpCandidate(HelpCandidate(fileName, fileName, HelpCandidate::Brief)); - return true; - } - } - return false; -} - -bool CppHoverHandler::matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos) -{ - foreach (const Document::MacroUse &use, document->macroUses()) { - if (use.contains(pos)) { - const unsigned begin = use.begin(); - const QString &name = use.macro().name(); - if (pos < begin + name.length()) { - setToolTip(use.macro().toString()); - addHelpCandidate(HelpCandidate(name, name, HelpCandidate::Macro)); - return true; - } - } - } - return false; -} - -void CppHoverHandler::handleLookupItemMatch(const LookupItem &lookupItem, - const LookupContext &context, - const bool assignTooltip) -{ - Symbol *matchingDeclaration = lookupItem.declaration(); - FullySpecifiedType matchingType = lookupItem.type(); - - Overview overview; - overview.setShowArgumentNames(true); - overview.setShowReturnTypes(true); - - if (!matchingDeclaration && assignTooltip) { - setToolTip(overview.prettyType(matchingType, QString())); + if (!cppEditor->extraSelectionTooltip(pos).isEmpty()) { + setToolTip(cppEditor->extraSelectionTooltip(pos)); } else { - QString name; - if (matchingDeclaration->scope()->isClass() || - matchingDeclaration->scope()->isNamespace() || - matchingDeclaration->scope()->isEnum()) { - name.append(overview.prettyName( - LookupContext::fullyQualifiedName(matchingDeclaration))); - - if (matchingDeclaration->isClass() || - matchingDeclaration->isForwardClassDeclaration()) { - buildClassHierarchy(matchingDeclaration, context, overview, &m_classHierarchy); - } - } else { - name.append(overview.prettyName(matchingDeclaration->name())); - } - - if (assignTooltip) { - if (matchingDeclaration->isClass() || - matchingDeclaration->isNamespace() || - matchingDeclaration->isForwardClassDeclaration() || - matchingDeclaration->isEnum()) { - setToolTip(name); - } else { - setToolTip(overview.prettyType(matchingType, name)); - } - } - - HelpCandidate::Category helpCategory = HelpCandidate::Unknown; - if (matchingDeclaration->isNamespace() || - matchingDeclaration->isClass() || - matchingDeclaration->isForwardClassDeclaration()) { - helpCategory = HelpCandidate::ClassOrNamespace; - } else if (matchingDeclaration->isEnum() || - matchingDeclaration->scope()->isEnum()) { - helpCategory = HelpCandidate::Enum; - } else if (matchingDeclaration->isTypedef()) { - helpCategory = HelpCandidate::Typedef; - } else if (matchingDeclaration->isFunction() || - (matchingType.isValid() && matchingType->isFunctionType())){ - helpCategory = HelpCandidate::Function; - } else if (matchingDeclaration->isDeclaration() && matchingType.isValid()) { - const Name *typeName = 0; - if (matchingType->isNamedType()) { - typeName = matchingType->asNamedType()->name(); - } else if (matchingType->isPointerType() || matchingType->isReferenceType()) { - FullySpecifiedType type; - if (matchingType->isPointerType()) - type = matchingType->asPointerType()->elementType(); - else - type = matchingType->asReferenceType()->elementType(); - if (type->isNamedType()) - typeName = type->asNamedType()->name(); - } - - if (typeName) { - if (ClassOrNamespace *clazz = context.lookupType(typeName, lookupItem.scope())) { - if (!clazz->symbols().isEmpty()) { - Symbol *symbol = clazz->symbols().at(0); - matchingDeclaration = symbol; - name = overview.prettyName(LookupContext::fullyQualifiedName(symbol)); - setToolTip(name); - buildClassHierarchy(symbol, context, overview, &m_classHierarchy); - helpCategory = HelpCandidate::ClassOrNamespace; - } - } - } - } - - if (helpCategory != HelpCandidate::Unknown) { - QString docMark = overview.prettyName(matchingDeclaration->name()); - - if (matchingType.isValid() && matchingType->isFunctionType()) { - // Functions marks can be found either by the main overload or signature based - // (with no argument names and no return). Help ids have no signature at all. - overview.setShowArgumentNames(false); - overview.setShowReturnTypes(false); - docMark = overview.prettyType(matchingType, docMark); - overview.setShowFunctionSignatures(false); - const QString &functionName = overview.prettyName(matchingDeclaration->name()); - addHelpCandidate(HelpCandidate(functionName, docMark, helpCategory)); - } else if (matchingDeclaration->scope()->isEnum()) { - Symbol *enumSymbol = matchingDeclaration->scope()->asEnum(); - docMark = overview.prettyName(enumSymbol->name()); - } - - addHelpCandidate(HelpCandidate(name, docMark, helpCategory)); + QTextCursor tc(cppEditor->document()); + tc.setPosition(pos); + + CppElementEvaluator evaluator(cppEditor); + evaluator.setTextCursor(tc); + QSharedPointer<CppElement> cppElement = evaluator.identifyCppElement(); + if (!cppElement.isNull()) { + setToolTip(cppElement->tooltip()); + foreach (const QString &helpId, cppElement->helpIdCandidates()) + addHelpCandidate(HelpCandidate(helpId, + cppElement->helpMark(), + cppElement->helpCategory())); } } } @@ -356,122 +111,9 @@ void CppHoverHandler::evaluateHelpCandidates() } void CppHoverHandler::decorateToolTip(TextEditor::ITextEditor *editor) -{ - if (!m_classHierarchy.isEmpty()) - generateDiagramTooltip(extendToolTips(editor)); - else - generateNormalTooltip(extendToolTips(editor)); -} - -void CppHoverHandler::generateDiagramTooltip(const bool extendTooltips) -{ - QString clazz = toolTip(); - - qSort(m_classHierarchy.begin(), m_classHierarchy.end(), ClassHierarchyComp()); - - // Remove duplicates (in case there are any). - m_classHierarchy.erase(std::unique(m_classHierarchy.begin(), m_classHierarchy.end()), - m_classHierarchy.end()); - - QStringList directBaseClasses; - foreach (const QStringList &hierarchy, m_classHierarchy) { - if (hierarchy.size() > 1) - break; - directBaseClasses.append(hierarchy.at(0)); - } - - QString diagram(QLatin1String("<table>")); - for (int i = 0; i < directBaseClasses.size(); ++i) { - if (i == 0) { - diagram.append(QString( - "<tr><td>%1</td><td>" - "<img src=\":/cppeditor/images/rightarrow.png\"></td>" - "<td>%2</td></tr>").arg(toolTip()).arg(directBaseClasses.at(i))); - } else { - diagram.append(QString( - "<tr><td></td><td>" - "<img src=\":/cppeditor/images/larrow.png\"></td>" - "<td>%1</td></tr>").arg(directBaseClasses.at(i))); - } - } - diagram.append(QLatin1String("</table>")); - setToolTip(diagram); - - if (matchingHelpCandidate() != -1) { - appendToolTip(getDocContents(extendTooltips)); - } else { - // Look for documented base classes. Diagram the nearest one or the nearest ones (in - // the case there are many at the same level). - int helpLevel = 0; - QList<int> baseClassesWithHelp; - for (int i = 0; i < m_classHierarchy.size(); ++i) { - const QStringList &hierarchy = m_classHierarchy.at(i); - if (helpLevel != 0 && hierarchy.size() != helpLevel) - break; - - bool exists = false; - QString name = hierarchy.last(); - if (helpIdExists(name)) { - exists = true; - } else { - name = removeClassNameQualification(name); - if (helpIdExists(name)) { - exists = true; - m_classHierarchy[i].last() = name; - } - } - if (exists) { - baseClassesWithHelp.append(i); - if (helpLevel == 0) - helpLevel = hierarchy.size(); - } - } - - if (!baseClassesWithHelp.isEmpty()) { - // Choose the first one as the help match. - QString base = m_classHierarchy.at(baseClassesWithHelp.at(0)).last(); - HelpCandidate help(base, base, HelpCandidate::ClassOrNamespace); - addHelpCandidate(help); - setMatchingHelpCandidate(helpCandidates().size() - 1); - - if (baseClassesWithHelp.size() == 1 && helpLevel == 1) { - appendToolTip(getDocContents(help, extendTooltips)); - } else { - foreach (int hierarchyIndex, baseClassesWithHelp) { - appendToolTip(QLatin1String("<p>")); - const QStringList &hierarchy = m_classHierarchy.at(hierarchyIndex); - Q_ASSERT(helpLevel <= hierarchy.size()); - - // Following contents are inside tables so they are on the exact same - // alignment as the top level diagram. - diagram = QString(QLatin1String("<table><tr><td>%1</td>")).arg(clazz); - for (int i = 0; i < helpLevel; ++i) { - diagram.append( - QLatin1String("<td><img src=\":/cppeditor/images/rightarrow.png\">" - "</td><td>") % - hierarchy.at(i) % - QLatin1String("</td>")); - } - diagram.append(QLatin1String("</tr></table>")); - - base = hierarchy.at(helpLevel - 1); - const QString &contents = - getDocContents(HelpCandidate(base, base, HelpCandidate::Brief), false); - if (!contents.isEmpty()) { - appendToolTip(diagram % QLatin1String("<table><tr><td>") % - contents % QLatin1String("</td></tr></table>")); - } - appendToolTip(QLatin1String("</p>")); - } - } - } - } -} - -void CppHoverHandler::generateNormalTooltip(const bool extendTooltips) { if (matchingHelpCandidate() != -1) { - const QString &contents = getDocContents(extendTooltips); + const QString &contents = getDocContents(extendToolTips(editor)); if (!contents.isEmpty()) { HelpCandidate::Category cat = helpCandidate(matchingHelpCandidate()).m_category; if (cat == HelpCandidate::ClassOrNamespace) @@ -486,8 +128,3 @@ void CppHoverHandler::generateNormalTooltip(const bool extendTooltips) } } } - -void CppHoverHandler::resetExtras() -{ - m_classHierarchy.clear(); -} diff --git a/src/plugins/cppeditor/cpphoverhandler.h b/src/plugins/cppeditor/cpphoverhandler.h index 1fab49b1d2e06658b23a81f3435745e97f02c394..6c7de5cd1fb0e16890968bdf24460f48823ae29f 100644 --- a/src/plugins/cppeditor/cpphoverhandler.h +++ b/src/plugins/cppeditor/cpphoverhandler.h @@ -30,26 +30,14 @@ #ifndef CPPHOVERHANDLER_H #define CPPHOVERHANDLER_H -#include <cplusplus/CppDocument.h> #include <texteditor/basehoverhandler.h> #include <QtCore/QObject> -#include <QtCore/QList> -#include <QtCore/QStringList> - -namespace CPlusPlus { -class LookupItem; -class LookupContext; -} namespace Core { class IEditor; } -namespace CppTools { -class CppModelManagerInterface; -} - namespace TextEditor { class ITextEditor; } @@ -62,26 +50,13 @@ class CppHoverHandler : public TextEditor::BaseHoverHandler Q_OBJECT public: CppHoverHandler(QObject *parent = 0); + virtual ~CppHoverHandler(); private: virtual bool acceptEditor(Core::IEditor *editor); virtual void identifyMatch(TextEditor::ITextEditor *editor, int pos); - virtual void resetExtras(); virtual void evaluateHelpCandidates(); virtual void decorateToolTip(TextEditor::ITextEditor *editor); - - bool matchDiagnosticMessage(const CPlusPlus::Document::Ptr &document, unsigned line); - bool matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line); - bool matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos); - void handleLookupItemMatch(const CPlusPlus::LookupItem &lookupItem, - const CPlusPlus::LookupContext &lookupContext, - const bool assignTooltip); - - void generateDiagramTooltip(const bool extendTooltips); - void generateNormalTooltip(const bool extendTooltips); - - CppTools::CppModelManagerInterface *m_modelManager; - QList<QStringList> m_classHierarchy; }; } // namespace Internal diff --git a/src/plugins/cppeditor/cppplugin.cpp b/src/plugins/cppeditor/cppplugin.cpp index b43bdd980bbd7d4d947e1131a492631bfbdc4568..858d2b7606dd17d0c4c1cea6599af7eda6d2ba89 100644 --- a/src/plugins/cppeditor/cppplugin.cpp +++ b/src/plugins/cppeditor/cppplugin.cpp @@ -36,6 +36,7 @@ #include "cpphoverhandler.h" #include "cppquickfix.h" #include "cppoutline.h" +#include "cpptypehierarchy.h" #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> @@ -46,6 +47,7 @@ #include <coreplugin/actionmanager/command.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/navigationwidget.h> #include <texteditor/completionsupport.h> #include <texteditor/fontsettings.h> #include <texteditor/storagesettings.h> @@ -60,6 +62,7 @@ #include <QtCore/QSettings> #include <QtCore/QTimer> #include <QtCore/QCoreApplication> +#include <QtCore/QStringList> #include <QtGui/QMenu> @@ -139,8 +142,8 @@ CppPlugin::CppPlugin() : m_sortedOutline(false), m_renameSymbolUnderCursorAction(0), m_findUsagesAction(0), - m_updateCodeModelAction(0) - + m_updateCodeModelAction(0), + m_openTypeHierarchyAction(0) { m_instance = this; @@ -205,7 +208,7 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess addAutoReleasedObject(new CppEditorFactory(this)); addAutoReleasedObject(new CppHoverHandler); addAutoReleasedObject(new CppOutlineWidgetFactory); - + addAutoReleasedObject(new CppTypeHierarchyFactory); m_quickFixCollector = new CppQuickFixCollector; addAutoReleasedObject(m_quickFixCollector); @@ -274,6 +277,13 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess contextMenu->addAction(cmd); cppToolsMenu->addAction(cmd); + m_openTypeHierarchyAction = new QAction(tr("Open Type Hierarchy"), this); + cmd = am->registerAction(m_openTypeHierarchyAction, Constants::OPEN_TYPE_HIERARCHY, context); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+T"))); + connect(m_openTypeHierarchyAction, SIGNAL(triggered()), this, SLOT(openTypeHierarchy())); + contextMenu->addAction(cmd); + cppToolsMenu->addAction(cmd); + // Update context in global context Core::Context globalContext(Core::Constants::C_GLOBAL); cppToolsMenu->addAction(createSeparator(am, this, globalContext, CppEditor::Constants::SEPARATOR2)); @@ -391,6 +401,7 @@ void CppPlugin::onTaskStarted(const QString &type) m_renameSymbolUnderCursorAction->setEnabled(false); m_findUsagesAction->setEnabled(false); m_updateCodeModelAction->setEnabled(false); + m_openTypeHierarchyAction->setEnabled(false); } } @@ -400,6 +411,7 @@ void CppPlugin::onAllTasksFinished(const QString &type) m_renameSymbolUnderCursorAction->setEnabled(true); m_findUsagesAction->setEnabled(true); m_updateCodeModelAction->setEnabled(true); + m_openTypeHierarchyAction->setEnabled(true); } } @@ -413,4 +425,15 @@ void CppPlugin::currentEditorChanged(Core::IEditor *editor) } } +void CppPlugin::openTypeHierarchy() +{ + Core::EditorManager *em = Core::EditorManager::instance(); + CPPEditor *editor = qobject_cast<CPPEditor*>(em->currentEditor()->widget()); + if (editor) { + Core::NavigationWidget *navigation = Core::NavigationWidget::instance(); + navigation->activateSubWidget(QLatin1String(Constants::TYPE_HIERARCHY_ID)); + emit typeHierarchyRequested(); + } +} + Q_EXPORT_PLUGIN(CppPlugin) diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h index b72a8a8df0ab324a03fc2e2c02f520e83d495d47..36550b560fc7b89141099dcf46c523d41f51b961 100644 --- a/src/plugins/cppeditor/cppplugin.h +++ b/src/plugins/cppeditor/cppplugin.h @@ -71,6 +71,7 @@ public: signals: void outlineSortingChanged(bool sort); + void typeHierarchyRequested(); public slots: void setSortedOutline(bool sorted); @@ -85,6 +86,7 @@ private slots: void quickFix(TextEditor::ITextEditable *editable); void quickFixNow(); void currentEditorChanged(Core::IEditor *editor); + void openTypeHierarchy(); private: Core::IEditor *createEditor(QWidget *parent); @@ -98,6 +100,7 @@ private: QAction *m_renameSymbolUnderCursorAction; QAction *m_findUsagesAction; QAction *m_updateCodeModelAction; + QAction *m_openTypeHierarchyAction; CppQuickFixCollector *m_quickFixCollector; diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a13265415228aca0fd25afd357758d81c6f70776 --- /dev/null +++ b/src/plugins/cppeditor/cpptypehierarchy.cpp @@ -0,0 +1,267 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cpptypehierarchy.h" +#include "cppeditorconstants.h" +#include "cppeditor.h" +#include "cppelementevaluator.h" +#include "cppplugin.h" + +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/coreconstants.h> +#include <utils/navigationtreeview.h> + +#include <QtCore/QLatin1Char> +#include <QtCore/QLatin1String> +#include <QtCore/QModelIndex> +#include <QtCore/QSettings> +#include <QtGui/QVBoxLayout> +#include <QtGui/QStandardItemModel> +#include <QtGui/QFontMetrics> +#include <QtGui/QApplication> +#include <QtGui/QPainter> +#include <QtGui/QLabel> + +using namespace CppEditor; +using namespace Internal; + +// CppTypeHierarchyItem +CppTypeHierarchyItem::CppTypeHierarchyItem(const CppClass &cppClass) : + QStandardItem(), m_cppClass(cppClass) +{} + +CppTypeHierarchyItem::~CppTypeHierarchyItem() +{} + +int CppTypeHierarchyItem::type() const +{ return UserType; } + +const CppClass &CppTypeHierarchyItem::cppClass() const +{ return m_cppClass; } + +// CppTypeHierarchyDelegate +CppTypeHierarchyDelegate::CppTypeHierarchyDelegate(QObject *parent) : QStyledItemDelegate(parent) +{} + +CppTypeHierarchyDelegate::~CppTypeHierarchyDelegate() +{} + +void CppTypeHierarchyDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (const QStyleOptionViewItemV3 *v3 = + qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option)) { + QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, v3, painter, v3->widget); + + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + const QStandardItemModel *model = static_cast<const QStandardItemModel *>(index.model()); + CppTypeHierarchyItem *item = + static_cast<CppTypeHierarchyItem *>(model->itemFromIndex(index)); + + painter->save(); + const QIcon &icon = item->cppClass().icon(); + const QSize &iconSize = icon.actualSize(opt.decorationSize); + QRect workingRect(opt.rect); + QRect decorationRect(workingRect.topLeft(), iconSize); + icon.paint(painter, decorationRect, opt.decorationAlignment); + workingRect.setX(workingRect.x() + iconSize.width() + 4); + + QRect boundingRect; + const QString &name = item->cppClass().name() + QLatin1Char(' '); + painter->drawText(workingRect, Qt::AlignLeft, name, &boundingRect); + if (item->cppClass().name() != item->cppClass().qualifiedName()) { + QFont font(painter->font()); + if (font.pointSize() > 2) + font.setPointSize(font.pointSize() - 2); + else if (font.pointSize() > 1) + font.setPointSize(font.pointSize() - 1); + font.setItalic(true); + painter->setFont(font); + + QFontMetrics metrics(font); + workingRect.setX(boundingRect.x() + boundingRect.width()); + workingRect.setY(boundingRect.y() + boundingRect.height() - metrics.height()); + painter->drawText(workingRect, Qt::AlignLeft, item->cppClass().qualifiedName()); + } + painter->restore(); + } else { + QStyledItemDelegate::paint(painter, option, index); + } +} + +QSize CppTypeHierarchyDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QSize size = QStyledItemDelegate::sizeHint(option, index); + size.rwidth() += 5; // Extend a bit because of the font processing. + return size; +} + +// CppTypeHierarchyWidget +CppTypeHierarchyWidget::CppTypeHierarchyWidget(Core::IEditor *editor) : + QWidget(0), + m_cppEditor(0), + m_treeView(0), + m_model(0), + m_delegate(0) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + if (CPPEditorEditable *cppEditable = qobject_cast<CPPEditorEditable *>(editor)) { + m_cppEditor = static_cast<CPPEditor *>(cppEditable->widget()); + + m_model = new QStandardItemModel; + m_treeView = new Utils::NavigationTreeView; + m_delegate = new CppTypeHierarchyDelegate; + m_treeView->setModel(m_model); + m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_treeView->setItemDelegate(m_delegate); + layout->addWidget(m_treeView); + + connect(m_treeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onItemClicked(QModelIndex))); + connect(CppPlugin::instance(), SIGNAL(typeHierarchyRequested()), this, SLOT(perform())); + } else { + QLabel *label = new QLabel(tr("No type hierarchy available"), this); + label->setAlignment(Qt::AlignCenter); + label->setAutoFillBackground(true); + label->setBackgroundRole(QPalette::Base); + layout->addWidget(label); + } + setLayout(layout); +} + +CppTypeHierarchyWidget::~CppTypeHierarchyWidget() +{ + delete m_model; + delete m_delegate; +} + +void CppTypeHierarchyWidget::perform() +{ + if (!m_cppEditor) + return; + + m_model->clear(); + + CppElementEvaluator evaluator(m_cppEditor); + evaluator.setLookupBaseClasses(true); + QSharedPointer<CppElement> cppElement = evaluator.identifyCppElement(); + if (!cppElement.isNull()) { + CppElement *element = cppElement.data(); + if (CppClass *cppClass = dynamic_cast<CppClass *>(element)) + buildModel(*cppClass, m_model->invisibleRootItem()); + } +} + +void CppTypeHierarchyWidget::buildModel(const CppClass &cppClass, QStandardItem *parent) +{ + CppTypeHierarchyItem *item = new CppTypeHierarchyItem(cppClass); + parent->appendRow(item); + + // The delegate retrieves data from the item directly. This is to help size hint. + const QString &display = cppClass.name() + cppClass.qualifiedName(); + m_model->setData(m_model->indexFromItem(item), display, Qt::DisplayRole); + m_model->setData(m_model->indexFromItem(item), cppClass.icon(), Qt::DecorationRole); + + foreach (const CppClass &cppBase, cppClass.bases()) + buildModel(cppBase, item); + + m_treeView->expand(m_model->indexFromItem(item)); +} + +void CppTypeHierarchyWidget::onItemClicked(const QModelIndex &index) +{ + if (QStandardItem *item = m_model->itemFromIndex(index)) { + CppTypeHierarchyItem *cppItem = static_cast<CppTypeHierarchyItem *>(item); + m_cppEditor->openLink(cppItem->cppClass().link()); + } +} + +// CppTypeHierarchyStackedWidget +CppTypeHierarchyStackedWidget::CppTypeHierarchyStackedWidget(QWidget *parent) : + QStackedWidget(parent), + m_typeHiearchyWidgetInstance( + new CppTypeHierarchyWidget(Core::EditorManager::instance()->currentEditor())) +{ + addWidget(m_typeHiearchyWidgetInstance); + + connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(editorChanged(Core::IEditor*))); +} + +CppTypeHierarchyStackedWidget::~CppTypeHierarchyStackedWidget() +{ + delete m_typeHiearchyWidgetInstance; +} + +void CppTypeHierarchyStackedWidget::editorChanged(Core::IEditor *editor) +{ + CppTypeHierarchyWidget *replacement = new CppTypeHierarchyWidget(editor); + removeWidget(m_typeHiearchyWidgetInstance); + m_typeHiearchyWidgetInstance->deleteLater(); + m_typeHiearchyWidgetInstance = replacement; + addWidget(m_typeHiearchyWidgetInstance); +} + +// CppTypeHierarchyFactory +CppTypeHierarchyFactory::CppTypeHierarchyFactory() +{} + +CppTypeHierarchyFactory::~CppTypeHierarchyFactory() +{} + +QString CppTypeHierarchyFactory::displayName() const +{ + return tr("Type Hierarchy"); +} + +QString CppTypeHierarchyFactory::id() const +{ + return QLatin1String(Constants::TYPE_HIERARCHY_ID); +} + +QKeySequence CppTypeHierarchyFactory::activationSequence() const +{ + return QKeySequence(); +} + +Core::NavigationView CppTypeHierarchyFactory::createWidget() +{ + CppTypeHierarchyStackedWidget *w = new CppTypeHierarchyStackedWidget; + static_cast<CppTypeHierarchyWidget *>(w->currentWidget())->perform(); + Core::NavigationView navigationView; + navigationView.widget = w; + return navigationView; +} diff --git a/src/plugins/cppeditor/cpptypehierarchy.h b/src/plugins/cppeditor/cpptypehierarchy.h new file mode 100644 index 0000000000000000000000000000000000000000..8bc2c192a519d87e04feff7071969cd2fa273aa9 --- /dev/null +++ b/src/plugins/cppeditor/cpptypehierarchy.h @@ -0,0 +1,142 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CPPTYPEHIERARCHY_H +#define CPPTYPEHIERARCHY_H + +#include "cppelementevaluator.h" + +#include <coreplugin/inavigationwidgetfactory.h> + +#include <QtCore/QString> +#include <QtGui/QWidget> +#include <QtGui/QStackedWidget> +#include <QtGui/QStandardItem> +#include <QtGui/QStyledItemDelegate> + +QT_BEGIN_NAMESPACE +class QStandardItemModel; +class QModelIndex; +class QPainter; +QT_END_NAMESPACE + +namespace Core { +class IEditor; +} + +namespace Utils { +class NavigationTreeView; +} + +namespace CppEditor { +namespace Internal { + +class CPPEditor; + +class CppTypeHierarchyItem : public QStandardItem +{ +public: + CppTypeHierarchyItem(const CppClass &cppClass); + virtual ~CppTypeHierarchyItem(); + + virtual int type() const; + + const CppClass &cppClass() const; + +private: + CppClass m_cppClass; +}; + +class CppTypeHierarchyDelegate : public QStyledItemDelegate +{ +public: + CppTypeHierarchyDelegate(QObject *parent = 0); + virtual ~CppTypeHierarchyDelegate(); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +class CppTypeHierarchyWidget : public QWidget +{ + Q_OBJECT +public: + CppTypeHierarchyWidget(Core::IEditor *editor); + virtual ~CppTypeHierarchyWidget(); + +public slots: + void perform(); + +private slots: + void onItemClicked(const QModelIndex &index); + +private: + void buildModel(const CppClass &cppClass, QStandardItem *item); + + CPPEditor *m_cppEditor; + Utils::NavigationTreeView *m_treeView; + QStandardItemModel *m_model; + CppTypeHierarchyDelegate *m_delegate; +}; + +// @todo: Pretty much the same design as the OutlineWidgetStack. Maybe we can generalize the +// outline factory so that it works for different widgets that support the same editor. +class CppTypeHierarchyStackedWidget : public QStackedWidget +{ + Q_OBJECT +public: + CppTypeHierarchyStackedWidget(QWidget *parent = 0); + virtual ~CppTypeHierarchyStackedWidget(); + +public slots: + void editorChanged(Core::IEditor* editor); + +private: + CppTypeHierarchyWidget *m_typeHiearchyWidgetInstance; +}; + +class CppTypeHierarchyFactory : public Core::INavigationWidgetFactory +{ + Q_OBJECT +public: + CppTypeHierarchyFactory(); + virtual ~CppTypeHierarchyFactory(); + + virtual QString displayName() const; + virtual QString id() const; + virtual QKeySequence activationSequence() const; + virtual Core::NavigationView createWidget(); +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPTYPEHIERARCHY_H diff --git a/src/plugins/texteditor/basehoverhandler.cpp b/src/plugins/texteditor/basehoverhandler.cpp index d993bb124664312ff7eb8667d65974bc29bda0e7..21aaa6368756c7fdf5a591f512a463cc3a5ded98 100644 --- a/src/plugins/texteditor/basehoverhandler.cpp +++ b/src/plugins/texteditor/basehoverhandler.cpp @@ -95,6 +95,10 @@ void BaseHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint void BaseHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos) { + BaseTextEditor *baseEditor = baseTextEditor(editor); + if (!baseEditor) + return; + // If the tooltip is visible and there is a help match, this match is used to update // the help id. Otherwise, let the identification process happen. if (!ToolTip::instance()->isVisible() || m_matchingHelpCandidate == -1) diff --git a/src/plugins/texteditor/basehoverhandler.h b/src/plugins/texteditor/basehoverhandler.h index 587f257210bd9ba5837a94a2fdc545b898dc1489..737b44660fd95cec0a99638312143e17392abdeb 100644 --- a/src/plugins/texteditor/basehoverhandler.h +++ b/src/plugins/texteditor/basehoverhandler.h @@ -57,19 +57,6 @@ class TEXTEDITOR_EXPORT BaseHoverHandler : public QObject public: BaseHoverHandler(QObject *parent = 0); -private slots: - void editorOpened(Core::IEditor *editor); - -public slots: - void showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos); - void updateContextHelpId(TextEditor::ITextEditor *editor, int pos); - -protected: - void setToolTip(const QString &tooltip); - const QString &toolTip() const; - void appendToolTip(const QString &extension); - void addF1ToToolTip(); - struct HelpCandidate { enum Category { @@ -94,6 +81,19 @@ protected: Category m_category; }; +private slots: + void editorOpened(Core::IEditor *editor); + +public slots: + void showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos); + void updateContextHelpId(TextEditor::ITextEditor *editor, int pos); + +protected: + void setToolTip(const QString &tooltip); + const QString &toolTip() const; + void appendToolTip(const QString &extension); + void addF1ToToolTip(); + void addHelpCandidate(const HelpCandidate &helpCandidate); void setHelpCandidate(const HelpCandidate &helpCandidate, int index); const QList<HelpCandidate> &helpCandidates() const; diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index afa439ed9fde7c8eae08a9b10cc187e015f92fc4..121ba012cde48dc6c227b2ce21c3b3d5eb6577a5 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -463,6 +463,7 @@ protected: static void countBrackets(QTextCursor cursor, int from, int end, QChar open, QChar close, int *errors, int *stillopen); +public: struct Link { Link(const QString &fileName = QString(), @@ -489,6 +490,7 @@ protected: int column; // Target column }; +protected: /*! Reimplement this function to enable code navigation.