diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp index 0178ab60557..977c890ce7a 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.cpp +++ b/src/plugins/cppeditor/cpptypehierarchy.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ QStandardItem *itemForClass(const CppClass &cppClass) item->setData(cppClass.qualifiedName, AnnotationRole); item->setData(cppClass.icon, Qt::DecorationRole); QVariant link; - link.setValue(Utils::Link(cppClass.link)); + link.setValue(Link(cppClass.link)); item->setData(link, LinkRole); return item; } @@ -76,7 +77,7 @@ QStandardItem *itemForClass(const CppClass &cppClass) QList sortClasses(const QList &cppClasses) { QList sorted = cppClasses; - Utils::sort(sorted, [](const CppClass &c1, const CppClass &c2) -> bool { + sort(sorted, [](const CppClass &c1, const CppClass &c2) -> bool { const QString key1 = c1.name + QLatin1String("::") + c1.qualifiedName; const QString key2 = c2.name + QLatin1String("::") + c2.qualifiedName; return key1 < key2; @@ -86,6 +87,49 @@ QList sortClasses(const QList &cppClasses) } // Anonymous +class CppTypeHierarchyTreeView : public NavigationTreeView +{ + Q_OBJECT +public: + CppTypeHierarchyTreeView(QWidget *parent); + + void contextMenuEvent(QContextMenuEvent *event) override; +}; + + +CppTypeHierarchyTreeView::CppTypeHierarchyTreeView(QWidget *parent) : + NavigationTreeView(parent) +{ +} + +void CppTypeHierarchyTreeView::contextMenuEvent(QContextMenuEvent *event) +{ + if (!event) + return; + + QMenu contextMenu; + + QAction *action = contextMenu.addAction(tr("Open in Editor")); + connect(action, &QAction::triggered, this, [this] () { + emit activated(currentIndex()); + }); + action = contextMenu.addAction(tr("Open Type Hierarchy")); + connect(action, &QAction::triggered, this, [this] () { + emit doubleClicked(currentIndex()); + }); + + contextMenu.addSeparator(); + + action = contextMenu.addAction(tr("Expand All")); + connect(action, &QAction::triggered, this, &QTreeView::expandAll); + action = contextMenu.addAction(tr("Collapse All")); + connect(action, &QAction::triggered, this, &QTreeView::collapseAll); + + contextMenu.exec(event->globalPos()); + + event->accept(); +} + namespace CppEditor { namespace Internal { @@ -95,12 +139,13 @@ CppTypeHierarchyWidget::CppTypeHierarchyWidget() m_inspectedClass = new TextEditor::TextEditorLinkLabel(this); m_inspectedClass->setContentsMargins(5, 5, 5, 5); m_model = new CppTypeHierarchyModel(this); - m_treeView = new NavigationTreeView(this); + m_treeView = new CppTypeHierarchyTreeView(this); m_treeView->setActivationMode(SingleClickActivation); m_delegate = new AnnotatedItemDelegate(this); m_delegate->setDelimiter(QLatin1String(" ")); m_delegate->setAnnotationRole(AnnotationRole); m_treeView->setModel(m_model); + m_treeView->setExpandsOnDoubleClick(false); m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_treeView->setItemDelegate(m_delegate); m_treeView->setRootIsDecorated(false); @@ -108,6 +153,7 @@ CppTypeHierarchyWidget::CppTypeHierarchyWidget() m_treeView->setDragDropMode(QAbstractItemView::DragOnly); m_treeView->setDefaultDropAction(Qt::MoveAction); connect(m_treeView, &QTreeView::activated, this, &CppTypeHierarchyWidget::onItemActivated); + connect(m_treeView, &QTreeView::doubleClicked, this, &CppTypeHierarchyWidget::onItemDoubleClicked); m_infoLabel = new QLabel(this); m_infoLabel->setAlignment(Qt::AlignCenter); @@ -179,6 +225,26 @@ void CppTypeHierarchyWidget::perform() Core::ProgressManager::addTask(m_future, tr("Evaluating Type Hierarchy"), "TypeHierarchy"); } +void CppTypeHierarchyWidget::performFromExpression(const QString &expression, const QString &fileName) +{ + if (m_future.isRunning()) + m_future.cancel(); + + updateSynchronizer(); + + showProgress(); + + CppElementEvaluator evaluator(nullptr); + evaluator.setLookupBaseClasses(true); + evaluator.setLookupDerivedClasses(true); + evaluator.setExpression(expression, fileName); + m_future = evaluator.asyncExpressionExecute(); + m_futureWatcher.setFuture(QFuture(m_future)); + m_synchronizer.addFuture(QFuture(m_future)); + + Core::ProgressManager::addTask(m_future, tr("Evaluating Type Hierarchy"), "TypeHierarchy"); +} + void CppTypeHierarchyWidget::displayHierarchy() { updateSynchronizer(); @@ -240,7 +306,7 @@ void CppTypeHierarchyWidget::showProgress() { m_infoLabel->setText(tr("Evaluating type hierarchy...")); if (!m_progressIndicator) { - m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Large); + m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Large); m_progressIndicator->attachToWidget(this); } m_progressIndicator->show(); @@ -258,14 +324,36 @@ void CppTypeHierarchyWidget::clearTypeHierarchy() m_model->clear(); } +static QString getExpression(const QModelIndex &index) +{ + const QString annotation = index.data(AnnotationRole).toString(); + if (!annotation.isEmpty()) + return annotation; + return index.data(Qt::DisplayRole).toString(); +} + void CppTypeHierarchyWidget::onItemActivated(const QModelIndex &index) { - auto link = index.data(LinkRole).value(); + auto link = index.data(LinkRole).value(); + if (!link.hasValidTarget()) + return; + + const Link updatedLink = CppElementEvaluator::linkFromExpression( + getExpression(index), link.targetFileName); + if (updatedLink.hasValidTarget()) + link = updatedLink; + + Core::EditorManager::openEditorAt(link.targetFileName, + link.targetLine, + link.targetColumn, + Constants::CPPEDITOR_ID); +} + +void CppTypeHierarchyWidget::onItemDoubleClicked(const QModelIndex &index) +{ + const auto link = index.data(LinkRole).value(); if (link.hasValidTarget()) - Core::EditorManager::openEditorAt(link.targetFileName, - link.targetLine, - link.targetColumn, - Constants::CPPEDITOR_ID); + performFromExpression(getExpression(index), link.targetFileName); } // CppTypeHierarchyFactory @@ -307,7 +395,7 @@ QMimeData *CppTypeHierarchyModel::mimeData(const QModelIndexList &indexes) const auto data = new DropMimeData; data->setOverrideFileDropAction(Qt::CopyAction); // do not remove the item from the model foreach (const QModelIndex &index, indexes) { - auto link = index.data(LinkRole).value(); + auto link = index.data(LinkRole).value(); if (link.hasValidTarget()) data->addFile(link.targetFileName, link.targetLine, link.targetColumn); } @@ -317,3 +405,4 @@ QMimeData *CppTypeHierarchyModel::mimeData(const QModelIndexList &indexes) const } // namespace Internal } // namespace CppEditor +#include "cpptypehierarchy.moc" diff --git a/src/plugins/cppeditor/cpptypehierarchy.h b/src/plugins/cppeditor/cpptypehierarchy.h index 94414db895c..8972bea7463 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.h +++ b/src/plugins/cppeditor/cpptypehierarchy.h @@ -84,6 +84,7 @@ private slots: private: typedef QList CppTools::CppClass::*HierarchyMember; + void performFromExpression(const QString &expression, const QString &fileName); void buildHierarchy(const CppTools::CppClass &cppClass, QStandardItem *parent, bool isRoot, HierarchyMember member); void showNoTypeHierarchyLabel(); @@ -92,6 +93,7 @@ private: void hideProgress(); void clearTypeHierarchy(); void onItemActivated(const QModelIndex &index); + void onItemDoubleClicked(const QModelIndex &index); void updateSynchronizer(); CppEditorWidget *m_cppEditor = nullptr; diff --git a/src/plugins/cpptools/cppelementevaluator.cpp b/src/plugins/cpptools/cppelementevaluator.cpp index 27c6f25ad07..5d1cca68ffc 100644 --- a/src/plugins/cpptools/cppelementevaluator.cpp +++ b/src/plugins/cpptools/cppelementevaluator.cpp @@ -350,7 +350,7 @@ public: CppElementEvaluator::CppElementEvaluator(TextEditor::TextEditorWidget *editor) : m_editor(editor), m_modelManager(CppModelManager::instance()), - m_tc(editor->textCursor()), + m_tc(editor ? editor->textCursor() : QTextCursor()), m_lookupBaseClasses(false), m_lookupDerivedClasses(false) {} @@ -364,6 +364,12 @@ void CppElementEvaluator::setLookupBaseClasses(const bool lookup) void CppElementEvaluator::setLookupDerivedClasses(const bool lookup) { m_lookupDerivedClasses = lookup; } +void CppElementEvaluator::setExpression(const QString &expression, const QString &fileName) +{ + m_expression = expression; + m_fileName = fileName; +} + // special case for bug QTCREATORBUG-4780 static bool shouldOmitElement(const LookupItem &lookupItem, const Scope *scope) { @@ -373,12 +379,17 @@ static bool shouldOmitElement(const LookupItem &lookupItem, const Scope *scope) void CppElementEvaluator::execute() { - execute(&CppElementEvaluator::syncExec); + execute(&CppElementEvaluator::sourceDataFromGui, &CppElementEvaluator::syncExec); } QFuture> CppElementEvaluator::asyncExecute() { - return execute(&CppElementEvaluator::asyncExec); + return execute(&CppElementEvaluator::sourceDataFromGui, &CppElementEvaluator::asyncExec); +} + +QFuture> CppElementEvaluator::asyncExpressionExecute() +{ + return execute(&CppElementEvaluator::sourceDataFromExpression, &CppElementEvaluator::asyncExec); } static QFuture> createFinishedFuture() @@ -389,18 +400,12 @@ static QFuture> createFinishedFuture() return futureInterface.future(); } -// @todo: Consider refactoring code from CppEditor::findLinkAt into here. -QFuture> CppElementEvaluator::execute(ExecFunction execFuntion) +bool CppElementEvaluator::sourceDataFromGui(const CPlusPlus::Snapshot &snapshot, + Document::Ptr &doc, Scope **scope, QString &expression) { - clear(); - - if (!m_modelManager) - return createFinishedFuture(); - - const Snapshot &snapshot = m_modelManager->snapshot(); - Document::Ptr doc = snapshot.document(m_editor->textDocument()->filePath()); + doc = snapshot.document(m_editor->textDocument()->filePath()); if (!doc) - return createFinishedFuture(); + return false; int line = 0; int column = 0; @@ -410,14 +415,44 @@ QFuture> CppElementEvaluator::execute(ExecFunction ex checkDiagnosticMessage(pos); if (matchIncludeFile(doc, line) || matchMacroInUse(doc, pos)) - return createFinishedFuture(); + return false; CppTools::moveCursorToEndOfIdentifier(&m_tc); + ExpressionUnderCursor expressionUnderCursor(doc->languageFeatures()); + expression = expressionUnderCursor(m_tc); // Fetch the expression's code - ExpressionUnderCursor expressionUnderCursor(doc->languageFeatures()); - const QString &expression = expressionUnderCursor(m_tc); - Scope *scope = doc->scopeAt(line, column - 1); + *scope = doc->scopeAt(line, column - 1); + return true; +} + +bool CppElementEvaluator::sourceDataFromExpression(const CPlusPlus::Snapshot &snapshot, + Document::Ptr &doc, Scope **scope, QString &expression) +{ + doc = snapshot.document(m_fileName); + expression = m_expression; + + // Fetch the expression's code + *scope = doc->globalNamespace(); + return true; +} + +// @todo: Consider refactoring code from CppEditor::findLinkAt into here. +QFuture> CppElementEvaluator::execute(SourceFunction sourceFunction, + ExecFunction execFuntion) +{ + clear(); + + if (!m_modelManager) + return createFinishedFuture(); + + const Snapshot &snapshot = m_modelManager->snapshot(); + + Document::Ptr doc; + QString expression; + Scope *scope = nullptr; + if (!std::invoke(sourceFunction, this, snapshot, doc, &scope, expression)) + return createFinishedFuture(); TypeOfExpression typeOfExpression; typeOfExpression.init(doc, snapshot); @@ -589,4 +624,28 @@ void CppElementEvaluator::clear() m_diagnosis.clear(); } +Utils::Link CppElementEvaluator::linkFromExpression(const QString &expression, const QString &fileName) +{ + const Snapshot &snapshot = CppModelManager::instance()->snapshot(); + Document::Ptr doc = snapshot.document(fileName); + Scope *scope = doc->globalNamespace(); + + TypeOfExpression typeOfExpression; + typeOfExpression.init(doc, snapshot); + typeOfExpression.setExpandTemplates(true); + const QList &lookupItems = typeOfExpression(expression.toUtf8(), scope); + if (lookupItems.isEmpty()) + return Utils::Link(); + + for (const LookupItem &item : lookupItems) { + Symbol *symbol = item.declaration(); + if (!symbol) + continue; + if (!symbol->isClass() && !symbol->isTemplate()) + continue; + return symbol->toLink(); + } + return Utils::Link(); +} + } // namespace CppTools diff --git a/src/plugins/cpptools/cppelementevaluator.h b/src/plugins/cpptools/cppelementevaluator.h index 08ded2c6cb3..51c4ce28fff 100644 --- a/src/plugins/cpptools/cppelementevaluator.h +++ b/src/plugins/cpptools/cppelementevaluator.h @@ -56,25 +56,41 @@ public: void setTextCursor(const QTextCursor &tc); void setLookupBaseClasses(const bool lookup); void setLookupDerivedClasses(const bool lookup); + void setExpression(const QString &expression, const QString &fileName); void execute(); QFuture> asyncExecute(); + QFuture> asyncExpressionExecute(); bool identifiedCppElement() const; const QSharedPointer &cppElement() const; bool hasDiagnosis() const; const QString &diagnosis() const; + static Utils::Link linkFromExpression(const QString &expression, const QString &fileName); + private: void clear(); using ExecFunction = QFuture>(CppElementEvaluator::*) (const CPlusPlus::Snapshot &, const CPlusPlus::LookupItem &, const CPlusPlus::LookupContext &); + using SourceFunction = bool(CppElementEvaluator::*) + (const CPlusPlus::Snapshot &, CPlusPlus::Document::Ptr &, + CPlusPlus::Scope **, QString &); - QFuture> execute(ExecFunction execFuntion); + QFuture> execute(SourceFunction sourceFunction, + ExecFunction execFuntion); QFuture> syncExec(const CPlusPlus::Snapshot &, const CPlusPlus::LookupItem &, const CPlusPlus::LookupContext &); QFuture> asyncExec(const CPlusPlus::Snapshot &, const CPlusPlus::LookupItem &, const CPlusPlus::LookupContext &); + bool sourceDataFromGui(const CPlusPlus::Snapshot &snapshot, + CPlusPlus::Document::Ptr &doc, + CPlusPlus::Scope **scope, + QString &expression); + bool sourceDataFromExpression(const CPlusPlus::Snapshot &snapshot, + CPlusPlus::Document::Ptr &doc, + CPlusPlus::Scope **scope, + QString &expression); void checkDiagnosticMessage(int pos); bool matchIncludeFile(const CPlusPlus::Document::Ptr &document, int line); bool matchMacroInUse(const CPlusPlus::Document::Ptr &document, int pos); @@ -82,6 +98,8 @@ private: TextEditor::TextEditorWidget *m_editor; CppTools::CppModelManager *m_modelManager; QTextCursor m_tc; + QString m_expression; + QString m_fileName; bool m_lookupBaseClasses; bool m_lookupDerivedClasses; QSharedPointer m_element;