LanguageClient: support additional goto targets in symbol support

Allow to follow to the symbol definition as well as to the type
definition for the symbol under the cursor position.

Change-Id: I8ff50b33a1e739f81b0832b1b28ffc525e1f7177
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-09-05 11:14:10 +02:00
parent 23828a1d9c
commit 80633a59aa
19 changed files with 265 additions and 106 deletions

View File

@@ -1013,7 +1013,11 @@ void ClangdClient::followSymbol(TextDocument *document,
const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document); const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document);
if (followTo == FollowTo::SymbolDef && !resolveTarget) { if (followTo == FollowTo::SymbolDef && !resolveTarget) {
symbolSupport().findLinkAt(document, adjustedCursor, callback, false); symbolSupport().findLinkAt(document,
adjustedCursor,
callback,
false,
LanguageClient::LinkTarget::SymbolDef);
return; return;
} }

View File

@@ -714,7 +714,11 @@ void ClangdFindLocalReferences::Private::findDefinition()
if (sentinel) if (sentinel)
getDefinitionAst(l); getDefinitionAst(l);
}; };
client()->symbolSupport().findLinkAt(document, cursor, linkHandler, true); client()->symbolSupport().findLinkAt(document,
cursor,
linkHandler,
true,
LanguageClient::LinkTarget::SymbolDef);
} }
void ClangdFindLocalReferences::Private::getDefinitionAst(const Link &link) void ClangdFindLocalReferences::Private::getDefinitionAst(const Link &link)

View File

@@ -154,7 +154,11 @@ ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor &
if (self->d->cursorNode) if (self->d->cursorNode)
self->d->handleGotoDefinitionResult(); self->d->handleGotoDefinitionResult();
}; };
client->symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true); client->symbolSupport().findLinkAt(document,
cursor,
std::move(gotoDefCallback),
true,
LanguageClient::LinkTarget::SymbolDef);
const auto astHandler = [self = QPointer(this)](const ClangdAstNode &ast, const MessageId &) { const auto astHandler = [self = QPointer(this)](const ClangdAstNode &ast, const MessageId &) {
qCDebug(clangdLog) << "received ast response for cursor"; qCDebug(clangdLog) << "received ast response for cursor";

View File

@@ -16,8 +16,6 @@ const char G_GLOBAL[] = "CppEditor.GGlobal";
const char CPPEDITOR_ID[] = "CppEditor.C++Editor"; const char CPPEDITOR_ID[] = "CppEditor.C++Editor";
const char SWITCH_DECLARATION_DEFINITION[] = "CppEditor.SwitchDeclarationDefinition"; const char SWITCH_DECLARATION_DEFINITION[] = "CppEditor.SwitchDeclarationDefinition";
const char OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT[] = "CppEditor.OpenDeclarationDefinitionInNextSplit"; const char OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT[] = "CppEditor.OpenDeclarationDefinitionInNextSplit";
const char FOLLOW_SYMBOL_TO_TYPE[] = "TextEditor.FollowSymbolToType";
const char FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolToTypeInNextSplit";
const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog"; const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog";
const char MULTIPLE_PARSE_CONTEXTS_AVAILABLE[] = "CppEditor.MultipleParseContextsAvailable"; const char MULTIPLE_PARSE_CONTEXTS_AVAILABLE[] = "CppEditor.MultipleParseContextsAvailable";
const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup"; const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup";

View File

@@ -152,6 +152,7 @@ public:
| TextEditorActionHandler::UnCommentSelection | TextEditorActionHandler::UnCommentSelection
| TextEditorActionHandler::UnCollapseAll | TextEditorActionHandler::UnCollapseAll
| TextEditorActionHandler::FollowSymbolUnderCursor | TextEditorActionHandler::FollowSymbolUnderCursor
| TextEditorActionHandler::FollowTypeUnderCursor
| TextEditorActionHandler::RenameSymbol | TextEditorActionHandler::RenameSymbol
| TextEditorActionHandler::FindUsage); | TextEditorActionHandler::FindUsage);
} }
@@ -336,31 +337,10 @@ void CppEditorPlugin::addPerSymbolActions()
touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION); touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION);
addSymbolActionToMenus(ActionManager::command( addSymbolActionToMenus(ActionManager::command(
TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT)); TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT));
addSymbolActionToMenus(ActionManager::command(
QAction * const followSymbolToType = new QAction(Tr::tr("Follow Symbol Under Cursor to Type"), TextEditor::Constants::FOLLOW_SYMBOL_TO_TYPE));
this); addSymbolActionToMenus(ActionManager::command(
cmd = ActionManager::registerAction(followSymbolToType, Constants::FOLLOW_SYMBOL_TO_TYPE, TextEditor::Constants::FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT));
context, true);
cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+F2")));
connect(followSymbolToType, &QAction::triggered, this, []{
if (CppEditorWidget *editorWidget = currentCppEditorWidget())
editorWidget->followSymbolToType(false);
});
addSymbolActionToMenus(cmd);
QAction * const followSymbolToTypeInNextSplit =
new QAction(Tr::tr("Follow Symbol to Type in Next Split"), this);
cmd = ActionManager::registerAction(followSymbolToTypeInNextSplit,
Constants::FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT,
context, true);
cmd->setDefaultKeySequence(QKeySequence(HostOsInfo::isMacHost()
? Tr::tr("Meta+E, Ctrl+Shift+F2")
: Tr::tr("Ctrl+E, Ctrl+Shift+F2")));
connect(followSymbolToTypeInNextSplit, &QAction::triggered, this, []{
if (CppEditorWidget *editorWidget = currentCppEditorWidget())
editorWidget->followSymbolToType(true);
});
addSymbolActionToMenus(cmd);
QAction * const switchDeclarationDefinition QAction * const switchDeclarationDefinition
= new QAction(Tr::tr("Switch Between Function Declaration/Definition"), this); = new QAction(Tr::tr("Switch Between Function Declaration/Definition"), this);

View File

@@ -893,20 +893,6 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
CppModelManager::switchDeclDef(cursor, std::move(callback)); CppModelManager::switchDeclDef(cursor, std::move(callback));
} }
void CppEditorWidget::followSymbolToType(bool inNextSplit)
{
if (!CppModelManager::instance())
return;
const CursorInEditor cursor(textCursor(), textDocument()->filePath(), this, textDocument());
const auto callback = [self = QPointer(this),
split = inNextSplit != alwaysOpenLinksInNextSplit()](const Link &link) {
if (self && link.hasValidTarget())
self->openLink(link, split);
};
CppModelManager::followSymbolToType(cursor, callback, inNextSplit);
}
bool CppEditorWidget::followUrl(const QTextCursor &cursor, bool CppEditorWidget::followUrl(const QTextCursor &cursor,
const Utils::LinkHandler &processLinkCallback) const Utils::LinkHandler &processLinkCallback)
{ {
@@ -997,11 +983,27 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor,
} }
callback(link); callback(link);
}; };
CppModelManager::followSymbol( CppModelManager::followSymbol(CursorInEditor{cursor, filePath, this, textDocument()},
CursorInEditor{cursor, filePath, this, textDocument()}, callbackWrapper,
callbackWrapper, resolveTarget,
resolveTarget, inNextSplit);
inNextSplit); }
void CppEditorWidget::findTypeAt(const QTextCursor &cursor,
const Utils::LinkHandler &processLinkCallback,
bool resolveTarget,
bool inNextSplit)
{
if (!CppModelManager::instance())
return;
const CursorInEditor cursorInEditor(cursor, textDocument()->filePath(), this, textDocument());
const auto callback = [self = QPointer(this),
split = inNextSplit != alwaysOpenLinksInNextSplit()](const Link &link) {
if (self && link.hasValidTarget())
self->openLink(link, split);
};
CppModelManager::followSymbolToType(cursorInEditor, callback, inNextSplit);
} }
unsigned CppEditorWidget::documentRevision() const unsigned CppEditorWidget::documentRevision() const

View File

@@ -57,7 +57,6 @@ public:
void selectAll() override; void selectAll() override;
void switchDeclarationDefinition(bool inNextSplit); void switchDeclarationDefinition(bool inNextSplit);
void followSymbolToType(bool inNextSplit);
void showPreProcessorWidget(); void showPreProcessorWidget();
void findUsages() override; void findUsages() override;
@@ -105,6 +104,11 @@ protected:
bool resolveTarget = true, bool resolveTarget = true,
bool inNextSplit = false) override; bool inNextSplit = false) override;
void findTypeAt(const QTextCursor &cursor,
const Utils::LinkHandler &processLinkCallback,
bool resolveTarget = true,
bool inNextSplit = false) override;
void slotCodeStyleSettingsChanged(const QVariant &) override; void slotCodeStyleSettingsChanged(const QVariant &) override;
private: private:

View File

@@ -936,6 +936,10 @@ 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 (symbolSupport().supportsFindLink(widget->textDocument(), LinkTarget::SymbolDef))
optionalActions |= TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor;
if (symbolSupport().supportsFindLink(widget->textDocument(), LinkTarget::SymbolTypeDef))
optionalActions |= TextEditor::TextEditorActionHandler::FollowTypeUnderCursor;
if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document())) if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document()))
optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy; optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy;
widget->setOptionalActions(optionalActions); widget->setOptionalActions(optionalActions);
@@ -1306,7 +1310,8 @@ SymbolSupport &Client::symbolSupport()
void Client::findLinkAt(TextEditor::TextDocument *document, void Client::findLinkAt(TextEditor::TextDocument *document,
const QTextCursor &cursor, const QTextCursor &cursor,
Utils::LinkHandler callback, Utils::LinkHandler callback,
const bool resolveTarget) const bool resolveTarget,
LinkTarget target)
{ {
if (d->m_runningFindLinkRequest.isValid()) if (d->m_runningFindLinkRequest.isValid())
cancelRequest(d->m_runningFindLinkRequest); cancelRequest(d->m_runningFindLinkRequest);
@@ -1317,7 +1322,8 @@ void Client::findLinkAt(TextEditor::TextDocument *document,
d->m_runningFindLinkRequest = {}; d->m_runningFindLinkRequest = {};
callback(link); callback(link);
}, },
resolveTarget); resolveTarget,
target);
} }
void Client::requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, void Client::requestCodeActions(const LanguageServerProtocol::DocumentUri &uri,

View File

@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "languageclient_global.h" #include "languageclient_global.h"
#include "languageclientsymbolsupport.h"
#include "languageclientutils.h" #include "languageclientutils.h"
#include "semantichighlightsupport.h" #include "semantichighlightsupport.h"
@@ -45,7 +46,6 @@ class LanguageClientOutlineItem;
class LanguageClientQuickFixProvider; class LanguageClientQuickFixProvider;
class LanguageFilter; class LanguageFilter;
class ProgressManager; class ProgressManager;
class SymbolSupport;
class LANGUAGECLIENT_EXPORT Client : public QObject class LANGUAGECLIENT_EXPORT Client : public QObject
{ {
@@ -157,7 +157,8 @@ public:
void findLinkAt(TextEditor::TextDocument *document, void findLinkAt(TextEditor::TextDocument *document,
const QTextCursor &cursor, const QTextCursor &cursor,
Utils::LinkHandler callback, Utils::LinkHandler callback,
const bool resolveTarget); const bool resolveTarget,
LinkTarget target);
DocumentSymbolCache *documentSymbolCache(); DocumentSymbolCache *documentSymbolCache();
HoverHandler *hoverHandler(); HoverHandler *hoverHandler();
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(const Utils::FilePath &filePath, QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(const Utils::FilePath &filePath,

View File

@@ -479,8 +479,24 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
connect(widget, &TextEditorWidget::requestLinkAt, this, connect(widget, &TextEditorWidget::requestLinkAt, this,
[document = textEditor->textDocument()] [document = textEditor->textDocument()]
(const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget) { (const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget) {
if (auto client = clientForDocument(document)) if (auto client = clientForDocument(document)) {
client->findLinkAt(document, cursor, callback, resolveTarget); client->findLinkAt(document,
cursor,
callback,
resolveTarget,
LinkTarget::SymbolDef);
}
});
connect(widget, &TextEditorWidget::requestTypeAt, this,
[document = textEditor->textDocument()]
(const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget) {
if (auto client = clientForDocument(document)) {
client->findLinkAt(document,
cursor,
callback,
resolveTarget,
LinkTarget::SymbolTypeDef);
}
}); });
connect(widget, &TextEditorWidget::requestUsages, this, connect(widget, &TextEditorWidget::requestUsages, this,
[document = textEditor->textDocument()](const QTextCursor &cursor) { [document = textEditor->textDocument()](const QTextCursor &cursor) {

View File

@@ -77,15 +77,14 @@ SymbolSupport::SymbolSupport(Client *client)
{} {}
template<typename Request> template<typename Request>
static void sendTextDocumentPositionParamsRequest(Client *client, static MessageId sendTextDocumentPositionParamsRequest(Client *client, const Request &request)
const Request &request,
const DynamicCapabilities &dynamicCapabilities,
const ServerCapabilities &serverCapability)
{ {
if (!request.isValid(nullptr)) if (!request.isValid(nullptr))
return; return {};
const DocumentUri uri = request.params().value().textDocument().uri(); const DocumentUri uri = request.params().value().textDocument().uri();
const bool supportedFile = client->isSupportedUri(uri); const bool supportedFile = client->isSupportedUri(uri);
const DynamicCapabilities dynamicCapabilities = client->dynamicCapabilities();
const ServerCapabilities serverCapability = client->capabilities();
bool sendMessage = dynamicCapabilities.isRegistered(Request::methodName).value_or(false); bool sendMessage = dynamicCapabilities.isRegistered(Request::methodName).value_or(false);
if (sendMessage) { if (sendMessage) {
const TextDocumentRegistrationOptions option( const TextDocumentRegistrationOptions option(
@@ -102,14 +101,17 @@ static void sendTextDocumentPositionParamsRequest(Client *client,
if (sendMessage && std::holds_alternative<bool>(*provider)) if (sendMessage && std::holds_alternative<bool>(*provider))
sendMessage = std::get<bool>(*provider); sendMessage = std::get<bool>(*provider);
} }
if (sendMessage) if (sendMessage) {
client->sendMessage(request); client->sendMessage(request);
return request.id();
}
return {};
} }
static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response &response, static void handleGotoResponse(const GotoDefinitionRequest::Response &response,
Utils::LinkHandler callback, Utils::LinkHandler callback,
std::optional<Utils::Link> linkUnderCursor, std::optional<Utils::Link> linkUnderCursor,
const Client *client) const Client *client)
{ {
if (std::optional<GotoResult> result = response.result()) { if (std::optional<GotoResult> result = response.result()) {
if (std::holds_alternative<std::nullptr_t>(*result)) { if (std::holds_alternative<std::nullptr_t>(*result)) {
@@ -137,14 +139,69 @@ static TextDocumentPositionParams generateDocPosParams(TextEditor::TextDocument
return TextDocumentPositionParams(documentId, pos); return TextDocumentPositionParams(documentId, pos);
} }
template<typename Request>
static MessageId sendGotoRequest(TextEditor::TextDocument *document,
const QTextCursor &cursor,
Utils::LinkHandler callback,
Client *client,
std::optional<Utils::Link> linkUnderCursor)
{
Request request(generateDocPosParams(document, cursor, client));
request.setResponseCallback([callback, linkUnderCursor, client](
const GotoDefinitionRequest::Response &response) {
handleGotoResponse(response, callback, linkUnderCursor, client);
});
return sendTextDocumentPositionParamsRequest(client, request);
return request.id();
}
bool SymbolSupport::supportsFindLink(TextEditor::TextDocument *document, LinkTarget target) const
{
const DocumentUri uri = m_client->hostPathToServerUri(document->filePath());
const DynamicCapabilities dynamicCapabilities = m_client->dynamicCapabilities();
const ServerCapabilities serverCapability = m_client->capabilities();
QString methodName;
std::optional<std::variant<bool, ServerCapabilities::RegistrationOptions>> provider;
switch (target) {
case LinkTarget::SymbolDef:
methodName = GotoDefinitionRequest::methodName;
provider = serverCapability.definitionProvider();
break;
case LinkTarget::SymbolTypeDef:
methodName = GotoTypeDefinitionRequest::methodName;
provider = serverCapability.typeDefinitionProvider();
break;
case LinkTarget::SymbolImplementation:
methodName = GotoImplementationRequest::methodName;
provider = serverCapability.implementationProvider();
break;
}
if (methodName.isEmpty())
return false;
bool supported = dynamicCapabilities.isRegistered(methodName).value_or(false);
if (supported) {
const TextDocumentRegistrationOptions option(dynamicCapabilities.option(methodName));
if (option.isValid())
supported = option.filterApplies(
Utils::FilePath::fromString(QUrl(uri).adjusted(QUrl::PreferLocalFile).toString()));
else
supported = m_client->isSupportedUri(uri);
} else {
supported = provider.has_value();
if (supported && std::holds_alternative<bool>(*provider))
supported = std::get<bool>(*provider);
}
return supported;
}
MessageId SymbolSupport::findLinkAt(TextEditor::TextDocument *document, MessageId SymbolSupport::findLinkAt(TextEditor::TextDocument *document,
const QTextCursor &cursor, const QTextCursor &cursor,
Utils::LinkHandler callback, Utils::LinkHandler callback,
const bool resolveTarget) const bool resolveTarget,
const LinkTarget target)
{ {
if (!m_client->reachable()) if (!m_client->reachable())
return {}; return {};
GotoDefinitionRequest request(generateDocPosParams(document, cursor, m_client));
std::optional<Utils::Link> linkUnderCursor; std::optional<Utils::Link> linkUnderCursor;
if (!resolveTarget) { if (!resolveTarget) {
QTextCursor linkCursor = cursor; QTextCursor linkCursor = cursor;
@@ -156,16 +213,29 @@ MessageId SymbolSupport::findLinkAt(TextEditor::TextDocument *document,
link.linkTextEnd = linkCursor.selectionEnd(); link.linkTextEnd = linkCursor.selectionEnd();
linkUnderCursor = link; linkUnderCursor = link;
} }
request.setResponseCallback([callback, linkUnderCursor, client = m_client](
const GotoDefinitionRequest::Response &response) {
handleGotoDefinitionResponse(response, callback, linkUnderCursor, client);
});
sendTextDocumentPositionParamsRequest(m_client, const TextDocumentPositionParams params = generateDocPosParams(document, cursor, m_client);
request, switch (target) {
m_client->dynamicCapabilities(), case LinkTarget::SymbolDef:
m_client->capabilities()); return sendGotoRequest<GotoDefinitionRequest>(document,
return request.id(); cursor,
callback,
m_client,
linkUnderCursor);
case LinkTarget::SymbolTypeDef:
return sendGotoRequest<GotoTypeDefinitionRequest>(document,
cursor,
callback,
m_client,
linkUnderCursor);
case LinkTarget::SymbolImplementation:
return sendGotoRequest<GotoImplementationRequest>(document,
cursor,
callback,
m_client,
linkUnderCursor);
}
return {};
} }
bool SymbolSupport::supportsFindUsages(TextEditor::TextDocument *document) const bool SymbolSupport::supportsFindUsages(TextEditor::TextDocument *document) const
@@ -317,10 +387,7 @@ std::optional<MessageId> SymbolSupport::findUsages(TextEditor::TextDocument *doc
handleFindReferencesResponse(response, wordUnderCursor, handler); handleFindReferencesResponse(response, wordUnderCursor, handler);
}); });
sendTextDocumentPositionParamsRequest(m_client, sendTextDocumentPositionParamsRequest(m_client, request);
request,
m_client->dynamicCapabilities(),
m_client->capabilities());
return request.id(); return request.id();
} }

View File

@@ -19,16 +19,19 @@ namespace LanguageServerProtocol { class MessageId; }
namespace LanguageClient { namespace LanguageClient {
class Client; class Client;
enum class LinkTarget { SymbolDef, SymbolTypeDef, SymbolImplementation };
class LANGUAGECLIENT_EXPORT SymbolSupport : public QObject class LANGUAGECLIENT_EXPORT SymbolSupport : public QObject
{ {
public: public:
explicit SymbolSupport(Client *client); explicit SymbolSupport(Client *client);
bool supportsFindLink(TextEditor::TextDocument *document, LinkTarget target) const;
LanguageServerProtocol::MessageId findLinkAt(TextEditor::TextDocument *document, LanguageServerProtocol::MessageId findLinkAt(TextEditor::TextDocument *document,
const QTextCursor &cursor, const QTextCursor &cursor,
Utils::LinkHandler callback, Utils::LinkHandler callback,
const bool resolveTarget); const bool resolveTarget,
const LinkTarget target);
bool supportsFindUsages(TextEditor::TextDocument *document) const; bool supportsFindUsages(TextEditor::TextDocument *document) const;
using ResultHandler = std::function<void(const QList<LanguageServerProtocol::Location> &)>; using ResultHandler = std::function<void(const QList<LanguageServerProtocol::Location> &)>;

View File

@@ -757,7 +757,11 @@ void QmlJSEditorWidget::findLinkAt(const QTextCursor &cursor,
bool /*inNextSplit*/) bool /*inNextSplit*/)
{ {
if (auto client = getQmllsClient(textDocument()->filePath())) { if (auto client = getQmllsClient(textDocument()->filePath())) {
client->findLinkAt(textDocument(), cursor, processLinkCallback, resolveTarget); client->findLinkAt(textDocument(),
cursor,
processLinkCallback,
resolveTarget,
LanguageClient::LinkTarget::SymbolDef);
return; return;
} }

View File

@@ -48,8 +48,7 @@ PlainTextEditorFactory::PlainTextEditorFactory()
setEditorActionHandlers(TextEditorActionHandler::Format | setEditorActionHandlers(TextEditorActionHandler::Format |
TextEditorActionHandler::UnCommentSelection | TextEditorActionHandler::UnCommentSelection |
TextEditorActionHandler::UnCollapseAll | TextEditorActionHandler::UnCollapseAll);
TextEditorActionHandler::FollowSymbolUnderCursor);
} }
PlainTextEditorFactory *PlainTextEditorFactory::instance() PlainTextEditorFactory *PlainTextEditorFactory::instance()

View File

@@ -717,6 +717,7 @@ public:
KSyntaxHighlighting::Definition currentDefinition(); KSyntaxHighlighting::Definition currentDefinition();
void rememberCurrentSyntaxDefinition(); void rememberCurrentSyntaxDefinition();
void openLinkUnderCursor(bool openInNextSplit); void openLinkUnderCursor(bool openInNextSplit);
void openTypeUnderCursor(bool openInNextSplit);
qreal charWidth() const; qreal charWidth() const;
public: public:
@@ -2446,6 +2447,16 @@ void TextEditorWidget::openLinkUnderCursorInNextSplit()
d->openLinkUnderCursor(!alwaysOpenLinksInNextSplit()); d->openLinkUnderCursor(!alwaysOpenLinksInNextSplit());
} }
void TextEditorWidget::openTypeUnderCursor()
{
d->openTypeUnderCursor(alwaysOpenLinksInNextSplit());
}
void TextEditorWidget::openTypeUnderCursorInNextSplit()
{
d->openTypeUnderCursor(!alwaysOpenLinksInNextSplit());
}
void TextEditorWidget::findUsages() void TextEditorWidget::findUsages()
{ {
emit requestUsages(textCursor()); emit requestUsages(textCursor());
@@ -3642,11 +3653,26 @@ void TextEditorWidgetPrivate::rememberCurrentSyntaxDefinition()
void TextEditorWidgetPrivate::openLinkUnderCursor(bool openInNextSplit) void TextEditorWidgetPrivate::openLinkUnderCursor(bool openInNextSplit)
{ {
q->findLinkAt(q->textCursor(), q->findLinkAt(
[openInNextSplit, self = QPointer<TextEditorWidget>(q)](const Link &symbolLink) { q->textCursor(),
if (self) [openInNextSplit, self = QPointer<TextEditorWidget>(q)](const Link &symbolLink) {
self->openLink(symbolLink, openInNextSplit); if (self)
}, true, openInNextSplit); self->openLink(symbolLink, openInNextSplit);
},
true,
openInNextSplit);
}
void TextEditorWidgetPrivate::openTypeUnderCursor(bool openInNextSplit)
{
q->findTypeAt(
q->textCursor(),
[openInNextSplit, self = QPointer<TextEditorWidget>(q)](const Link &symbolLink) {
if (self)
self->openLink(symbolLink, openInNextSplit);
},
true,
openInNextSplit);
} }
qreal TextEditorWidgetPrivate::charWidth() const qreal TextEditorWidgetPrivate::charWidth() const
@@ -6682,6 +6708,14 @@ void TextEditorWidget::findLinkAt(const QTextCursor &cursor,
emit requestLinkAt(cursor, callback, resolveTarget, inNextSplit); emit requestLinkAt(cursor, callback, resolveTarget, inNextSplit);
} }
void TextEditorWidget::findTypeAt(const QTextCursor &cursor,
const Utils::LinkHandler &callback,
bool resolveTarget,
bool inNextSplit)
{
emit requestTypeAt(cursor, callback, resolveTarget, inNextSplit);
}
bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit) bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit)
{ {
#ifdef WITH_TESTS #ifdef WITH_TESTS
@@ -8394,20 +8428,30 @@ void TextEditorWidget::setupFallBackEditor(Id id)
void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu) void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
{ {
if (optionalActions() & TextEditorActionHandler::FollowSymbolUnderCursor) {
const auto action = ActionManager::command(Constants::FOLLOW_SYMBOL_UNDER_CURSOR)->action();
if (!menu->actions().contains(action))
menu->addAction(action);
}
if (optionalActions() & TextEditorActionHandler::FollowTypeUnderCursor) {
const auto action = ActionManager::command(Constants::FOLLOW_SYMBOL_TO_TYPE)->action();
if (!menu->actions().contains(action))
menu->addAction(action);
}
if (optionalActions() & TextEditorActionHandler::FindUsage) { if (optionalActions() & TextEditorActionHandler::FindUsage) {
const auto findUsage = ActionManager::command(Constants::FIND_USAGES)->action(); const auto action = ActionManager::command(Constants::FIND_USAGES)->action();
if (!menu->actions().contains(findUsage)) if (!menu->actions().contains(action))
menu->addAction(findUsage); menu->addAction(action);
} }
if (optionalActions() & TextEditorActionHandler::RenameSymbol) { if (optionalActions() & TextEditorActionHandler::RenameSymbol) {
const auto renameSymbol = ActionManager::command(Constants::RENAME_SYMBOL)->action(); const auto action = ActionManager::command(Constants::RENAME_SYMBOL)->action();
if (!menu->actions().contains(renameSymbol)) if (!menu->actions().contains(action))
menu->addAction(renameSymbol); menu->addAction(action);
} }
if (optionalActions() & TextEditorActionHandler::CallHierarchy) { if (optionalActions() & TextEditorActionHandler::CallHierarchy) {
const auto callHierarchy = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action(); const auto action = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action();
if (!menu->actions().contains(callHierarchy)) if (!menu->actions().contains(action))
menu->addAction(callHierarchy); menu->addAction(action);
} }
menu->addSeparator(); menu->addSeparator();

View File

@@ -154,7 +154,6 @@ private:
Internal::BaseTextEditorPrivate *d; Internal::BaseTextEditorPrivate *d;
}; };
class TEXTEDITOR_EXPORT TextEditorWidget : public QPlainTextEdit class TEXTEDITOR_EXPORT TextEditorWidget : public QPlainTextEdit
{ {
Q_OBJECT Q_OBJECT
@@ -438,6 +437,8 @@ public:
void openLinkUnderCursor(); void openLinkUnderCursor();
void openLinkUnderCursorInNextSplit(); void openLinkUnderCursorInNextSplit();
void openTypeUnderCursor();
void openTypeUnderCursorInNextSplit();
virtual void findUsages(); virtual void findUsages();
virtual void renameSymbolUnderCursor(); virtual void renameSymbolUnderCursor();
@@ -496,6 +497,8 @@ signals:
void requestLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &callback, void requestLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &callback,
bool resolveTarget, bool inNextSplit); bool resolveTarget, bool inNextSplit);
void requestTypeAt(const QTextCursor &cursor, const Utils::LinkHandler &callback,
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 requestCallHierarchy(const QTextCursor &cursor);
@@ -588,6 +591,11 @@ protected:
bool resolveTarget = true, bool resolveTarget = true,
bool inNextSplit = false); bool inNextSplit = false);
virtual void findTypeAt(const QTextCursor &,
const Utils::LinkHandler &processLinkCallback,
bool resolveTarget = true,
bool inNextSplit = false);
/*! /*!
Returns whether the link was opened successfully. Returns whether the link was opened successfully.
*/ */

View File

@@ -118,6 +118,8 @@ public:
QAction *m_unfoldAllAction = nullptr; QAction *m_unfoldAllAction = nullptr;
QAction *m_followSymbolAction = nullptr; QAction *m_followSymbolAction = nullptr;
QAction *m_followSymbolInNextSplitAction = nullptr; QAction *m_followSymbolInNextSplitAction = nullptr;
QAction *m_followToTypeAction = nullptr;
QAction *m_followToTypeInNextSplitAction = nullptr;
QAction *m_findUsageAction = nullptr; QAction *m_findUsageAction = nullptr;
QAction *m_openCallHierarchyAction = nullptr; QAction *m_openCallHierarchyAction = nullptr;
QAction *m_renameSymbolAction = nullptr; QAction *m_renameSymbolAction = nullptr;
@@ -224,6 +226,12 @@ void TextEditorActionHandlerPrivate::createActions()
m_followSymbolInNextSplitAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT, m_followSymbolInNextSplitAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT,
[] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Follow Symbol Under Cursor in Next Split"), [] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Follow Symbol Under Cursor in Next Split"),
QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2"))); QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2")));
m_followToTypeAction = registerAction(FOLLOW_SYMBOL_TO_TYPE,
[] (TextEditorWidget *w) { w->openTypeUnderCursor(); }, true, Tr::tr("Follow Type Under Cursor"),
QKeySequence(Tr::tr("Ctrl+Shift+F2")));
m_followToTypeInNextSplitAction = registerAction(FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT,
[] (TextEditorWidget *w) { w->openTypeUnderCursorInNextSplit(); }, true, Tr::tr("Follow Type Under Cursor in Next Split"),
QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, Shift+F2") : Tr::tr("Ctrl+E, Ctrl+Shift+F2")));
m_findUsageAction = registerAction(FIND_USAGES, m_findUsageAction = registerAction(FIND_USAGES,
[] (TextEditorWidget *w) { w->findUsages(); }, true, Tr::tr("Find References to Symbol Under Cursor"), [] (TextEditorWidget *w) { w->findUsages(); }, true, Tr::tr("Find References to Symbol Under Cursor"),
QKeySequence(Tr::tr("Ctrl+Shift+U"))); QKeySequence(Tr::tr("Ctrl+Shift+U")));
@@ -492,6 +500,10 @@ void TextEditorActionHandlerPrivate::updateOptionalActions()
optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor); optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor);
m_followSymbolInNextSplitAction->setEnabled( m_followSymbolInNextSplitAction->setEnabled(
optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor); optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor);
m_followToTypeAction->setEnabled(
optionalActions & TextEditorActionHandler::FollowTypeUnderCursor);
m_followToTypeInNextSplitAction->setEnabled(
optionalActions & TextEditorActionHandler::FollowTypeUnderCursor);
m_findUsageAction->setEnabled( m_findUsageAction->setEnabled(
optionalActions & TextEditorActionHandler::FindUsage); optionalActions & TextEditorActionHandler::FindUsage);
m_jumpToFileAction->setEnabled( m_jumpToFileAction->setEnabled(

View File

@@ -34,10 +34,11 @@ public:
UnCommentSelection = 2, UnCommentSelection = 2,
UnCollapseAll = 4, UnCollapseAll = 4,
FollowSymbolUnderCursor = 8, FollowSymbolUnderCursor = 8,
JumpToFileUnderCursor = 16, FollowTypeUnderCursor = 16,
RenameSymbol = 32, JumpToFileUnderCursor = 32,
FindUsage = 64, RenameSymbol = 64,
CallHierarchy = 128 FindUsage = 128,
CallHierarchy = 256
}; };
using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>; using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>;

View File

@@ -206,6 +206,8 @@ const char INDENT[] = "TextEditor.Indent";
const char UNINDENT[] = "TextEditor.Unindent"; const char UNINDENT[] = "TextEditor.Unindent";
const char FOLLOW_SYMBOL_UNDER_CURSOR[] = "TextEditor.FollowSymbolUnderCursor"; const char FOLLOW_SYMBOL_UNDER_CURSOR[] = "TextEditor.FollowSymbolUnderCursor";
const char FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolUnderCursorInNextSplit"; const char FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolUnderCursorInNextSplit";
const char FOLLOW_SYMBOL_TO_TYPE[] = "TextEditor.FollowSymbolToType";
const char FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolToTypeInNextSplit";
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";