forked from qt-creator/qt-creator
ClangCodeModel: Let user choose the override
.... when following virtual function calls. This brings us up to par with the built-in code model. We do lose the icons, but they are of very little use in this context. Change-Id: I29b27d538e7277d06a5af7acee07bddb6eb94c98 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -55,7 +55,13 @@ public:
|
||||
{ return linkTextStart != linkTextEnd; }
|
||||
|
||||
bool operator==(const Link &other) const
|
||||
{ return linkTextStart == other.linkTextStart && linkTextEnd == other.linkTextEnd; }
|
||||
{
|
||||
return targetFilePath == other.targetFilePath
|
||||
&& targetLine == other.targetLine
|
||||
&& targetColumn == other.targetColumn
|
||||
&& linkTextStart == other.linkTextStart
|
||||
&& linkTextEnd == other.linkTextEnd;
|
||||
}
|
||||
|
||||
int linkTextStart = -1;
|
||||
int linkTextEnd = -1;
|
||||
|
@@ -25,24 +25,35 @@
|
||||
|
||||
#include "clangdclient.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/find/searchresultitem.h>
|
||||
#include <coreplugin/find/searchresultwindow.h>
|
||||
#include <cplusplus/FindUsages.h>
|
||||
#include <cpptools/cppcodemodelsettings.h>
|
||||
#include <cpptools/cppeditorwidgetinterface.h>
|
||||
#include <cpptools/cppfindreferences.h>
|
||||
#include <cpptools/cpptoolsreuse.h>
|
||||
#include <cpptools/cppvirtualfunctionassistprovider.h>
|
||||
#include <cpptools/cppvirtualfunctionproposalitem.h>
|
||||
#include <languageclient/languageclientinterface.h>
|
||||
#include <projectexplorer/projecttree.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <texteditor/basefilefind.h>
|
||||
#include <texteditor/codeassist/assistinterface.h>
|
||||
#include <texteditor/codeassist/iassistprocessor.h>
|
||||
#include <texteditor/codeassist/iassistprovider.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <QPair>
|
||||
#include <QPointer>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
using namespace Core;
|
||||
using namespace LanguageClient;
|
||||
@@ -59,7 +70,12 @@ class AstParams : public JsonObject
|
||||
{
|
||||
public:
|
||||
AstParams() {}
|
||||
AstParams(const TextDocumentIdentifier &document, const Range &range);
|
||||
AstParams(const TextDocumentIdentifier &document, const Range &range)
|
||||
{
|
||||
setTextDocument(document);
|
||||
setRange(range);
|
||||
}
|
||||
|
||||
using JsonObject::JsonObject;
|
||||
|
||||
// The open file to inspect.
|
||||
@@ -119,6 +135,16 @@ public:
|
||||
return detail() && detail().value() == s;
|
||||
}
|
||||
|
||||
bool isMemberFunctionCall() const
|
||||
{
|
||||
return role() == "expression" && kind() == "Member" && arcanaContains("member function");
|
||||
}
|
||||
|
||||
bool isPureVirtualDeclaration() const
|
||||
{
|
||||
return role() == "declaration" && kind() == "CXXMethod" && arcanaContains("virtual pure");
|
||||
}
|
||||
|
||||
QString type() const
|
||||
{
|
||||
const Utils::optional<QString> arcanaString = arcana();
|
||||
@@ -175,6 +201,15 @@ public:
|
||||
- openingQuoteOffset - 1);
|
||||
}
|
||||
|
||||
// For debugging.
|
||||
void print(int indent = 0) const
|
||||
{
|
||||
(qDebug().noquote() << QByteArray(indent, ' ')).quote() << role() << kind()
|
||||
<< detail().value_or(QString()) << arcana().value_or(QString());
|
||||
for (const AstNode &c : children().value_or(QList<AstNode>()))
|
||||
c.print(indent + 2);
|
||||
}
|
||||
|
||||
bool isValid() const override
|
||||
{
|
||||
return contains(roleKey) && contains(kindKey);
|
||||
@@ -283,6 +318,40 @@ public:
|
||||
explicit AstRequest(const AstParams ¶ms) : Request("textDocument/ast", params) {}
|
||||
};
|
||||
|
||||
class SymbolDetails : public JsonObject
|
||||
{
|
||||
public:
|
||||
using JsonObject::JsonObject;
|
||||
|
||||
static constexpr char usrKey[] = "usr";
|
||||
|
||||
// the unqualified name of the symbol
|
||||
QString name() const { return typedValue<QString>(nameKey); }
|
||||
|
||||
// the enclosing namespace, class etc (without trailing ::)
|
||||
// [NOTE: This is not true, the trailing colons are included]
|
||||
QString containerName() const { return typedValue<QString>(containerNameKey); }
|
||||
|
||||
// the clang-specific “unified symbol resolution” identifier
|
||||
QString usr() const { return typedValue<QString>(usrKey); }
|
||||
|
||||
// the clangd-specific opaque symbol ID
|
||||
Utils::optional<QString> id() const { return optionalValue<QString>(idKey); }
|
||||
|
||||
bool isValid() const override
|
||||
{
|
||||
return contains(nameKey) && contains(containerNameKey) && contains(usrKey);
|
||||
}
|
||||
};
|
||||
|
||||
class SymbolInfoRequest : public Request<LanguageClientArray<SymbolDetails>, std::nullptr_t, TextDocumentPositionParams>
|
||||
{
|
||||
public:
|
||||
using Request::Request;
|
||||
explicit SymbolInfoRequest(const TextDocumentPositionParams ¶ms)
|
||||
: Request("textDocument/symbolInfo", params) {}
|
||||
};
|
||||
|
||||
static BaseClientInterface *clientInterface(const Utils::FilePath &jsonDbDir)
|
||||
{
|
||||
Utils::CommandLine cmd{CppTools::codeModelSettings()->clangdFilePath(),
|
||||
@@ -318,6 +387,86 @@ public:
|
||||
bool canceled = false;
|
||||
};
|
||||
|
||||
using SymbolData = QPair<QString, Utils::Link>;
|
||||
using SymbolDataList = QList<SymbolData>;
|
||||
|
||||
class ClangdClient::VirtualFunctionAssistProcessor : public TextEditor::IAssistProcessor
|
||||
{
|
||||
public:
|
||||
VirtualFunctionAssistProcessor(ClangdClient::Private *data) : m_data(data) {}
|
||||
|
||||
void cancel() override;
|
||||
bool running() override { return m_data; }
|
||||
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextEditor::IAssistProposal *immediateProposal(const TextEditor::AssistInterface *) override;
|
||||
|
||||
ClangdClient::Private *m_data = nullptr;
|
||||
};
|
||||
|
||||
class ClangdClient::VirtualFunctionAssistProvider : public TextEditor::IAssistProvider
|
||||
{
|
||||
public:
|
||||
VirtualFunctionAssistProvider(ClangdClient::Private *data) : m_data(data) {}
|
||||
|
||||
private:
|
||||
RunType runType() const override { return Asynchronous; }
|
||||
TextEditor::IAssistProcessor *createProcessor() const override;
|
||||
|
||||
ClangdClient::Private * const m_data;
|
||||
};
|
||||
|
||||
class ClangdClient::FollowSymbolData {
|
||||
public:
|
||||
FollowSymbolData(ClangdClient *q, quint64 id, const QTextCursor &cursor,
|
||||
CppTools::CppEditorWidgetInterface *editorWidget,
|
||||
const DocumentUri &uri, Utils::ProcessLinkCallback &&callback,
|
||||
bool openInSplit)
|
||||
: q(q), id(id), cursor(cursor), editorWidget(editorWidget), uri(uri),
|
||||
callback(std::move(callback)), virtualFuncAssistProvider(q->d),
|
||||
openInSplit(openInSplit) {}
|
||||
|
||||
~FollowSymbolData()
|
||||
{
|
||||
closeTempDocuments();
|
||||
if (virtualFuncAssistProcessor)
|
||||
virtualFuncAssistProcessor->cancel();
|
||||
for (const MessageId &id : qAsConst(pendingSymbolInfoRequests))
|
||||
q->cancelRequest(id);
|
||||
}
|
||||
|
||||
void closeTempDocuments()
|
||||
{
|
||||
for (const Utils::FilePath &fp : qAsConst(openedFiles))
|
||||
q->closeExtraFile(fp);
|
||||
openedFiles.clear();
|
||||
}
|
||||
|
||||
ClangdClient * const q;
|
||||
const quint64 id;
|
||||
const QTextCursor cursor;
|
||||
CppTools::CppEditorWidgetInterface * const editorWidget;
|
||||
const DocumentUri uri;
|
||||
const Utils::ProcessLinkCallback callback;
|
||||
VirtualFunctionAssistProvider virtualFuncAssistProvider;
|
||||
QList<MessageId> pendingSymbolInfoRequests;
|
||||
const bool openInSplit;
|
||||
|
||||
Utils::Link defLink;
|
||||
AstNode cursorNode;
|
||||
SymbolDataList symbolsToDisplay;
|
||||
std::set<Utils::FilePath> openedFiles;
|
||||
VirtualFunctionAssistProcessor *virtualFuncAssistProcessor = nullptr;
|
||||
};
|
||||
|
||||
|
||||
class ClangdClient::Private
|
||||
{
|
||||
public:
|
||||
@@ -334,10 +483,17 @@ public:
|
||||
void reportAllSearchResultsAndFinish(ReferencesData &data);
|
||||
void finishSearch(const ReferencesData &refData, bool canceled);
|
||||
|
||||
void handleGotoDefinitionResult();
|
||||
void handleGotoImplementationResult(const GotoImplementationRequest::Response &response);
|
||||
void handleDocumentInfoResults();
|
||||
void closeTempDocuments();
|
||||
|
||||
ClangdClient * const q;
|
||||
QHash<quint64, ReferencesData> runningFindUsages;
|
||||
Utils::optional<FollowSymbolData> followSymbolData;
|
||||
Utils::optional<QVersionNumber> versionNumber;
|
||||
quint64 nextFindUsagesKey = 0;
|
||||
quint64 nextFollowSymbolId = 0;
|
||||
bool isFullyIndexed = false;
|
||||
bool isTesting = false;
|
||||
};
|
||||
@@ -380,6 +536,10 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
||||
|
||||
ClangdClient::~ClangdClient()
|
||||
{
|
||||
if (d->followSymbolData) {
|
||||
d->followSymbolData->openedFiles.clear();
|
||||
d->followSymbolData->pendingSymbolInfoRequests.clear();
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
@@ -672,6 +832,234 @@ void ClangdClient::Private::finishSearch(const ReferencesData &refData, bool can
|
||||
runningFindUsages.remove(refData.key);
|
||||
}
|
||||
|
||||
void ClangdClient::followSymbol(
|
||||
TextEditor::TextDocument *document,
|
||||
const QTextCursor &cursor,
|
||||
CppTools::CppEditorWidgetInterface *editorWidget,
|
||||
Utils::ProcessLinkCallback &&callback,
|
||||
bool resolveTarget,
|
||||
bool openInSplit
|
||||
)
|
||||
{
|
||||
QTC_ASSERT(documentOpen(document), openDocument(document));
|
||||
if (!resolveTarget) {
|
||||
d->followSymbolData.reset();
|
||||
symbolSupport().findLinkAt(document, cursor, std::move(callback), false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->followSymbolData.emplace(this, ++d->nextFollowSymbolId, cursor, editorWidget,
|
||||
DocumentUri::fromFilePath(document->filePath()),
|
||||
std::move(callback), openInSplit);
|
||||
|
||||
// Step 1: Follow the symbol via "Go to Definition". At the same time, request the
|
||||
// AST node corresponding to the cursor position, so we can find out whether
|
||||
// we have to look for overrides.
|
||||
const auto gotoDefCallback = [this, id = d->followSymbolData->id](const Utils::Link &link) {
|
||||
if (!link.hasValidTarget()) {
|
||||
d->followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
if (!d->followSymbolData || id != d->followSymbolData->id)
|
||||
return;
|
||||
d->followSymbolData->defLink = link;
|
||||
if (d->followSymbolData->cursorNode.isValid())
|
||||
d->handleGotoDefinitionResult();
|
||||
};
|
||||
symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true);
|
||||
|
||||
AstRequest astRequest(AstParams(TextDocumentIdentifier(d->followSymbolData->uri),
|
||||
Range(cursor)));
|
||||
astRequest.setResponseCallback([this, id = d->followSymbolData->id](
|
||||
const AstRequest::Response &response) {
|
||||
if (!d->followSymbolData || d->followSymbolData->id != id)
|
||||
return;
|
||||
const auto result = response.result();
|
||||
if (!result) {
|
||||
d->followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
d->followSymbolData->cursorNode = *result;
|
||||
if (d->followSymbolData->defLink.hasValidTarget())
|
||||
d->handleGotoDefinitionResult();
|
||||
});
|
||||
sendContent(astRequest);
|
||||
}
|
||||
|
||||
void ClangdClient::Private::handleGotoDefinitionResult()
|
||||
{
|
||||
QTC_ASSERT(followSymbolData->defLink.hasValidTarget(), return);
|
||||
QTC_ASSERT(followSymbolData->cursorNode.isValid(), return);
|
||||
|
||||
// No dis-ambiguation necessary. Call back with the link and finish.
|
||||
if (!followSymbolData->cursorNode.isMemberFunctionCall()
|
||||
&& !followSymbolData->cursorNode.isPureVirtualDeclaration()) {
|
||||
followSymbolData->callback(followSymbolData->defLink);
|
||||
followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Get all possible overrides via "Go to Implementation".
|
||||
// Note that we have to do this for all member function calls, because
|
||||
// we cannot tell here whether the member function is virtual.
|
||||
const TextDocumentIdentifier documentId(followSymbolData->uri);
|
||||
const Position pos(followSymbolData->cursor);
|
||||
GotoImplementationRequest req(TextDocumentPositionParams(documentId, pos));
|
||||
req.setResponseCallback([this, id = followSymbolData->id](
|
||||
const GotoImplementationRequest::Response &response) {
|
||||
if (!followSymbolData || id != followSymbolData->id)
|
||||
return;
|
||||
handleGotoImplementationResult(response);
|
||||
});
|
||||
q->sendContent(req);
|
||||
}
|
||||
|
||||
void ClangdClient::Private::handleGotoImplementationResult(
|
||||
const GotoImplementationRequest::Response &response)
|
||||
{
|
||||
if (!response.result()) {
|
||||
followSymbolData->callback(followSymbolData->defLink);
|
||||
followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QList<Utils::Link> links;
|
||||
const GotoResult result = response.result().value();
|
||||
if (const auto ploc = Utils::get_if<Location>(&result))
|
||||
links = {ploc->toLink()};
|
||||
if (const auto plloc = Utils::get_if<QList<Location>>(&result))
|
||||
links = Utils::transform(*plloc, [](const Location &loc) { return loc.toLink(); });
|
||||
if (!links.contains(followSymbolData->defLink))
|
||||
links.prepend(followSymbolData->defLink);
|
||||
if (links.size() == 1) {
|
||||
followSymbolData->callback(links.first());
|
||||
followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: We found more than one possible target.
|
||||
// Make a symbol info request for each link to get the class names.
|
||||
// This is the expensive part, so we must start the code assist procedure now.
|
||||
const bool textEditorWidgetStillAlive = Utils::anyOf(EditorManager::visibleEditors(),
|
||||
[this](IEditor *editor) {
|
||||
const auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
|
||||
return textEditor && dynamic_cast<CppTools::CppEditorWidgetInterface *>(
|
||||
textEditor->editorWidget()) == followSymbolData->editorWidget;
|
||||
});
|
||||
if (textEditorWidgetStillAlive) {
|
||||
followSymbolData->editorWidget->invokeTextEditorWidgetAssist(
|
||||
TextEditor::FollowSymbol, &followSymbolData->virtualFuncAssistProvider);
|
||||
}
|
||||
|
||||
for (const Utils::Link &link : links) {
|
||||
if (!q->documentForFilePath(link.targetFilePath)
|
||||
&& followSymbolData->openedFiles.insert(link.targetFilePath).second) {
|
||||
q->openExtraFile(link.targetFilePath);
|
||||
}
|
||||
const TextDocumentIdentifier doc(DocumentUri::fromFilePath(link.targetFilePath));
|
||||
const Position pos(link.targetLine - 1, link.targetColumn);
|
||||
SymbolInfoRequest req(TextDocumentPositionParams(doc, pos));
|
||||
req.setResponseCallback([this, link, id = followSymbolData->id, reqId = req.id()](
|
||||
const SymbolInfoRequest::Response &response) {
|
||||
qCDebug(clangdLog) << "handling symbol info reply"
|
||||
<< link.targetFilePath.toUserOutput() << link.targetLine;
|
||||
if (!followSymbolData || id != followSymbolData->id)
|
||||
return;
|
||||
if (const auto result = response.result()) {
|
||||
if (const auto list = Utils::get_if<QList<SymbolDetails>>(&result.value())) {
|
||||
if (!list->isEmpty()) {
|
||||
// According to the documentation, we should receive a single
|
||||
// object here, but it's a list. No idea what it means if there's
|
||||
// more than one entry. We choose the first one.
|
||||
const SymbolDetails &sd = list->first();
|
||||
followSymbolData->symbolsToDisplay << qMakePair(sd.containerName()
|
||||
+ sd.name(), link);
|
||||
}
|
||||
}
|
||||
}
|
||||
followSymbolData->pendingSymbolInfoRequests.removeOne(reqId);
|
||||
if (followSymbolData->pendingSymbolInfoRequests.isEmpty())
|
||||
handleDocumentInfoResults();
|
||||
});
|
||||
followSymbolData->pendingSymbolInfoRequests << req.id();
|
||||
qCDebug(clangdLog) << "sending symbol info request";
|
||||
q->sendContent(req);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangdClient::Private::handleDocumentInfoResults()
|
||||
{
|
||||
followSymbolData->closeTempDocuments();
|
||||
|
||||
// If something went wrong, we just follow the original link.
|
||||
if (followSymbolData->symbolsToDisplay.isEmpty()) {
|
||||
followSymbolData->callback(followSymbolData->defLink);
|
||||
followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
if (followSymbolData->symbolsToDisplay.size() == 1) {
|
||||
followSymbolData->callback(followSymbolData->symbolsToDisplay.first().second);
|
||||
followSymbolData.reset();
|
||||
return;
|
||||
}
|
||||
QTC_ASSERT(followSymbolData->virtualFuncAssistProcessor
|
||||
&& followSymbolData->virtualFuncAssistProcessor->running(),
|
||||
followSymbolData.reset(); return);
|
||||
followSymbolData->virtualFuncAssistProcessor->finalize();
|
||||
}
|
||||
|
||||
void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
||||
{
|
||||
if (!m_data)
|
||||
return;
|
||||
m_data->followSymbolData->virtualFuncAssistProcessor = nullptr;
|
||||
m_data->followSymbolData.reset();
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
void ClangdClient::VirtualFunctionAssistProcessor::finalize()
|
||||
{
|
||||
QList<TextEditor::AssistProposalItemInterface *> items;
|
||||
for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) {
|
||||
const auto item = new CppTools::VirtualFunctionProposalItem(
|
||||
symbol.second, m_data->followSymbolData->openInSplit);
|
||||
item->setText(symbol.first);
|
||||
items << item;
|
||||
}
|
||||
setAsyncProposalAvailable(new CppTools::VirtualFunctionProposal(
|
||||
m_data->followSymbolData->cursor.position(),
|
||||
items, m_data->followSymbolData->openInSplit));
|
||||
|
||||
m_data->followSymbolData->virtualFuncAssistProcessor = nullptr;
|
||||
m_data->followSymbolData.reset();
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
TextEditor::IAssistProposal *ClangdClient::VirtualFunctionAssistProcessor::immediateProposal(
|
||||
const TextEditor::AssistInterface *)
|
||||
{
|
||||
QTC_ASSERT(m_data && m_data->followSymbolData, return nullptr);
|
||||
|
||||
QList<TextEditor::AssistProposalItemInterface *> items;
|
||||
if (!m_data->followSymbolData->cursorNode.isPureVirtualDeclaration()) {
|
||||
const auto defLinkItem = new CppTools::VirtualFunctionProposalItem(
|
||||
m_data->followSymbolData->defLink, m_data->followSymbolData->openInSplit);
|
||||
defLinkItem->setText(ClangdClient::tr("<base declaration>"));
|
||||
items << defLinkItem;
|
||||
}
|
||||
const auto infoItem = new CppTools::VirtualFunctionProposalItem({}, false);
|
||||
infoItem->setText(ClangdClient::tr("collecting overrides ..."));
|
||||
items << infoItem;
|
||||
return new CppTools::VirtualFunctionProposal(m_data->followSymbolData->cursor.position(),
|
||||
items, m_data->followSymbolData->openInSplit);
|
||||
}
|
||||
|
||||
TextEditor::IAssistProcessor *ClangdClient::VirtualFunctionAssistProvider::createProcessor() const
|
||||
{
|
||||
return m_data->followSymbolData->virtualFuncAssistProcessor
|
||||
= new VirtualFunctionAssistProcessor(m_data);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
|
@@ -26,11 +26,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <languageclient/client.h>
|
||||
#include <utils/link.h>
|
||||
#include <utils/optional.h>
|
||||
|
||||
#include <QVersionNumber>
|
||||
|
||||
namespace Core { class SearchResultItem; }
|
||||
namespace CppTools { class CppEditorWidgetInterface; }
|
||||
namespace ProjectExplorer { class Project; }
|
||||
namespace TextEditor { class TextDocument; }
|
||||
|
||||
@@ -52,6 +54,12 @@ public:
|
||||
|
||||
void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
||||
const Utils::optional<QString> &replacement);
|
||||
void followSymbol(TextEditor::TextDocument *document,
|
||||
const QTextCursor &cursor,
|
||||
CppTools::CppEditorWidgetInterface *editorWidget,
|
||||
Utils::ProcessLinkCallback &&callback,
|
||||
bool resolveTarget,
|
||||
bool openInSplit);
|
||||
|
||||
void enableTesting();
|
||||
|
||||
@@ -62,6 +70,9 @@ signals:
|
||||
|
||||
private:
|
||||
class Private;
|
||||
class FollowSymbolData;
|
||||
class VirtualFunctionAssistProcessor;
|
||||
class VirtualFunctionAssistProvider;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
|
@@ -180,10 +180,8 @@ void ClangFollowSymbol::findLink(const CppTools::CursorInEditor &data,
|
||||
ClangdClient * const client
|
||||
= ClangModelManagerSupport::instance()->clientForFile(data.filePath());
|
||||
if (client && client->isFullyIndexed()) {
|
||||
QTC_ASSERT(client->documentOpen(data.textDocument()),
|
||||
client->openDocument(data.textDocument()));
|
||||
client->symbolSupport().findLinkAt(data.textDocument(), data.cursor(),
|
||||
std::move(processLinkCallback), resolveTarget);
|
||||
client->followSymbol(data.textDocument(), data.cursor(), data.editorWidget(),
|
||||
std::move(processLinkCallback), resolveTarget, inNextSplit);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -132,10 +132,8 @@ void RefactoringEngine::globalFollowSymbol(
|
||||
inNextSplit);
|
||||
return;
|
||||
}
|
||||
QTC_ASSERT(client->documentOpen(cursor.textDocument()),
|
||||
client->openDocument(cursor.textDocument()));
|
||||
client->symbolSupport().findLinkAt(cursor.textDocument(), cursor.cursor(), std::move(callback),
|
||||
true);
|
||||
client->followSymbol(cursor.textDocument(), cursor.cursor(), cursor.editorWidget(),
|
||||
std::move(callback), true, inNextSplit);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -37,7 +37,6 @@
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
|
||||
#include <texteditor/codeassist/genericproposalmodel.h>
|
||||
#include <texteditor/codeassist/genericproposal.h>
|
||||
#include <texteditor/codeassist/genericproposalwidget.h>
|
||||
#include <texteditor/codeassist/assistinterface.h>
|
||||
#include <texteditor/codeassist/iassistprocessor.h>
|
||||
@@ -84,9 +83,13 @@ protected:
|
||||
{
|
||||
GenericProposalModelPtr proposalModel = model();
|
||||
if (proposalModel && proposalModel->size() == 1) {
|
||||
emit proposalItemActivated(proposalModel->proposalItem(0));
|
||||
deleteLater();
|
||||
return;
|
||||
const auto item = dynamic_cast<VirtualFunctionProposalItem *>(
|
||||
proposalModel->proposalItem(0));
|
||||
if (item && item->link().hasValidTarget()) {
|
||||
emit proposalItemActivated(proposalModel->proposalItem(0));
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
}
|
||||
GenericProposalWidget::showProposal(prefix);
|
||||
}
|
||||
@@ -95,24 +98,7 @@ private:
|
||||
QKeySequence m_sequence;
|
||||
};
|
||||
|
||||
class VirtualFunctionProposal : public GenericProposal
|
||||
{
|
||||
public:
|
||||
VirtualFunctionProposal(int cursorPos,
|
||||
const QList<AssistProposalItemInterface *> &items,
|
||||
bool openInSplit)
|
||||
: GenericProposal(cursorPos, items)
|
||||
, m_openInSplit(openInSplit)
|
||||
{
|
||||
setFragile(true);
|
||||
}
|
||||
|
||||
IAssistProposalWidget *createWidget() const override
|
||||
{ return new VirtualFunctionProposalWidget(m_openInSplit); }
|
||||
|
||||
private:
|
||||
bool m_openInSplit;
|
||||
};
|
||||
|
||||
class VirtualFunctionAssistProcessor : public IAssistProcessor
|
||||
{
|
||||
@@ -207,4 +193,16 @@ IAssistProcessor *VirtualFunctionAssistProvider::createProcessor() const
|
||||
return new VirtualFunctionAssistProcessor(m_params);
|
||||
}
|
||||
|
||||
VirtualFunctionProposal::VirtualFunctionProposal(
|
||||
int cursorPos, const QList<AssistProposalItemInterface *> &items, bool openInSplit)
|
||||
: GenericProposal(cursorPos, items), m_openInSplit(openInSplit)
|
||||
{
|
||||
setFragile(true);
|
||||
}
|
||||
|
||||
IAssistProposalWidget *VirtualFunctionProposal::createWidget() const
|
||||
{
|
||||
return new VirtualFunctionProposalWidget(m_openInSplit);
|
||||
}
|
||||
|
||||
} // namespace CppTools
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "cpptools_global.h"
|
||||
|
||||
#include <texteditor/codeassist/genericproposal.h>
|
||||
#include <texteditor/codeassist/iassistprovider.h>
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
@@ -36,8 +37,26 @@
|
||||
#include <QSharedPointer>
|
||||
#include <QTextCursor>
|
||||
|
||||
namespace TextEditor {
|
||||
class AssistProposalItemInterface;
|
||||
class IAssistProposalWidget;
|
||||
}
|
||||
|
||||
namespace CppTools {
|
||||
|
||||
class CPPTOOLS_EXPORT VirtualFunctionProposal : public TextEditor::GenericProposal
|
||||
{
|
||||
public:
|
||||
VirtualFunctionProposal(int cursorPos,
|
||||
const QList<TextEditor::AssistProposalItemInterface *> &items,
|
||||
bool openInSplit);
|
||||
|
||||
private:
|
||||
TextEditor::IAssistProposalWidget *createWidget() const override;
|
||||
|
||||
bool m_openInSplit;
|
||||
};
|
||||
|
||||
class CPPTOOLS_EXPORT VirtualFunctionAssistProvider : public TextEditor::IAssistProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "languageclient_global.h"
|
||||
|
||||
#include "utils/optional.h"
|
||||
|
||||
#include <languageserverprotocol/languagefeatures.h>
|
||||
@@ -39,7 +41,7 @@ namespace LanguageClient {
|
||||
|
||||
class Client;
|
||||
|
||||
class DocumentSymbolCache : public QObject
|
||||
class LANGUAGECLIENT_EXPORT DocumentSymbolCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@@ -277,6 +277,8 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
|
||||
} else if (!processor->running()) {
|
||||
delete processor;
|
||||
} else { // ...async request was triggered
|
||||
if (IAssistProposal *newProposal = processor->immediateProposal(assistInterface))
|
||||
displayProposal(newProposal, reason);
|
||||
QTC_CHECK(!m_asyncProcessor);
|
||||
m_asyncProcessor = processor;
|
||||
}
|
||||
|
Reference in New Issue
Block a user