forked from qt-creator/qt-creator
TextEditor: Allow incremental proposals
... and make use of them with clangd. This way, users can get immediate feedback when a new proposal entry has been found, rather than having to wait until all of them have been collected. Change-Id: I2adfe0153aa7a058f28eb3bd65c71dd30ea018e0 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -425,6 +425,7 @@ public:
|
|||||||
void cancel() override;
|
void cancel() override;
|
||||||
bool running() override { return m_data; }
|
bool running() override { return m_data; }
|
||||||
|
|
||||||
|
void update();
|
||||||
void finalize();
|
void finalize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -433,9 +434,17 @@ private:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditor::IAssistProposal *immediateProposal(const TextEditor::AssistInterface *) override;
|
TextEditor::IAssistProposal *immediateProposal(const TextEditor::AssistInterface *) override
|
||||||
|
{
|
||||||
|
return createProposal(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetData();
|
||||||
|
|
||||||
TextEditor::IAssistProposal *immediateProposalImpl() const;
|
TextEditor::IAssistProposal *immediateProposalImpl() const;
|
||||||
|
TextEditor::IAssistProposal *createProposal(bool final) const;
|
||||||
|
CppTools::VirtualFunctionProposalItem *createEntry(const QString &name,
|
||||||
|
const Utils::Link &link) const;
|
||||||
|
|
||||||
ClangdClient::Private *m_data = nullptr;
|
ClangdClient::Private *m_data = nullptr;
|
||||||
};
|
};
|
||||||
@@ -511,6 +520,7 @@ public:
|
|||||||
SymbolDataList symbolsToDisplay;
|
SymbolDataList symbolsToDisplay;
|
||||||
std::set<Utils::FilePath> openedFiles;
|
std::set<Utils::FilePath> openedFiles;
|
||||||
VirtualFunctionAssistProcessor *virtualFuncAssistProcessor = nullptr;
|
VirtualFunctionAssistProcessor *virtualFuncAssistProcessor = nullptr;
|
||||||
|
bool finished = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SwitchDeclDefData {
|
class SwitchDeclDefData {
|
||||||
@@ -1156,6 +1166,7 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
followSymbolData->pendingSymbolInfoRequests.removeOne(reqId);
|
followSymbolData->pendingSymbolInfoRequests.removeOne(reqId);
|
||||||
|
followSymbolData->virtualFuncAssistProcessor->update();
|
||||||
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
||||||
&& followSymbolData->pendingGotoDefRequests.isEmpty()
|
&& followSymbolData->pendingGotoDefRequests.isEmpty()
|
||||||
&& followSymbolData->defLinkNode.isValid()) {
|
&& followSymbolData->defLinkNode.isValid()) {
|
||||||
@@ -1275,6 +1286,33 @@ void ClangdClient::Private::handleDeclDefSwitchReplies()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
||||||
|
{
|
||||||
|
resetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangdClient::VirtualFunctionAssistProcessor::update()
|
||||||
|
{
|
||||||
|
if (!m_data->followSymbolData->isEditorWidgetStillAlive())
|
||||||
|
return;
|
||||||
|
setAsyncProposalAvailable(createProposal(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangdClient::VirtualFunctionAssistProcessor::finalize()
|
||||||
|
{
|
||||||
|
if (!m_data->followSymbolData->isEditorWidgetStillAlive())
|
||||||
|
return;
|
||||||
|
const auto proposal = createProposal(true);
|
||||||
|
if (m_data->followSymbolData->editorWidget->inTestMode) {
|
||||||
|
m_data->followSymbolData->symbolsToDisplay.clear();
|
||||||
|
const auto immediateProposal = createProposal(false);
|
||||||
|
m_data->followSymbolData->editorWidget->setProposals(immediateProposal, proposal);
|
||||||
|
} else {
|
||||||
|
setAsyncProposalAvailable(proposal);
|
||||||
|
}
|
||||||
|
resetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangdClient::VirtualFunctionAssistProcessor::resetData()
|
||||||
{
|
{
|
||||||
if (!m_data)
|
if (!m_data)
|
||||||
return;
|
return;
|
||||||
@@ -1283,77 +1321,58 @@ void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
|||||||
m_data = nullptr;
|
m_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::VirtualFunctionAssistProcessor::finalize()
|
TextEditor::IAssistProposal *ClangdClient::VirtualFunctionAssistProcessor::createProposal(bool final) const
|
||||||
{
|
|
||||||
QList<TextEditor::AssistProposalItemInterface *> items;
|
|
||||||
for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) {
|
|
||||||
Utils::Link link = symbol.second;
|
|
||||||
const bool isOriginalLink = m_data->followSymbolData->defLink == link;
|
|
||||||
if (isOriginalLink && m_data->followSymbolData->defLinkNode.range()
|
|
||||||
.contains(Position(m_data->followSymbolData->cursor))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!isOriginalLink) {
|
|
||||||
const Utils::Link defLink = m_data->followSymbolData->declDefMap.value(symbol.second);
|
|
||||||
if (defLink.hasValidTarget())
|
|
||||||
link = defLink;
|
|
||||||
}
|
|
||||||
const auto item = new CppTools::VirtualFunctionProposalItem(
|
|
||||||
link, m_data->followSymbolData->openInSplit);
|
|
||||||
QString text = symbol.first;
|
|
||||||
if (isOriginalLink) {
|
|
||||||
item->setOrder(1000); // Ensure base declaration is on top.
|
|
||||||
if (m_data->followSymbolData->defLinkNode.isPureVirtualDeclaration()
|
|
||||||
|| m_data->followSymbolData->defLinkNode.isPureVirtualDefinition()) {
|
|
||||||
text += " = 0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item->setText(text);
|
|
||||||
items << item;
|
|
||||||
}
|
|
||||||
const auto finalProposal = new CppTools::VirtualFunctionProposal(
|
|
||||||
m_data->followSymbolData->cursor.position(),
|
|
||||||
items, m_data->followSymbolData->openInSplit);
|
|
||||||
if (m_data->followSymbolData->isEditorWidgetStillAlive()
|
|
||||||
&& m_data->followSymbolData->editorWidget->inTestMode) {
|
|
||||||
m_data->followSymbolData->editorWidget->setProposals(immediateProposalImpl(),
|
|
||||||
finalProposal);
|
|
||||||
} else {
|
|
||||||
setAsyncProposalAvailable(finalProposal);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_data->followSymbolData->virtualFuncAssistProcessor = nullptr;
|
|
||||||
m_data->followSymbolData.reset();
|
|
||||||
m_data = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextEditor::IAssistProposal *ClangdClient::VirtualFunctionAssistProcessor::immediateProposal(
|
|
||||||
const TextEditor::AssistInterface *)
|
|
||||||
{
|
|
||||||
if (m_data->followSymbolData->isEditorWidgetStillAlive()
|
|
||||||
&& m_data->followSymbolData->editorWidget->inTestMode) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return immediateProposalImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
TextEditor::IAssistProposal *
|
|
||||||
ClangdClient::VirtualFunctionAssistProcessor::immediateProposalImpl() const
|
|
||||||
{
|
{
|
||||||
QTC_ASSERT(m_data && m_data->followSymbolData, return nullptr);
|
QTC_ASSERT(m_data && m_data->followSymbolData, return nullptr);
|
||||||
|
|
||||||
QList<TextEditor::AssistProposalItemInterface *> items;
|
QList<TextEditor::AssistProposalItemInterface *> items;
|
||||||
if (!m_data->followSymbolData->cursorNode.isPureVirtualDeclaration()) {
|
bool needsBaseDeclEntry = !m_data->followSymbolData->defLinkNode.range()
|
||||||
const auto defLinkItem = new CppTools::VirtualFunctionProposalItem(
|
.contains(Position(m_data->followSymbolData->cursor));
|
||||||
m_data->followSymbolData->defLink, m_data->followSymbolData->openInSplit);
|
for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) {
|
||||||
defLinkItem->setText(ClangdClient::tr("<base declaration>"));
|
Utils::Link link = symbol.second;
|
||||||
items << defLinkItem;
|
if (m_data->followSymbolData->defLink == link) {
|
||||||
|
if (!needsBaseDeclEntry)
|
||||||
|
continue;
|
||||||
|
needsBaseDeclEntry = false;
|
||||||
|
} else {
|
||||||
|
const Utils::Link defLink = m_data->followSymbolData->declDefMap.value(symbol.second);
|
||||||
|
if (defLink.hasValidTarget())
|
||||||
|
link = defLink;
|
||||||
|
}
|
||||||
|
items << createEntry(symbol.first, link);
|
||||||
}
|
}
|
||||||
const auto infoItem = new CppTools::VirtualFunctionProposalItem({}, false);
|
if (needsBaseDeclEntry)
|
||||||
infoItem->setText(ClangdClient::tr("collecting overrides ..."));
|
items << createEntry({}, m_data->followSymbolData->defLink);
|
||||||
items << infoItem;
|
if (!final) {
|
||||||
return new CppTools::VirtualFunctionProposal(m_data->followSymbolData->cursor.position(),
|
const auto infoItem = new CppTools::VirtualFunctionProposalItem({}, false);
|
||||||
items, m_data->followSymbolData->openInSplit);
|
infoItem->setText(ClangdClient::tr("collecting overrides ..."));
|
||||||
|
infoItem->setOrder(-1);
|
||||||
|
items << infoItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CppTools::VirtualFunctionProposal(
|
||||||
|
m_data->followSymbolData->cursor.position(),
|
||||||
|
items, m_data->followSymbolData->openInSplit);
|
||||||
|
}
|
||||||
|
|
||||||
|
CppTools::VirtualFunctionProposalItem *
|
||||||
|
ClangdClient::VirtualFunctionAssistProcessor::createEntry(const QString &name,
|
||||||
|
const Utils::Link &link) const
|
||||||
|
{
|
||||||
|
const auto item = new CppTools::VirtualFunctionProposalItem(
|
||||||
|
link, m_data->followSymbolData->openInSplit);
|
||||||
|
QString text = name;
|
||||||
|
if (link == m_data->followSymbolData->defLink) {
|
||||||
|
item->setOrder(1000); // Ensure base declaration is on top.
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
text = ClangdClient::tr("<base declaration>");
|
||||||
|
} else if (m_data->followSymbolData->defLinkNode.isPureVirtualDeclaration()
|
||||||
|
|| m_data->followSymbolData->defLinkNode.isPureVirtualDefinition()) {
|
||||||
|
text += " = 0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item->setText(text);
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditor::IAssistProcessor *ClangdClient::VirtualFunctionAssistProvider::createProcessor() const
|
TextEditor::IAssistProcessor *ClangdClient::VirtualFunctionAssistProvider::createProcessor() const
|
||||||
|
@@ -61,8 +61,9 @@ public:
|
|||||||
bool gotResults = false;
|
bool gotResults = false;
|
||||||
|
|
||||||
processor->setAsyncCompletionAvailableHandler(
|
processor->setAsyncCompletionAvailableHandler(
|
||||||
[this, &gotResults] (TextEditor::IAssistProposal *proposal) {
|
[this, processor, &gotResults] (TextEditor::IAssistProposal *proposal) {
|
||||||
QTC_ASSERT(proposal, return);
|
QTC_ASSERT(proposal, return);
|
||||||
|
QTC_CHECK(!processor->running());
|
||||||
proposalModel = proposal->model();
|
proposalModel = proposal->model();
|
||||||
delete proposal;
|
delete proposal;
|
||||||
gotResults = true;
|
gotResults = true;
|
||||||
|
@@ -252,11 +252,14 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IAssistProvider::Asynchronous: {
|
case IAssistProvider::Asynchronous: {
|
||||||
processor->setAsyncCompletionAvailableHandler([this, reason, processor](IAssistProposal *newProposal) {
|
processor->setAsyncCompletionAvailableHandler([this, reason, processor](
|
||||||
// do not delete this processor directly since this function is called from within the processor
|
IAssistProposal *newProposal) {
|
||||||
QMetaObject::invokeMethod(QCoreApplication::instance(), [processor]() {
|
if (!processor->running()) {
|
||||||
delete processor;
|
// do not delete this processor directly since this function is called from within the processor
|
||||||
}, Qt::QueuedConnection);
|
QMetaObject::invokeMethod(QCoreApplication::instance(), [processor]() {
|
||||||
|
delete processor;
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
if (processor != m_asyncProcessor)
|
if (processor != m_asyncProcessor)
|
||||||
return;
|
return;
|
||||||
invalidateCurrentRequestData();
|
invalidateCurrentRequestData();
|
||||||
@@ -266,7 +269,10 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
|
|||||||
requestProposal(reason, m_assistKind, m_requestProvider);
|
requestProposal(reason, m_assistKind, m_requestProvider);
|
||||||
} else {
|
} else {
|
||||||
displayProposal(newProposal, reason);
|
displayProposal(newProposal, reason);
|
||||||
emit q->finished();
|
if (processor && processor->running())
|
||||||
|
m_asyncProcessor = processor;
|
||||||
|
else
|
||||||
|
emit q->finished();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -565,6 +571,9 @@ bool CodeAssistantPrivate::eventFilter(QObject *o, QEvent *e)
|
|||||||
destroyContext();
|
destroyContext();
|
||||||
else if (!keyText.isEmpty() && !m_receivedContentWhileWaiting)
|
else if (!keyText.isEmpty() && !m_receivedContentWhileWaiting)
|
||||||
m_receivedContentWhileWaiting = true;
|
m_receivedContentWhileWaiting = true;
|
||||||
|
} else if (type == QEvent::KeyRelease
|
||||||
|
&& static_cast<QKeyEvent *>(e)->key() == Qt::Key_Escape) {
|
||||||
|
destroyContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -47,9 +47,9 @@ void IAssistProcessor::setAsyncProposalAvailable(IAssistProposal *proposal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IAssistProcessor::setAsyncCompletionAvailableHandler(
|
void IAssistProcessor::setAsyncCompletionAvailableHandler(
|
||||||
const IAssistProcessor::AsyncCompletionsAvailableHandler &finalizer)
|
const IAssistProcessor::AsyncCompletionsAvailableHandler &handler)
|
||||||
{
|
{
|
||||||
m_asyncCompletionsAvailableHandler = finalizer;
|
m_asyncCompletionsAvailableHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@@ -46,8 +46,9 @@ public:
|
|||||||
void setAsyncProposalAvailable(IAssistProposal *proposal);
|
void setAsyncProposalAvailable(IAssistProposal *proposal);
|
||||||
|
|
||||||
// Internal, used by CodeAssist
|
// Internal, used by CodeAssist
|
||||||
using AsyncCompletionsAvailableHandler = std::function<void (IAssistProposal *proposal)>;
|
using AsyncCompletionsAvailableHandler
|
||||||
void setAsyncCompletionAvailableHandler(const AsyncCompletionsAvailableHandler &finalizer);
|
= std::function<void (IAssistProposal *proposal)>;
|
||||||
|
void setAsyncCompletionAvailableHandler(const AsyncCompletionsAvailableHandler &handler);
|
||||||
|
|
||||||
virtual bool running() { return false; }
|
virtual bool running() { return false; }
|
||||||
virtual bool needsRestart() const { return false; }
|
virtual bool needsRestart() const { return false; }
|
||||||
|
Reference in New Issue
Block a user