LanguageClient: add action to open call hierarchy

Fixes: QTCREATORBUG-28839
Fixes: QTCREATORBUG-28842
Change-Id: Icb70412282c0c2c36241559d942a58ffddab5664
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-03-22 10:39:09 +01:00
parent f423db5cb7
commit eb7ccfd889
12 changed files with 74 additions and 21 deletions

View File

@@ -431,6 +431,10 @@ void CppEditorPlugin::initialize()
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
cppToolsMenu->addAction(cmd); cppToolsMenu->addAction(cmd);
cmd = ActionManager::command(TextEditor::Constants::OPEN_CALL_HIERARCHY);
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
cppToolsMenu->addAction(cmd);
// Refactoring sub-menu // Refactoring sub-menu
Command *sep = contextMenu->addSeparator(); Command *sep = contextMenu->addSeparator();
sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT)); sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT));

View File

@@ -23,8 +23,6 @@ using namespace LanguageServerProtocol;
namespace LanguageClient { namespace LanguageClient {
const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
namespace { namespace {
enum Direction { Incoming, Outgoing }; enum Direction { Incoming, Outgoing };
@@ -186,6 +184,9 @@ public:
layout()->setSpacing(0); layout()->setSpacing(0);
connect(m_view, &NavigationTreeView::activated, this, &CallHierarchy::onItemActivated); connect(m_view, &NavigationTreeView::activated, this, &CallHierarchy::onItemActivated);
connect(LanguageClientManager::instance(), &LanguageClientManager::openCallHierarchy,
this, &CallHierarchy::updateHierarchyAtCursorPosition);
} }
void onItemActivated(const QModelIndex &index) void onItemActivated(const QModelIndex &index)
@@ -211,26 +212,14 @@ void CallHierarchy::updateHierarchyAtCursorPosition()
BaseTextEditor *editor = BaseTextEditor::currentTextEditor(); BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
if (!editor) if (!editor)
return; return;
Client *client = LanguageClientManager::clientForFilePath(editor->document()->filePath());
Core::IDocument *document = editor->document();
Client *client = LanguageClientManager::clientForFilePath(document->filePath());
if (!client) if (!client)
return; return;
const QString methodName = PrepareCallHierarchyRequest::methodName; if (!CallHierarchyFactory::supportsCallHierarchy(client, document))
std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
bool supported = registered.value_or(false);
const Core::IDocument *document = editor->document();
if (registered) {
if (supported) {
const QJsonValue &options = client->dynamicCapabilities().option(methodName);
const TextDocumentRegistrationOptions docOptions(options);
supported = docOptions.filterApplies(document->filePath(),
Utils::mimeTypeForName(document->mimeType()));
}
} else {
supported = client->capabilities().callHierarchyProvider().has_value();
}
if (!supported)
return; return;
TextDocumentPositionParams params; TextDocumentPositionParams params;
@@ -273,7 +262,25 @@ CallHierarchyFactory::CallHierarchyFactory()
{ {
setDisplayName(Tr::tr("Call Hierarchy")); setDisplayName(Tr::tr("Call Hierarchy"));
setPriority(650); setPriority(650);
setId(CALL_HIERARCHY_FACTORY_ID); setId(Constants::CALL_HIERARCHY_FACTORY_ID);
}
bool CallHierarchyFactory::supportsCallHierarchy(Client *client, const Core::IDocument *document)
{
const QString methodName = PrepareCallHierarchyRequest::methodName;
std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
bool supported = registered.value_or(false);
if (registered) {
if (supported) {
const QJsonValue &options = client->dynamicCapabilities().option(methodName);
const TextDocumentRegistrationOptions docOptions(options);
supported = docOptions.filterApplies(document->filePath(),
Utils::mimeTypeForName(document->mimeType()));
}
} else {
supported = client->capabilities().callHierarchyProvider().has_value();
}
return supported;
} }
Core::NavigationView CallHierarchyFactory::createWidget() Core::NavigationView CallHierarchyFactory::createWidget()

View File

@@ -5,8 +5,12 @@
#pragma once #pragma once
namespace Core { class IDocument; }
namespace LanguageClient { namespace LanguageClient {
class Client;
class CallHierarchyFactory : public Core::INavigationWidgetFactory class CallHierarchyFactory : public Core::INavigationWidgetFactory
{ {
Q_OBJECT Q_OBJECT
@@ -14,6 +18,8 @@ class CallHierarchyFactory : public Core::INavigationWidgetFactory
public: public:
CallHierarchyFactory(); CallHierarchyFactory();
static bool supportsCallHierarchy(Client *client, const Core::IDocument *document);
Core::NavigationView createWidget() override; Core::NavigationView createWidget() override;
}; };

View File

@@ -3,6 +3,7 @@
#include "client.h" #include "client.h"
#include "callhierarchy.h"
#include "diagnosticmanager.h" #include "diagnosticmanager.h"
#include "documentsymbolcache.h" #include "documentsymbolcache.h"
#include "languageclientcompletionassist.h" #include "languageclientcompletionassist.h"
@@ -879,6 +880,8 @@ void Client::activateEditor(Core::IEditor *editor)
optionalActions |= TextEditor::TextEditorActionHandler::FindUsage; optionalActions |= TextEditor::TextEditorActionHandler::FindUsage;
if (symbolSupport().supportsRename(widget->textDocument())) if (symbolSupport().supportsRename(widget->textDocument()))
optionalActions |= TextEditor::TextEditorActionHandler::RenameSymbol; optionalActions |= TextEditor::TextEditorActionHandler::RenameSymbol;
if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document()))
optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy;
widget->setOptionalActions(optionalActions); widget->setOptionalActions(optionalActions);
} }
} }

View File

@@ -29,5 +29,7 @@ const char LANGUAGECLIENT_WORKSPACE_CLASS_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_N
const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID[] = "Workspace Functions and Methods"; const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_ID[] = "Workspace Functions and Methods";
const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Functions and Methods in Workspace"); const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Functions and Methods in Workspace");
const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
} // namespace Constants } // namespace Constants
} // namespace LanguageClient } // namespace LanguageClient

View File

@@ -11,6 +11,7 @@
#include <coreplugin/editormanager/ieditor.h> #include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/find/searchresultwindow.h> #include <coreplugin/find/searchresultwindow.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/navigationwidget.h>
#include <languageserverprotocol/messages.h> #include <languageserverprotocol/messages.h>
#include <languageserverprotocol/progresssupport.h> #include <languageserverprotocol/progresssupport.h>
@@ -448,6 +449,8 @@ QList<Client *> LanguageClientManager::reachableClients()
void LanguageClientManager::editorOpened(Core::IEditor *editor) void LanguageClientManager::editorOpened(Core::IEditor *editor)
{ {
using namespace TextEditor; using namespace TextEditor;
using namespace Core;
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) { if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
if (TextEditorWidget *widget = textEditor->editorWidget()) { if (TextEditorWidget *widget = textEditor->editorWidget()) {
connect(widget, &TextEditorWidget::requestLinkAt, this, connect(widget, &TextEditorWidget::requestLinkAt, this,
@@ -466,6 +469,14 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
if (auto client = clientForDocument(document)) if (auto client = clientForDocument(document))
client->symbolSupport().renameSymbol(document, cursor); client->symbolSupport().renameSymbol(document, cursor);
}); });
connect(widget, &TextEditorWidget::requestCallHierarchy, this,
[this, textEditor](const QTextCursor &cursor) {
if (auto client = clientForDocument(textEditor->textDocument())) {
emit openCallHierarchy();
NavigationWidget::activateSubWidget(Constants::CALL_HIERARCHY_FACTORY_ID,
Side::Left);
}
});
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() { connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() {
if (Client *client = clientForDocument(widget->textDocument())) if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable()) if (client->reachable())

View File

@@ -83,6 +83,7 @@ signals:
void clientInitialized(Client *client); void clientInitialized(Client *client);
void clientRemoved(Client *client); void clientRemoved(Client *client);
void shutdownFinished(); void shutdownFinished();
void openCallHierarchy();
private: private:
LanguageClientManager(QObject *parent); LanguageClientManager(QObject *parent);

View File

@@ -2348,6 +2348,11 @@ void TextEditorWidget::renameSymbolUnderCursor()
emit requestRename(textCursor()); emit requestRename(textCursor());
} }
void TextEditorWidget::openCallHierarchy()
{
emit requestCallHierarchy(textCursor());
}
void TextEditorWidget::abortAssist() void TextEditorWidget::abortAssist()
{ {
d->m_codeAssistant.destroyContext(); d->m_codeAssistant.destroyContext();
@@ -8225,6 +8230,11 @@ void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
if (!menu->actions().contains(findUsage)) if (!menu->actions().contains(findUsage))
menu->addAction(findUsage); menu->addAction(findUsage);
} }
if (optionalActions() & TextEditorActionHandler::CallHierarchy) {
const auto callHierarchy = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action();
if (!menu->actions().contains(callHierarchy))
menu->addAction(callHierarchy);
}
menu->addSeparator(); menu->addSeparator();
appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU); appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU);

View File

@@ -437,6 +437,7 @@ public:
virtual void findUsages(); virtual void findUsages();
virtual void renameSymbolUnderCursor(); virtual void renameSymbolUnderCursor();
virtual void openCallHierarchy();
/// Abort code assistant if it is running. /// Abort code assistant if it is running.
void abortAssist(); void abortAssist();
@@ -487,6 +488,7 @@ signals:
bool resolveTarget, bool inNextSplit); bool resolveTarget, bool inNextSplit);
void requestUsages(const QTextCursor &cursor); void requestUsages(const QTextCursor &cursor);
void requestRename(const QTextCursor &cursor); void requestRename(const QTextCursor &cursor);
void requestCallHierarchy(const QTextCursor &cursor);
void optionalActionMaskChanged(); void optionalActionMaskChanged();
void toolbarOutlineChanged(QWidget *newOutline); void toolbarOutlineChanged(QWidget *newOutline);

View File

@@ -116,6 +116,7 @@ public:
QAction *m_followSymbolAction = nullptr; QAction *m_followSymbolAction = nullptr;
QAction *m_followSymbolInNextSplitAction = nullptr; QAction *m_followSymbolInNextSplitAction = nullptr;
QAction *m_findUsageAction = nullptr; QAction *m_findUsageAction = nullptr;
QAction *m_openCallHierarchyAction = 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;
@@ -228,6 +229,8 @@ void TextEditorActionHandlerPrivate::createActions()
m_jumpToFileInNextSplitAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT, m_jumpToFileInNextSplitAction = registerAction(JUMP_TO_FILE_UNDER_CURSOR_IN_NEXT_SPLIT,
[] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Jump to File Under Cursor in Next Split"), [] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Jump to File Under Cursor in Next Split"),
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,
[] (TextEditorWidget *w) { w->openCallHierarchy(); }, true, Tr::tr("Open Call Hierarchy"));
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"),
@@ -484,6 +487,8 @@ void TextEditorActionHandlerPrivate::updateOptionalActions()
optionalActions & TextEditorActionHandler::UnCollapseAll); optionalActions & TextEditorActionHandler::UnCollapseAll);
m_renameSymbolAction->setEnabled( m_renameSymbolAction->setEnabled(
optionalActions & TextEditorActionHandler::RenameSymbol); optionalActions & TextEditorActionHandler::RenameSymbol);
m_openCallHierarchyAction->setEnabled(
optionalActions & TextEditorActionHandler::CallHierarchy);
bool formatEnabled = (optionalActions & TextEditorActionHandler::Format) bool formatEnabled = (optionalActions & TextEditorActionHandler::Format)
&& m_currentEditorWidget && !m_currentEditorWidget->isReadOnly(); && m_currentEditorWidget && !m_currentEditorWidget->isReadOnly();

View File

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

View File

@@ -208,6 +208,7 @@ const char FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbol
const char FIND_USAGES[] = "TextEditor.FindUsages"; 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 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";