forked from qt-creator/qt-creator
ClangCodeModel: Implement declaration/definition switch via clangd
Change-Id: I522a415d76fbc5332e5cc1fdfd2d7ab19cb9ed64 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -230,7 +230,8 @@ public:
|
|||||||
void print(int indent = 0) const
|
void print(int indent = 0) const
|
||||||
{
|
{
|
||||||
(qDebug().noquote() << QByteArray(indent, ' ')).quote() << role() << kind()
|
(qDebug().noquote() << QByteArray(indent, ' ')).quote() << role() << kind()
|
||||||
<< detail().value_or(QString()) << arcana().value_or(QString());
|
<< detail().value_or(QString()) << arcana().value_or(QString())
|
||||||
|
<< range();
|
||||||
for (const AstNode &c : children().value_or(QList<AstNode>()))
|
for (const AstNode &c : children().value_or(QList<AstNode>()))
|
||||||
c.print(indent + 2);
|
c.print(indent + 2);
|
||||||
}
|
}
|
||||||
@@ -512,6 +513,58 @@ public:
|
|||||||
VirtualFunctionAssistProcessor *virtualFuncAssistProcessor = nullptr;
|
VirtualFunctionAssistProcessor *virtualFuncAssistProcessor = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SwitchDeclDefData {
|
||||||
|
public:
|
||||||
|
SwitchDeclDefData(quint64 id, TextEditor::TextDocument *doc, const QTextCursor &cursor,
|
||||||
|
CppTools::CppEditorWidgetInterface *editorWidget,
|
||||||
|
Utils::ProcessLinkCallback &&callback)
|
||||||
|
: id(id), document(doc), uri(DocumentUri::fromFilePath(doc->filePath())),
|
||||||
|
cursor(cursor), editorWidget(editorWidget), callback(std::move(callback)) {}
|
||||||
|
|
||||||
|
Utils::optional<AstNode> getFunctionNode() const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(ast, return {});
|
||||||
|
|
||||||
|
const QList<AstNode> path = getAstPath(*ast, Range(cursor));
|
||||||
|
for (auto it = path.rbegin(); it != path.rend(); ++it) {
|
||||||
|
if (it->role() == "declaration"
|
||||||
|
&& (it->kind() == "CXXMethod" || it->kind() == "CXXConversion"
|
||||||
|
|| it->kind() == "CXXConstructor" || it->kind() == "CXXDestructor")) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursorForFunctionName(const AstNode &functionNode) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(docSymbols, return {});
|
||||||
|
|
||||||
|
const auto symbolList = Utils::get_if<QList<DocumentSymbol>>(&*docSymbols);
|
||||||
|
if (!symbolList)
|
||||||
|
return {};
|
||||||
|
const Range &astRange = functionNode.range();
|
||||||
|
QList symbolsToCheck = *symbolList;
|
||||||
|
while (!symbolsToCheck.isEmpty()) {
|
||||||
|
const DocumentSymbol symbol = symbolsToCheck.takeFirst();
|
||||||
|
if (symbol.range() == astRange)
|
||||||
|
return symbol.selectionRange().start().toTextCursor(document->document());
|
||||||
|
if (symbol.range().contains(astRange))
|
||||||
|
symbolsToCheck << symbol.children().value_or(QList<DocumentSymbol>());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const quint64 id;
|
||||||
|
const QPointer<TextEditor::TextDocument> document;
|
||||||
|
const DocumentUri uri;
|
||||||
|
const QTextCursor cursor;
|
||||||
|
CppTools::CppEditorWidgetInterface * const editorWidget;
|
||||||
|
Utils::ProcessLinkCallback callback;
|
||||||
|
Utils::optional<DocumentSymbolsResult> docSymbols;
|
||||||
|
Utils::optional<AstNode> ast;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class ClangdClient::Private
|
class ClangdClient::Private
|
||||||
{
|
{
|
||||||
@@ -535,12 +588,16 @@ public:
|
|||||||
void handleDocumentInfoResults();
|
void handleDocumentInfoResults();
|
||||||
void closeTempDocuments();
|
void closeTempDocuments();
|
||||||
|
|
||||||
|
void handleDeclDefSwitchReplies();
|
||||||
|
|
||||||
ClangdClient * const q;
|
ClangdClient * const q;
|
||||||
QHash<quint64, ReferencesData> runningFindUsages;
|
QHash<quint64, ReferencesData> runningFindUsages;
|
||||||
Utils::optional<FollowSymbolData> followSymbolData;
|
Utils::optional<FollowSymbolData> followSymbolData;
|
||||||
|
Utils::optional<SwitchDeclDefData> switchDeclDefData;
|
||||||
Utils::optional<QVersionNumber> versionNumber;
|
Utils::optional<QVersionNumber> versionNumber;
|
||||||
quint64 nextFindUsagesKey = 0;
|
quint64 nextFindUsagesKey = 0;
|
||||||
quint64 nextFollowSymbolId = 0;
|
quint64 nextFollowSymbolId = 0;
|
||||||
|
quint64 nextSwitchDeclDefId = 0;
|
||||||
bool isFullyIndexed = false;
|
bool isFullyIndexed = false;
|
||||||
bool isTesting = false;
|
bool isTesting = false;
|
||||||
};
|
};
|
||||||
@@ -555,7 +612,6 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
|||||||
setSupportedLanguage(langFilter);
|
setSupportedLanguage(langFilter);
|
||||||
LanguageServerProtocol::ClientCapabilities caps = Client::defaultClientCapabilities();
|
LanguageServerProtocol::ClientCapabilities caps = Client::defaultClientCapabilities();
|
||||||
caps.clearExperimental();
|
caps.clearExperimental();
|
||||||
caps.clearTextDocument();
|
|
||||||
setClientCapabilities(caps);
|
setClientCapabilities(caps);
|
||||||
setLocatorsEnabled(false);
|
setLocatorsEnabled(false);
|
||||||
setProgressTitleForToken(indexingToken(), tr("Parsing C/C++ Files (clangd)"));
|
setProgressTitleForToken(indexingToken(), tr("Parsing C/C++ Files (clangd)"));
|
||||||
@@ -581,6 +637,15 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
|||||||
QTC_CHECK(d->runningFindUsages.isEmpty());
|
QTC_CHECK(d->runningFindUsages.isEmpty());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(documentSymbolCache(), &DocumentSymbolCache::gotSymbols, this,
|
||||||
|
[this](const DocumentUri &uri, const DocumentSymbolsResult &symbols) {
|
||||||
|
if (!d->switchDeclDefData || d->switchDeclDefData->uri != uri)
|
||||||
|
return;
|
||||||
|
d->switchDeclDefData->docSymbols = symbols;
|
||||||
|
if (d->switchDeclDefData->ast)
|
||||||
|
d->handleDeclDefSwitchReplies();
|
||||||
|
});
|
||||||
|
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,6 +1008,42 @@ void ClangdClient::followSymbol(
|
|||||||
sendContent(astRequest);
|
sendContent(astRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
||||||
|
CppTools::CppEditorWidgetInterface *editorWidget,
|
||||||
|
Utils::ProcessLinkCallback &&callback)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(documentOpen(document), openDocument(document));
|
||||||
|
|
||||||
|
qCDebug(clangdLog) << "switch decl/dev requested" << document->filePath()
|
||||||
|
<< cursor.blockNumber() << cursor.positionInBlock();
|
||||||
|
d->switchDeclDefData.emplace(++d->nextSwitchDeclDefId, document, cursor, editorWidget,
|
||||||
|
std::move(callback));
|
||||||
|
|
||||||
|
// Retrieve AST and document symbols.
|
||||||
|
AstParams astParams;
|
||||||
|
astParams.setTextDocument(TextDocumentIdentifier(d->switchDeclDefData->uri));
|
||||||
|
AstRequest astRequest(astParams);
|
||||||
|
astRequest.setResponseCallback([this, id = d->switchDeclDefData->id]
|
||||||
|
(const AstRequest::Response &response) {
|
||||||
|
qCDebug(clangdLog) << "received ast for decl/def switch";
|
||||||
|
if (!d->switchDeclDefData || d->switchDeclDefData->id != id
|
||||||
|
|| !d->switchDeclDefData->document)
|
||||||
|
return;
|
||||||
|
const auto result = response.result();
|
||||||
|
if (!result) {
|
||||||
|
d->switchDeclDefData.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d->switchDeclDefData->ast = *result;
|
||||||
|
if (d->switchDeclDefData->docSymbols)
|
||||||
|
d->handleDeclDefSwitchReplies();
|
||||||
|
|
||||||
|
});
|
||||||
|
sendContent(astRequest);
|
||||||
|
documentSymbolCache()->requestSymbols(d->switchDeclDefData->uri);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::handleGotoDefinitionResult()
|
void ClangdClient::Private::handleGotoDefinitionResult()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
||||||
@@ -1144,6 +1245,35 @@ void ClangdClient::Private::handleDocumentInfoResults()
|
|||||||
followSymbolData->virtualFuncAssistProcessor->finalize();
|
followSymbolData->virtualFuncAssistProcessor->finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangdClient::Private::handleDeclDefSwitchReplies()
|
||||||
|
{
|
||||||
|
if (!switchDeclDefData->document) {
|
||||||
|
switchDeclDefData.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the function declaration or definition associated with the cursor.
|
||||||
|
// For instance, the cursor could be somwehere inside a function body or
|
||||||
|
// on a function return type, or ...
|
||||||
|
if (clangdLog().isDebugEnabled())
|
||||||
|
switchDeclDefData->ast->print(0);
|
||||||
|
const Utils::optional<AstNode> functionNode = switchDeclDefData->getFunctionNode();
|
||||||
|
if (!functionNode) {
|
||||||
|
switchDeclDefData.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, the AST does not contain the location of the actual function name symbol,
|
||||||
|
// so we have to look for it in the document symbols.
|
||||||
|
const QTextCursor funcNameCursor = switchDeclDefData->cursorForFunctionName(*functionNode);
|
||||||
|
if (!funcNameCursor.isNull()) {
|
||||||
|
q->followSymbol(switchDeclDefData->document.data(), funcNameCursor,
|
||||||
|
switchDeclDefData->editorWidget, std::move(switchDeclDefData->callback),
|
||||||
|
true, false);
|
||||||
|
}
|
||||||
|
switchDeclDefData.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
||||||
{
|
{
|
||||||
if (!m_data)
|
if (!m_data)
|
||||||
|
@@ -61,6 +61,11 @@ public:
|
|||||||
bool resolveTarget,
|
bool resolveTarget,
|
||||||
bool openInSplit);
|
bool openInSplit);
|
||||||
|
|
||||||
|
void switchDeclDef(TextEditor::TextDocument *document,
|
||||||
|
const QTextCursor &cursor,
|
||||||
|
CppTools::CppEditorWidgetInterface *editorWidget,
|
||||||
|
Utils::ProcessLinkCallback &&callback);
|
||||||
|
|
||||||
void enableTesting();
|
void enableTesting();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@@ -251,5 +251,23 @@ void ClangFollowSymbol::findLink(const CppTools::CursorInEditor &data,
|
|||||||
m_watcher->setFuture(infoFuture);
|
m_watcher->setFuture(infoFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangFollowSymbol::switchDeclDef(const CppTools::CursorInEditor &data,
|
||||||
|
Utils::ProcessLinkCallback &&processLinkCallback,
|
||||||
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
|
CppTools::SymbolFinder *symbolFinder)
|
||||||
|
{
|
||||||
|
ClangdClient * const client
|
||||||
|
= ClangModelManagerSupport::instance()->clientForFile(data.filePath());
|
||||||
|
if (client && client->isFullyIndexed() && client->versionNumber() >= QVersionNumber(13)) {
|
||||||
|
client->switchDeclDef(data.textDocument(), data.cursor(), data.editorWidget(),
|
||||||
|
std::move(processLinkCallback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CppTools::CppModelManager::builtinFollowSymbol().switchDeclDef(
|
||||||
|
data, std::move(processLinkCallback), snapshot, documentFromSemanticInfo,
|
||||||
|
symbolFinder);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangCodeModel
|
} // namespace ClangCodeModel
|
||||||
|
@@ -43,9 +43,17 @@ public:
|
|||||||
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
CppTools::SymbolFinder *symbolFinder,
|
CppTools::SymbolFinder *symbolFinder,
|
||||||
bool inNextSplit) override;
|
bool inNextSplit) override;
|
||||||
|
|
||||||
|
void switchDeclDef(const CppTools::CursorInEditor &data,
|
||||||
|
Utils::ProcessLinkCallback &&processLinkCallback,
|
||||||
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
|
CppTools::SymbolFinder *symbolFinder) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using FutureSymbolWatcher = QFutureWatcher<CppTools::SymbolInfo>;
|
using FutureSymbolWatcher = QFutureWatcher<CppTools::SymbolInfo>;
|
||||||
std::unique_ptr<FutureSymbolWatcher> m_watcher;
|
std::unique_ptr<FutureSymbolWatcher> m_watcher;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -711,78 +711,15 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit)
|
|||||||
if (!d->m_modelManager)
|
if (!d->m_modelManager)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!d->m_lastSemanticInfo.doc)
|
const CursorInEditor cursor(textCursor(), textDocument()->filePath(), this, textDocument());
|
||||||
return;
|
auto callback = [self = QPointer(this),
|
||||||
|
split = inNextSplit != alwaysOpenLinksInNextSplit()](const Link &link) {
|
||||||
// Find function declaration or definition under cursor
|
if (self && link.hasValidTarget())
|
||||||
Function *functionDefinitionSymbol = nullptr;
|
self->openLink(link, split);
|
||||||
Symbol *functionDeclarationSymbol = nullptr;
|
};
|
||||||
Symbol *declarationSymbol = nullptr;
|
followSymbolInterface().switchDeclDef(cursor, std::move(callback),
|
||||||
|
d->m_modelManager->snapshot(), d->m_lastSemanticInfo.doc,
|
||||||
ASTPath astPathFinder(d->m_lastSemanticInfo.doc);
|
d->m_modelManager->symbolFinder());
|
||||||
const QList<AST *> astPath = astPathFinder(textCursor());
|
|
||||||
|
|
||||||
for (AST *ast : astPath) {
|
|
||||||
if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) {
|
|
||||||
if ((functionDefinitionSymbol = functionDefinitionAST->symbol))
|
|
||||||
break; // Function definition found!
|
|
||||||
} else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) {
|
|
||||||
if (List<Symbol *> *symbols = simpleDeclaration->symbols) {
|
|
||||||
if (Symbol *symbol = symbols->value) {
|
|
||||||
if (symbol->isDeclaration()) {
|
|
||||||
declarationSymbol = symbol;
|
|
||||||
if (symbol->type()->isFunctionType()) {
|
|
||||||
functionDeclarationSymbol = symbol;
|
|
||||||
break; // Function declaration found!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link to function definition/declaration
|
|
||||||
Utils::Link symbolLink;
|
|
||||||
if (functionDeclarationSymbol) {
|
|
||||||
Symbol *symbol = d->m_modelManager->symbolFinder()
|
|
||||||
->findMatchingDefinition(functionDeclarationSymbol, d->m_modelManager->snapshot());
|
|
||||||
if (symbol)
|
|
||||||
symbolLink = symbol->toLink();
|
|
||||||
} else if (declarationSymbol) {
|
|
||||||
Symbol *symbol = d->m_modelManager->symbolFinder()
|
|
||||||
->findMatchingVarDefinition(declarationSymbol, d->m_modelManager->snapshot());
|
|
||||||
if (symbol)
|
|
||||||
symbolLink = symbol->toLink();
|
|
||||||
} else if (functionDefinitionSymbol) {
|
|
||||||
const Snapshot snapshot = d->m_modelManager->snapshot();
|
|
||||||
LookupContext context(d->m_lastSemanticInfo.doc, snapshot);
|
|
||||||
ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
|
|
||||||
const QList<LookupItem> declarations
|
|
||||||
= context.lookup(functionDefinitionSymbol->name(),
|
|
||||||
functionDefinitionSymbol->enclosingScope());
|
|
||||||
|
|
||||||
QList<Symbol *> best;
|
|
||||||
foreach (const LookupItem &r, declarations) {
|
|
||||||
if (Symbol *decl = r.declaration()) {
|
|
||||||
if (Function *funTy = decl->type()->asFunctionType()) {
|
|
||||||
if (funTy->match(functionDefinitionSymbol)) {
|
|
||||||
if (decl != functionDefinitionSymbol && binding == r.binding())
|
|
||||||
best.prepend(decl);
|
|
||||||
else
|
|
||||||
best.append(decl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (best.isEmpty())
|
|
||||||
return;
|
|
||||||
symbolLink = best.first()->toLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open Editor at link position
|
|
||||||
if (symbolLink.hasValidTarget())
|
|
||||||
openLink(symbolLink, inNextSplit != alwaysOpenLinksInNextSplit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CppEditorWidget::findLinkAt(const QTextCursor &cursor,
|
void CppEditorWidget::findLinkAt(const QTextCursor &cursor,
|
||||||
|
@@ -297,14 +297,12 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
QSKIP("fuzzy matching is not supposed to work with clangd"); // TODO: Implement fallback as we do with libclang
|
QSKIP("fuzzy matching is not supposed to work with clangd"); // TODO: Implement fallback as we do with libclang
|
||||||
if (tag == "baseClassFunctionIntroducedByUsingDeclaration")
|
if (tag == "baseClassFunctionIntroducedByUsingDeclaration")
|
||||||
QSKIP("clangd points to the using declaration");
|
QSKIP("clangd points to the using declaration");
|
||||||
if (tag == "classDestructor")
|
if (tag == "classDestructor" || tag == "fromDestructorDefinitionSymbol"
|
||||||
|
|| tag == "fromDestructorBody") {
|
||||||
QSKIP("clangd wants the cursor before the ~ character");
|
QSKIP("clangd wants the cursor before the ~ character");
|
||||||
|
}
|
||||||
if (curTestName == "test_FollowSymbolUnderCursor_classOperator_inOp")
|
if (curTestName == "test_FollowSymbolUnderCursor_classOperator_inOp")
|
||||||
QSKIP("clangd goes to operator name first");
|
QSKIP("clangd goes to operator name first");
|
||||||
if (tag == "fromFunctionBody" || tag == "fromReturnType"
|
|
||||||
|| tag == "conversionOperatorDecl2Def") {
|
|
||||||
QSKIP("TODO: explicit decl/def switch not yet supported with clangd");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write files to disk
|
// Write files to disk
|
||||||
@@ -415,7 +413,9 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SwitchBetweenMethodDeclarationDefinitionAction:
|
case SwitchBetweenMethodDeclarationDefinitionAction:
|
||||||
if (CppTools::codeModelSettings()->useClangd())
|
// Some test cases were erroneously added as decl/def, but they are really
|
||||||
|
// follow symbol functionality (in commit a0764603d0).
|
||||||
|
if (useClangd && tag.endsWith("Var"))
|
||||||
initialTestFile->m_editorWidget->openLinkUnderCursor();
|
initialTestFile->m_editorWidget->openLinkUnderCursor();
|
||||||
else
|
else
|
||||||
CppEditorPlugin::instance()->switchDeclarationDefinition();
|
CppEditorPlugin::instance()->switchDeclarationDefinition();
|
||||||
@@ -623,6 +623,126 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_data()
|
|||||||
"}\n" // Line 10
|
"}\n" // Line 10
|
||||||
);
|
);
|
||||||
|
|
||||||
|
QTest::newRow("fromConstructorDeclarationSymbol") << _(
|
||||||
|
"class C\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" C();\n"
|
||||||
|
" int @function();\n" // Line 5
|
||||||
|
"};\n"
|
||||||
|
) << _(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"C::C()\n"
|
||||||
|
"{\n"
|
||||||
|
"}\n" // Line 5
|
||||||
|
"\n"
|
||||||
|
"int C::$function()\n"
|
||||||
|
"{\n"
|
||||||
|
" return 1 + 1;\n"
|
||||||
|
"}\n" // Line 10
|
||||||
|
);
|
||||||
|
|
||||||
|
QTest::newRow("fromConstructorDefinitionSymbol") << _(
|
||||||
|
"class C\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" $C();\n"
|
||||||
|
" int function();\n"
|
||||||
|
"};\n"
|
||||||
|
) << _(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"C::@C()\n"
|
||||||
|
"{\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"int C::function()\n"
|
||||||
|
"{\n"
|
||||||
|
" return 1 + 1;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
QTest::newRow("fromConstructorBody") << _(
|
||||||
|
"class C\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" $C();\n"
|
||||||
|
" int function();\n"
|
||||||
|
"};\n"
|
||||||
|
) << _(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"C::C()\n"
|
||||||
|
"{@\n"
|
||||||
|
"}\n" // Line 5
|
||||||
|
"\n"
|
||||||
|
"int C::function()\n"
|
||||||
|
"{\n"
|
||||||
|
" return 1 + 1;\n"
|
||||||
|
"}\n" // Line 10
|
||||||
|
);
|
||||||
|
|
||||||
|
QTest::newRow("fromDestructorDeclarationSymbol") << _(
|
||||||
|
"class C\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" @C();\n"
|
||||||
|
" int function();\n" // Line 5
|
||||||
|
"};\n"
|
||||||
|
) << _(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"C::$C()\n"
|
||||||
|
"{\n"
|
||||||
|
"}\n" // Line 5
|
||||||
|
"\n"
|
||||||
|
"int C::function()\n"
|
||||||
|
"{\n"
|
||||||
|
" return 1 + 1;\n"
|
||||||
|
"}\n" // Line 10
|
||||||
|
);
|
||||||
|
|
||||||
|
QTest::newRow("fromDestructorDefinitionSymbol") << _(
|
||||||
|
"class C\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" ~$C();\n"
|
||||||
|
" int function();\n"
|
||||||
|
"};\n"
|
||||||
|
) << _(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"C::@~C()\n"
|
||||||
|
"{\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"int C::function()\n"
|
||||||
|
"{\n"
|
||||||
|
" return 1 + 1;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
QTest::newRow("fromDestructorBody") << _(
|
||||||
|
"class C\n"
|
||||||
|
"{\n"
|
||||||
|
"public:\n"
|
||||||
|
" ~$C();\n"
|
||||||
|
" int function();\n"
|
||||||
|
"};\n"
|
||||||
|
) << _(
|
||||||
|
"#include \"file.h\"\n"
|
||||||
|
"\n"
|
||||||
|
"C::~C()\n"
|
||||||
|
"{@\n"
|
||||||
|
"}\n" // Line 5
|
||||||
|
"\n"
|
||||||
|
"int C::function()\n"
|
||||||
|
"{\n"
|
||||||
|
" return 1 + 1;\n"
|
||||||
|
"}\n" // Line 10
|
||||||
|
);
|
||||||
|
|
||||||
QTest::newRow("fromReturnType") << _(
|
QTest::newRow("fromReturnType") << _(
|
||||||
"class C\n"
|
"class C\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
@@ -796,6 +796,83 @@ void FollowSymbolUnderCursor::findLink(
|
|||||||
processLinkCallback(Link());
|
processLinkCallback(Link());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FollowSymbolUnderCursor::switchDeclDef(
|
||||||
|
const CursorInEditor &data,
|
||||||
|
Utils::ProcessLinkCallback &&processLinkCallback,
|
||||||
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
|
SymbolFinder *symbolFinder)
|
||||||
|
{
|
||||||
|
if (!documentFromSemanticInfo) {
|
||||||
|
processLinkCallback({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find function declaration or definition under cursor
|
||||||
|
Function *functionDefinitionSymbol = nullptr;
|
||||||
|
Symbol *functionDeclarationSymbol = nullptr;
|
||||||
|
Symbol *declarationSymbol = nullptr;
|
||||||
|
|
||||||
|
ASTPath astPathFinder(documentFromSemanticInfo);
|
||||||
|
const QList<AST *> astPath = astPathFinder(data.cursor());
|
||||||
|
|
||||||
|
for (AST *ast : astPath) {
|
||||||
|
if (FunctionDefinitionAST *functionDefinitionAST = ast->asFunctionDefinition()) {
|
||||||
|
if ((functionDefinitionSymbol = functionDefinitionAST->symbol))
|
||||||
|
break; // Function definition found!
|
||||||
|
} else if (SimpleDeclarationAST *simpleDeclaration = ast->asSimpleDeclaration()) {
|
||||||
|
if (List<Symbol *> *symbols = simpleDeclaration->symbols) {
|
||||||
|
if (Symbol *symbol = symbols->value) {
|
||||||
|
if (symbol->isDeclaration()) {
|
||||||
|
declarationSymbol = symbol;
|
||||||
|
if (symbol->type()->isFunctionType()) {
|
||||||
|
functionDeclarationSymbol = symbol;
|
||||||
|
break; // Function declaration found!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link to function definition/declaration
|
||||||
|
Utils::Link symbolLink;
|
||||||
|
if (functionDeclarationSymbol) {
|
||||||
|
Symbol *symbol = symbolFinder->findMatchingDefinition(functionDeclarationSymbol, snapshot);
|
||||||
|
if (symbol)
|
||||||
|
symbolLink = symbol->toLink();
|
||||||
|
} else if (declarationSymbol) {
|
||||||
|
Symbol *symbol = symbolFinder->findMatchingVarDefinition(declarationSymbol, snapshot);
|
||||||
|
if (symbol)
|
||||||
|
symbolLink = symbol->toLink();
|
||||||
|
} else if (functionDefinitionSymbol) {
|
||||||
|
LookupContext context(documentFromSemanticInfo, snapshot);
|
||||||
|
ClassOrNamespace *binding = context.lookupType(functionDefinitionSymbol);
|
||||||
|
const QList<LookupItem> declarations
|
||||||
|
= context.lookup(functionDefinitionSymbol->name(),
|
||||||
|
functionDefinitionSymbol->enclosingScope());
|
||||||
|
|
||||||
|
QList<Symbol *> best;
|
||||||
|
foreach (const LookupItem &r, declarations) {
|
||||||
|
if (Symbol *decl = r.declaration()) {
|
||||||
|
if (Function *funTy = decl->type()->asFunctionType()) {
|
||||||
|
if (funTy->match(functionDefinitionSymbol)) {
|
||||||
|
if (decl != functionDefinitionSymbol && binding == r.binding())
|
||||||
|
best.prepend(decl);
|
||||||
|
else
|
||||||
|
best.append(decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best.isEmpty())
|
||||||
|
return;
|
||||||
|
symbolLink = best.first()->toLink();
|
||||||
|
}
|
||||||
|
processLinkCallback(symbolLink);
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<VirtualFunctionAssistProvider> FollowSymbolUnderCursor::virtualFunctionAssistProvider()
|
QSharedPointer<VirtualFunctionAssistProvider> FollowSymbolUnderCursor::virtualFunctionAssistProvider()
|
||||||
{
|
{
|
||||||
return m_virtualFunctionAssistProvider;
|
return m_virtualFunctionAssistProvider;
|
||||||
|
@@ -44,6 +44,12 @@ public:
|
|||||||
CppTools::SymbolFinder *symbolFinder,
|
CppTools::SymbolFinder *symbolFinder,
|
||||||
bool inNextSplit) override;
|
bool inNextSplit) override;
|
||||||
|
|
||||||
|
void switchDeclDef(const CursorInEditor &data,
|
||||||
|
Utils::ProcessLinkCallback &&processLinkCallback,
|
||||||
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
|
SymbolFinder *symbolFinder) override;
|
||||||
|
|
||||||
QSharedPointer<VirtualFunctionAssistProvider> virtualFunctionAssistProvider();
|
QSharedPointer<VirtualFunctionAssistProvider> virtualFunctionAssistProvider();
|
||||||
void setVirtualFunctionAssistProvider(
|
void setVirtualFunctionAssistProvider(
|
||||||
const QSharedPointer<VirtualFunctionAssistProvider> &provider);
|
const QSharedPointer<VirtualFunctionAssistProvider> &provider);
|
||||||
|
@@ -472,6 +472,11 @@ RefactoringEngineInterface *CppModelManager::builtinRefactoringEngine()
|
|||||||
return instance()->d->m_refactoringEngines.value(RefactoringEngineType::BuiltIn);
|
return instance()->d->m_refactoringEngines.value(RefactoringEngineType::BuiltIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FollowSymbolInterface &CppModelManager::builtinFollowSymbol()
|
||||||
|
{
|
||||||
|
return instance()->d->m_builtinModelManagerSupport->followSymbolInterface();
|
||||||
|
}
|
||||||
|
|
||||||
template<class FilterClass>
|
template<class FilterClass>
|
||||||
static void setFilter(std::unique_ptr<FilterClass> &filter,
|
static void setFilter(std::unique_ptr<FilterClass> &filter,
|
||||||
std::unique_ptr<FilterClass> &&newFilter)
|
std::unique_ptr<FilterClass> &&newFilter)
|
||||||
|
@@ -218,6 +218,7 @@ public:
|
|||||||
RefactoringEngineInterface *refactoringEngine);
|
RefactoringEngineInterface *refactoringEngine);
|
||||||
static void removeRefactoringEngine(RefactoringEngineType type);
|
static void removeRefactoringEngine(RefactoringEngineType type);
|
||||||
static RefactoringEngineInterface *builtinRefactoringEngine();
|
static RefactoringEngineInterface *builtinRefactoringEngine();
|
||||||
|
static FollowSymbolInterface &builtinFollowSymbol();
|
||||||
|
|
||||||
void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
||||||
void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
||||||
|
@@ -49,6 +49,11 @@ public:
|
|||||||
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
SymbolFinder *symbolFinder,
|
SymbolFinder *symbolFinder,
|
||||||
bool inNextSplit) = 0;
|
bool inNextSplit) = 0;
|
||||||
|
virtual void switchDeclDef(const CursorInEditor &data,
|
||||||
|
Utils::ProcessLinkCallback &&processLinkCallback,
|
||||||
|
const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||||
|
SymbolFinder *symbolFinder) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace CppTools
|
} // namespace CppTools
|
||||||
|
Reference in New Issue
Block a user