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);
cppToolsMenu->addAction(cmd);
cmd = ActionManager::command(TextEditor::Constants::OPEN_CALL_HIERARCHY);
contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST);
cppToolsMenu->addAction(cmd);
// Refactoring sub-menu
Command *sep = contextMenu->addSeparator();
sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT));

View File

@@ -23,8 +23,6 @@ using namespace LanguageServerProtocol;
namespace LanguageClient {
const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
namespace {
enum Direction { Incoming, Outgoing };
@@ -186,6 +184,9 @@ public:
layout()->setSpacing(0);
connect(m_view, &NavigationTreeView::activated, this, &CallHierarchy::onItemActivated);
connect(LanguageClientManager::instance(), &LanguageClientManager::openCallHierarchy,
this, &CallHierarchy::updateHierarchyAtCursorPosition);
}
void onItemActivated(const QModelIndex &index)
@@ -211,26 +212,14 @@ void CallHierarchy::updateHierarchyAtCursorPosition()
BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
if (!editor)
return;
Client *client = LanguageClientManager::clientForFilePath(editor->document()->filePath());
Core::IDocument *document = editor->document();
Client *client = LanguageClientManager::clientForFilePath(document->filePath());
if (!client)
return;
const QString methodName = PrepareCallHierarchyRequest::methodName;
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)
if (!CallHierarchyFactory::supportsCallHierarchy(client, document))
return;
TextDocumentPositionParams params;
@@ -273,7 +262,25 @@ CallHierarchyFactory::CallHierarchyFactory()
{
setDisplayName(Tr::tr("Call Hierarchy"));
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()

View File

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

View File

@@ -3,6 +3,7 @@
#include "client.h"
#include "callhierarchy.h"
#include "diagnosticmanager.h"
#include "documentsymbolcache.h"
#include "languageclientcompletionassist.h"
@@ -879,6 +880,8 @@ void Client::activateEditor(Core::IEditor *editor)
optionalActions |= TextEditor::TextEditorActionHandler::FindUsage;
if (symbolSupport().supportsRename(widget->textDocument()))
optionalActions |= TextEditor::TextEditorActionHandler::RenameSymbol;
if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document()))
optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy;
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_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::LanguageClient", "Functions and Methods in Workspace");
const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
} // namespace Constants
} // namespace LanguageClient

View File

@@ -11,6 +11,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/find/searchresultwindow.h>
#include <coreplugin/icore.h>
#include <coreplugin/navigationwidget.h>
#include <languageserverprotocol/messages.h>
#include <languageserverprotocol/progresssupport.h>
@@ -448,6 +449,8 @@ QList<Client *> LanguageClientManager::reachableClients()
void LanguageClientManager::editorOpened(Core::IEditor *editor)
{
using namespace TextEditor;
using namespace Core;
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
if (TextEditorWidget *widget = textEditor->editorWidget()) {
connect(widget, &TextEditorWidget::requestLinkAt, this,
@@ -466,6 +469,14 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
if (auto client = clientForDocument(document))
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]() {
if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable())

View File

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

View File

@@ -2348,6 +2348,11 @@ void TextEditorWidget::renameSymbolUnderCursor()
emit requestRename(textCursor());
}
void TextEditorWidget::openCallHierarchy()
{
emit requestCallHierarchy(textCursor());
}
void TextEditorWidget::abortAssist()
{
d->m_codeAssistant.destroyContext();
@@ -8225,6 +8230,11 @@ void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
if (!menu->actions().contains(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();
appendMenuActionsFromContext(menu, Constants::M_STANDARDCONTEXTMENU);

View File

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

View File

@@ -116,6 +116,7 @@ public:
QAction *m_followSymbolAction = nullptr;
QAction *m_followSymbolInNextSplitAction = nullptr;
QAction *m_findUsageAction = nullptr;
QAction *m_openCallHierarchyAction = nullptr;
QAction *m_renameSymbolAction = nullptr;
QAction *m_jumpToFileAction = nullptr;
QAction *m_jumpToFileInNextSplitAction = nullptr;
@@ -228,6 +229,8 @@ void TextEditorActionHandlerPrivate::createActions()
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"),
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,
[] (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);
m_renameSymbolAction->setEnabled(
optionalActions & TextEditorActionHandler::RenameSymbol);
m_openCallHierarchyAction->setEnabled(
optionalActions & TextEditorActionHandler::CallHierarchy);
bool formatEnabled = (optionalActions & TextEditorActionHandler::Format)
&& m_currentEditorWidget && !m_currentEditorWidget->isReadOnly();

View File

@@ -36,7 +36,8 @@ public:
FollowSymbolUnderCursor = 8,
JumpToFileUnderCursor = 16,
RenameSymbol = 32,
FindUsage = 64
FindUsage = 64,
CallHierarchy = 128
};
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";
// moved from CppEditor to TextEditor avoid breaking the setting by using the old key
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_IN_NEXT_SPLIT[] = "TextEditor.JumpToFileUnderCursorInNextSplit";