TextEditor: Add type hierarchy infrastructure

We want to support more than one back-end in the future.

Task-number: QTCREATORBUG-28116
Change-Id: I72020c94b36072a297e13f44130e5e2482922cd4
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2024-03-05 10:33:50 +01:00
parent f5de774d77
commit 3f457c158f
14 changed files with 238 additions and 56 deletions

View File

@@ -22,9 +22,6 @@ const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup";
const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel"; const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel";
const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel"; const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel";
const char TYPE_HIERARCHY_ID[] = "CppEditor.TypeHierarchy";
const char OPEN_TYPE_HIERARCHY[] = "CppEditor.OpenTypeHierarchy";
const char INCLUDE_HIERARCHY_ID[] = "CppEditor.IncludeHierarchy"; const char INCLUDE_HIERARCHY_ID[] = "CppEditor.IncludeHierarchy";
const char OPEN_INCLUDE_HIERARCHY[] = "CppEditor.OpenIncludeHierarchy"; const char OPEN_INCLUDE_HIERARCHY[] = "CppEditor.OpenIncludeHierarchy";

View File

@@ -144,6 +144,7 @@ public:
| TextEditorActionHandler::FollowSymbolUnderCursor | TextEditorActionHandler::FollowSymbolUnderCursor
| TextEditorActionHandler::FollowTypeUnderCursor | TextEditorActionHandler::FollowTypeUnderCursor
| TextEditorActionHandler::RenameSymbol | TextEditorActionHandler::RenameSymbol
| TextEditorActionHandler::TypeHierarchy
| TextEditorActionHandler::FindUsage); | TextEditorActionHandler::FindUsage);
} }
}; };
@@ -358,6 +359,7 @@ void CppEditorPlugin::addPerSymbolActions()
setupCppTypeHierarchy(); setupCppTypeHierarchy();
addSymbolActionToMenus(TextEditor::Constants::OPEN_TYPE_HIERARCHY);
addSymbolActionToMenus(TextEditor::Constants::OPEN_CALL_HIERARCHY); addSymbolActionToMenus(TextEditor::Constants::OPEN_CALL_HIERARCHY);
// Refactoring sub-menu // Refactoring sub-menu

View File

@@ -4,6 +4,7 @@
#include "cpptypehierarchy.h" #include "cpptypehierarchy.h"
#include "cppeditorconstants.h" #include "cppeditorconstants.h"
#include "cppeditordocument.h"
#include "cppeditortr.h" #include "cppeditortr.h"
#include "cppeditorwidget.h" #include "cppeditorwidget.h"
#include "cppelementevaluator.h" #include "cppelementevaluator.h"
@@ -11,11 +12,10 @@
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/itemviewfind.h> #include <coreplugin/find/itemviewfind.h>
#include <coreplugin/inavigationwidgetfactory.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/typehierarchy.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/delegates.h> #include <utils/delegates.h>
@@ -53,7 +53,7 @@ public:
QMimeData *mimeData(const QModelIndexList &indexes) const override; QMimeData *mimeData(const QModelIndexList &indexes) const override;
}; };
class CppTypeHierarchyWidget : public QWidget class CppTypeHierarchyWidget : public TextEditor::TypeHierarchyWidget
{ {
public: public:
CppTypeHierarchyWidget(); CppTypeHierarchyWidget();
@@ -61,6 +61,8 @@ public:
void perform(); void perform();
private: private:
void reload() override { perform(); }
void displayHierarchy(); void displayHierarchy();
typedef QList<CppClass> CppClass::*HierarchyMember; typedef QList<CppClass> CppClass::*HierarchyMember;
void performFromExpression(const QString &expression, const FilePath &filePath); void performFromExpression(const QString &expression, const FilePath &filePath);
@@ -87,6 +89,7 @@ private:
ProgressIndicator *m_progressIndicator = nullptr; ProgressIndicator *m_progressIndicator = nullptr;
QString m_oldClass; QString m_oldClass;
bool m_showOldClass = false; bool m_showOldClass = false;
int m_runningIndexers = 0;
}; };
enum ItemRole { enum ItemRole {
@@ -197,10 +200,22 @@ CppTypeHierarchyWidget::CppTypeHierarchyWidget()
connect(&m_futureWatcher, &QFutureWatcher<void>::finished, connect(&m_futureWatcher, &QFutureWatcher<void>::finished,
this, &CppTypeHierarchyWidget::displayHierarchy); this, &CppTypeHierarchyWidget::displayHierarchy);
connect(ProgressManager::instance(), &ProgressManager::taskStarted, [this](Id type) {
if (type == Constants::TASK_INDEX)
++m_runningIndexers;
});
connect(ProgressManager::instance(), &ProgressManager::allTasksFinished, [this](Id type) {
if (type == Constants::TASK_INDEX)
--m_runningIndexers;
});
} }
void CppTypeHierarchyWidget::perform() void CppTypeHierarchyWidget::perform()
{ {
if (m_runningIndexers > 0)
return;
if (m_future.isRunning()) if (m_future.isRunning())
m_future.cancel(); m_future.cancel();
@@ -397,47 +412,19 @@ QMimeData *CppTypeHierarchyModel::mimeData(const QModelIndexList &indexes) const
// CppTypeHierarchyFactory // CppTypeHierarchyFactory
class CppTypeHierarchyFactory final : public INavigationWidgetFactory class CppTypeHierarchyFactory final : public TextEditor::TypeHierarchyWidgetFactory
{ {
public: TextEditor::TypeHierarchyWidget *createWidget(Core::IEditor *editor) final
CppTypeHierarchyFactory()
{ {
setDisplayName(Tr::tr("Type Hierarchy")); const auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
setPriority(700); if (!textEditor)
setId(Constants::TYPE_HIERARCHY_ID); return nullptr;
const auto cppDoc = qobject_cast<CppEditorDocument *>(textEditor->textDocument());
if (!cppDoc /* || cppDoc->usesClangd() */)
return nullptr;
ActionBuilder openTypeHierarchy(this, Constants::OPEN_TYPE_HIERARCHY); return new CppTypeHierarchyWidget;
openTypeHierarchy.setText(Tr::tr("Open Type Hierarchy"));
openTypeHierarchy.setContext(Context(Constants::CPPEDITOR_ID));
openTypeHierarchy.bindContextAction(&m_openTypeHierarchyAction);
openTypeHierarchy.setDefaultKeySequence(Tr::tr("Meta+Shift+T"), Tr::tr("Ctrl+Shift+T"));
openTypeHierarchy.addToContainers({Constants::M_TOOLS_CPP, Constants::M_CONTEXT},
Constants::G_SYMBOL);
connect(m_openTypeHierarchyAction, &QAction::triggered, this, [] {
NavigationWidget::activateSubWidget(Constants::TYPE_HIERARCHY_ID, Side::Left);
});
connect(ProgressManager::instance(), &ProgressManager::taskStarted, [this](Id type) {
if (type == Constants::TASK_INDEX)
m_openTypeHierarchyAction->setEnabled(false);
});
connect(ProgressManager::instance(), &ProgressManager::allTasksFinished, [this](Id type) {
if (type == Constants::TASK_INDEX)
m_openTypeHierarchyAction->setEnabled(true);
});
} }
NavigationView createWidget() final
{
auto w = new CppTypeHierarchyWidget;
connect(m_openTypeHierarchyAction, &QAction::triggered, w, &CppTypeHierarchyWidget::perform);
w->perform();
return {w, {}};
}
QAction *m_openTypeHierarchyAction = nullptr;
}; };
static CppTypeHierarchyFactory &cppTypeHierarchyFactory() static CppTypeHierarchyFactory &cppTypeHierarchyFactory()
@@ -446,11 +433,6 @@ static CppTypeHierarchyFactory &cppTypeHierarchyFactory()
return theCppTypeHierarchyFactory; return theCppTypeHierarchyFactory;
} }
void openCppTypeHierarchy()
{
cppTypeHierarchyFactory().m_openTypeHierarchyAction->trigger();
}
void setupCppTypeHierarchy() void setupCppTypeHierarchy()
{ {
(void) cppTypeHierarchyFactory(); // Trigger instantiation (void) cppTypeHierarchyFactory(); // Trigger instantiation

View File

@@ -4,8 +4,5 @@
#pragma once #pragma once
namespace CppEditor::Internal { namespace CppEditor::Internal {
void openCppTypeHierarchy();
void setupCppTypeHierarchy(); void setupCppTypeHierarchy();
} // CppEditor::Internal } // CppEditor::Internal

View File

@@ -9,7 +9,6 @@
#include "cppinsertvirtualmethods.h" #include "cppinsertvirtualmethods.h"
#include "cppmodelmanager.h" #include "cppmodelmanager.h"
#include "cpptoolstestcase.h" #include "cpptoolstestcase.h"
#include "cpptypehierarchy.h"
#include "cppworkingcopy.h" #include "cppworkingcopy.h"
#include "projectinfo.h" #include "projectinfo.h"
@@ -18,6 +17,7 @@
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/typehierarchy.h>
#include <cplusplus/CppDocument.h> #include <cplusplus/CppDocument.h>
#include <cplusplus/TranslationUnit.h> #include <cplusplus/TranslationUnit.h>
@@ -383,7 +383,7 @@ public:
void OpenTypeHierarchyTokenAction::run(CppEditorWidget *) void OpenTypeHierarchyTokenAction::run(CppEditorWidget *)
{ {
openCppTypeHierarchy(); TextEditor::openTypeHierarchy();
QApplication::processEvents(); QApplication::processEvents();
} }

View File

@@ -113,6 +113,7 @@ add_qtc_plugin(TextEditor
textindenter.cpp textindenter.h textindenter.cpp textindenter.h
textmark.cpp textmark.h textmark.cpp textmark.h
textstyles.h textstyles.h
typehierarchy.cpp typehierarchy.h
typingsettings.cpp typingsettings.h typingsettings.cpp typingsettings.h
) )

View File

@@ -8759,6 +8759,11 @@ void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
if (!menu->actions().contains(action)) if (!menu->actions().contains(action))
menu->addAction(action); menu->addAction(action);
} }
if (optionalActions() & TextEditorActionHandler::TypeHierarchy) {
const auto action = ActionManager::command(Constants::OPEN_TYPE_HIERARCHY)->action();
if (!menu->actions().contains(action))
menu->addAction(action);
}
menu->addSeparator(); menu->addSeparator();
appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU); appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU);

View File

@@ -137,7 +137,8 @@ QtcPlugin {
"texteditor.cpp", "texteditor.cpp",
"texteditor.h", "texteditor.h",
"texteditor.qrc", "texteditor.qrc",
"texteditor_global.h", "texteditortr.h", "texteditor_global.h",
"texteditortr.h",
"texteditoractionhandler.cpp", "texteditoractionhandler.cpp",
"texteditoractionhandler.h", "texteditoractionhandler.h",
"texteditorconstants.cpp", "texteditorconstants.cpp",
@@ -152,6 +153,8 @@ QtcPlugin {
"textmark.cpp", "textmark.cpp",
"textmark.h", "textmark.h",
"textstyles.h", "textstyles.h",
"typehierarchy.cpp",
"typehierarchy.h",
"typingsettings.cpp", "typingsettings.cpp",
"typingsettings.h", "typingsettings.h",
] ]

View File

@@ -9,6 +9,7 @@
#include "linenumberfilter.h" #include "linenumberfilter.h"
#include "texteditortr.h" #include "texteditortr.h"
#include "texteditorsettings.h" #include "texteditorsettings.h"
#include "typehierarchy.h"
#include <aggregation/aggregate.h> #include <aggregation/aggregate.h>
@@ -16,6 +17,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
#include <coreplugin/navigationwidget.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h> #include <coreplugin/actionmanager/command.h>
@@ -137,6 +139,7 @@ public:
QAction *m_followToTypeInNextSplitAction = nullptr; QAction *m_followToTypeInNextSplitAction = nullptr;
QAction *m_findUsageAction = nullptr; QAction *m_findUsageAction = nullptr;
QAction *m_openCallHierarchyAction = nullptr; QAction *m_openCallHierarchyAction = nullptr;
QAction *m_openTypeHierarchyAction = nullptr;
QAction *m_renameSymbolAction = nullptr; QAction *m_renameSymbolAction = nullptr;
QAction *m_jumpToFileAction = nullptr; QAction *m_jumpToFileAction = nullptr;
QAction *m_jumpToFileInNextSplitAction = nullptr; QAction *m_jumpToFileInNextSplitAction = nullptr;
@@ -269,7 +272,17 @@ void TextEditorActionHandlerPrivate::createActions()
QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2")).toString()); QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2")).toString());
m_openCallHierarchyAction = registerAction(OPEN_CALL_HIERARCHY, m_openCallHierarchyAction = registerAction(OPEN_CALL_HIERARCHY,
[] (TextEditorWidget *w) { w->openCallHierarchy(); }, true, Tr::tr("Open Call Hierarchy")); [] (TextEditorWidget *w) { w->openCallHierarchy(); }, true, Tr::tr("Open Call Hierarchy"));
m_openTypeHierarchyAction = registerAction(
OPEN_TYPE_HIERARCHY,
[](TextEditorWidget *) {
updateTypeHierarchy(
NavigationWidget::activateSubWidget(Constants::TYPE_HIERARCHY_FACTORY_ID,
Side::Left));
},
true,
Tr::tr("Open Type Hierarchy"),
QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+Shift+T")
: Tr::tr("Ctrl+Shift+T")));
registerAction(VIEW_PAGE_UP, registerAction(VIEW_PAGE_UP,
[] (TextEditorWidget *w) { w->viewPageUp(); }, true, Tr::tr("Move the View a Page Up and Keep the Cursor Position"), [] (TextEditorWidget *w) { w->viewPageUp(); }, true, Tr::tr("Move the View a Page Up and Keep the Cursor Position"),
QKeySequence(Tr::tr("Ctrl+PgUp"))); QKeySequence(Tr::tr("Ctrl+PgUp")));
@@ -550,6 +563,8 @@ void TextEditorActionHandlerPrivate::updateOptionalActions()
optionalActions & TextEditorActionHandler::RenameSymbol); optionalActions & TextEditorActionHandler::RenameSymbol);
m_openCallHierarchyAction->setEnabled( m_openCallHierarchyAction->setEnabled(
optionalActions & TextEditorActionHandler::CallHierarchy); optionalActions & TextEditorActionHandler::CallHierarchy);
m_openTypeHierarchyAction->setEnabled(
optionalActions & TextEditorActionHandler::TypeHierarchy);
bool formatEnabled = (optionalActions & TextEditorActionHandler::Format) bool formatEnabled = (optionalActions & TextEditorActionHandler::Format)
&& m_currentEditorWidget && !m_currentEditorWidget->isReadOnly(); && m_currentEditorWidget && !m_currentEditorWidget->isReadOnly();

View File

@@ -38,7 +38,8 @@ public:
JumpToFileUnderCursor = 32, JumpToFileUnderCursor = 32,
RenameSymbol = 64, RenameSymbol = 64,
FindUsage = 128, FindUsage = 128,
CallHierarchy = 256 CallHierarchy = 256,
TypeHierarchy = 512,
}; };
using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>; using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>;

View File

@@ -212,6 +212,8 @@ const char FIND_USAGES[] = "TextEditor.FindUsages";
// moved from CppEditor to TextEditor avoid breaking the setting by using the old key // moved from CppEditor to TextEditor avoid breaking the setting by using the old key
const char RENAME_SYMBOL[] = "CppEditor.RenameSymbolUnderCursor"; const char RENAME_SYMBOL[] = "CppEditor.RenameSymbolUnderCursor";
const char OPEN_CALL_HIERARCHY[] = "TextEditor.OpenCallHierarchy"; const char OPEN_CALL_HIERARCHY[] = "TextEditor.OpenCallHierarchy";
const char OPEN_TYPE_HIERARCHY[] = "TextEditor.OpenTypeHierarchy";
const char TYPE_HIERARCHY_FACTORY_ID[] = "TextEditor.TypeHierarchy";
const char JUMP_TO_FILE_UNDER_CURSOR[] = "TextEditor.JumpToFileUnderCursor"; const char JUMP_TO_FILE_UNDER_CURSOR[] = "TextEditor.JumpToFileUnderCursor";
const char JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnderCursorInNextSplit"; const char JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnderCursorInNextSplit";

View File

@@ -26,6 +26,7 @@
#include "texteditorsettings.h" #include "texteditorsettings.h"
#include "texteditortr.h" #include "texteditortr.h"
#include "textmark.h" #include "textmark.h"
#include "typehierarchy.h"
#include "typingsettings.h" #include "typingsettings.h"
#ifdef WITH_TESTS #ifdef WITH_TESTS
@@ -105,6 +106,7 @@ void TextEditorPlugin::initialize()
setupTextMarkRegistry(this); setupTextMarkRegistry(this);
setupOutlineFactory(); setupOutlineFactory();
setupTypeHierarchyFactory();
setupLineNumberFilter(); // Goto line functionality for quick open setupLineNumberFilter(); // Goto line functionality for quick open
setupPlainTextEditor(); setupPlainTextEditor();

View File

@@ -0,0 +1,136 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "typehierarchy.h"
#include "texteditorconstants.h"
#include "texteditortr.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/inavigationwidgetfactory.h>
#include <utils/utilsicons.h>
#include <QLabel>
#include <QPalette>
#include <QStackedWidget>
#include <QToolButton>
using namespace Utils;
namespace TextEditor {
namespace Internal {
static QList<TypeHierarchyWidgetFactory *> g_widgetFactories;
class TypeHierarchyFactory : public Core::INavigationWidgetFactory
{
public:
TypeHierarchyFactory()
{
setDisplayName(Tr::tr("Type Hierarchy"));
setPriority(649);
setId(Constants::TYPE_HIERARCHY_FACTORY_ID);
}
private:
Core::NavigationView createWidget() override;
};
class TypeHierarchyWidgetStack : public QStackedWidget
{
Q_OBJECT
public:
TypeHierarchyWidgetStack();
void reload();
};
static TypeHierarchyFactory &typeHierarchyFactory()
{
static TypeHierarchyFactory theFactory;
return theFactory;
}
void setupTypeHierarchyFactory()
{
(void) typeHierarchyFactory();
}
TypeHierarchyWidgetStack::TypeHierarchyWidgetStack()
{
QLabel *label = new QLabel(Tr::tr("No type hierarchy available"), this);
label->setAlignment(Qt::AlignCenter);
label->setAutoFillBackground(true);
label->setBackgroundRole(QPalette::Base);
addWidget(label);
reload();
}
void TypeHierarchyWidgetStack::reload()
{
const auto editor = Core::EditorManager::currentEditor();
TypeHierarchyWidget *newWidget = nullptr;
if (editor) {
for (TypeHierarchyWidgetFactory * const widgetFactory : std::as_const(g_widgetFactories)) {
if ((newWidget = widgetFactory->createWidget(editor)))
break;
}
}
QWidget * const current = currentWidget();
if (current) {
removeWidget(current);
current->deleteLater();
}
if (newWidget) {
addWidget(newWidget);
setCurrentWidget(newWidget);
setFocusProxy(newWidget);
newWidget->reload();
}
}
Core::NavigationView TypeHierarchyFactory::createWidget()
{
const auto placeholder = new TypeHierarchyWidgetStack;
const auto reloadButton = new QToolButton;
reloadButton->setIcon(Icons::RELOAD_TOOLBAR.icon());
reloadButton->setToolTip(Tr::tr("Reloads the type hierarchy for the symbol under the cursor."));
connect(reloadButton, &QToolButton::clicked, placeholder, &TypeHierarchyWidgetStack::reload);
return {placeholder, {reloadButton}};
}
void updateTypeHierarchy(QWidget *widget)
{
if (const auto w = qobject_cast<TypeHierarchyWidgetStack *>(widget))
w->reload();
}
} // namespace Internal
TypeHierarchyWidgetFactory::TypeHierarchyWidgetFactory()
{
Internal::g_widgetFactories.append(this);
}
TypeHierarchyWidgetFactory::~TypeHierarchyWidgetFactory()
{
Internal::g_widgetFactories.removeOne(this);
}
void openTypeHierarchy()
{
if (const auto action = Core::ActionManager::command(Constants::OPEN_TYPE_HIERARCHY)->action())
action->trigger();
}
} // namespace TextEditor
#include <typehierarchy.moc>

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <texteditor/texteditor_global.h>
#include <QWidget>
namespace Core { class IEditor; }
namespace TextEditor {
namespace Internal {
void setupTypeHierarchyFactory();
void updateTypeHierarchy(QWidget *widget);
}
class TEXTEDITOR_EXPORT TypeHierarchyWidget : public QWidget
{
Q_OBJECT
public:
virtual void reload() = 0;
};
class TEXTEDITOR_EXPORT TypeHierarchyWidgetFactory : public QObject
{
Q_OBJECT
public:
virtual TypeHierarchyWidget *createWidget(Core::IEditor *editor) = 0;
protected:
TypeHierarchyWidgetFactory();
~TypeHierarchyWidgetFactory() override;
};
TEXTEDITOR_EXPORT void openTypeHierarchy();
} // namespace TextEditor