forked from qt-creator/qt-creator
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:
@@ -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));
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 *)>;
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user