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;
|
||||
bool running() override { return m_data; }
|
||||
|
||||
void update();
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
@@ -433,9 +434,17 @@ private:
|
||||
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 *createProposal(bool final) const;
|
||||
CppTools::VirtualFunctionProposalItem *createEntry(const QString &name,
|
||||
const Utils::Link &link) const;
|
||||
|
||||
ClangdClient::Private *m_data = nullptr;
|
||||
};
|
||||
@@ -511,6 +520,7 @@ public:
|
||||
SymbolDataList symbolsToDisplay;
|
||||
std::set<Utils::FilePath> openedFiles;
|
||||
VirtualFunctionAssistProcessor *virtualFuncAssistProcessor = nullptr;
|
||||
bool finished = false;
|
||||
};
|
||||
|
||||
class SwitchDeclDefData {
|
||||
@@ -1156,6 +1166,7 @@ void ClangdClient::Private::handleGotoImplementationResult(
|
||||
}
|
||||
}
|
||||
followSymbolData->pendingSymbolInfoRequests.removeOne(reqId);
|
||||
followSymbolData->virtualFuncAssistProcessor->update();
|
||||
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
|
||||
&& followSymbolData->pendingGotoDefRequests.isEmpty()
|
||||
&& followSymbolData->defLinkNode.isValid()) {
|
||||
@@ -1275,6 +1286,33 @@ void ClangdClient::Private::handleDeclDefSwitchReplies()
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
@@ -1283,77 +1321,58 @@ void ClangdClient::VirtualFunctionAssistProcessor::cancel()
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
void ClangdClient::VirtualFunctionAssistProcessor::finalize()
|
||||
{
|
||||
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
|
||||
TextEditor::IAssistProposal *ClangdClient::VirtualFunctionAssistProcessor::createProposal(bool final) const
|
||||
{
|
||||
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;
|
||||
bool needsBaseDeclEntry = !m_data->followSymbolData->defLinkNode.range()
|
||||
.contains(Position(m_data->followSymbolData->cursor));
|
||||
for (const SymbolData &symbol : qAsConst(m_data->followSymbolData->symbolsToDisplay)) {
|
||||
Utils::Link link = symbol.second;
|
||||
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);
|
||||
infoItem->setText(ClangdClient::tr("collecting overrides ..."));
|
||||
items << infoItem;
|
||||
return new CppTools::VirtualFunctionProposal(m_data->followSymbolData->cursor.position(),
|
||||
items, m_data->followSymbolData->openInSplit);
|
||||
if (needsBaseDeclEntry)
|
||||
items << createEntry({}, m_data->followSymbolData->defLink);
|
||||
if (!final) {
|
||||
const auto infoItem = new CppTools::VirtualFunctionProposalItem({}, false);
|
||||
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
|
||||
|
@@ -61,8 +61,9 @@ public:
|
||||
bool gotResults = false;
|
||||
|
||||
processor->setAsyncCompletionAvailableHandler(
|
||||
[this, &gotResults] (TextEditor::IAssistProposal *proposal) {
|
||||
[this, processor, &gotResults] (TextEditor::IAssistProposal *proposal) {
|
||||
QTC_ASSERT(proposal, return);
|
||||
QTC_CHECK(!processor->running());
|
||||
proposalModel = proposal->model();
|
||||
delete proposal;
|
||||
gotResults = true;
|
||||
|
@@ -252,11 +252,14 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
|
||||
break;
|
||||
}
|
||||
case IAssistProvider::Asynchronous: {
|
||||
processor->setAsyncCompletionAvailableHandler([this, reason, processor](IAssistProposal *newProposal) {
|
||||
// do not delete this processor directly since this function is called from within the processor
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), [processor]() {
|
||||
delete processor;
|
||||
}, Qt::QueuedConnection);
|
||||
processor->setAsyncCompletionAvailableHandler([this, reason, processor](
|
||||
IAssistProposal *newProposal) {
|
||||
if (!processor->running()) {
|
||||
// do not delete this processor directly since this function is called from within the processor
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), [processor]() {
|
||||
delete processor;
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
if (processor != m_asyncProcessor)
|
||||
return;
|
||||
invalidateCurrentRequestData();
|
||||
@@ -266,7 +269,10 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
|
||||
requestProposal(reason, m_assistKind, m_requestProvider);
|
||||
} else {
|
||||
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();
|
||||
else if (!keyText.isEmpty() && !m_receivedContentWhileWaiting)
|
||||
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(
|
||||
const IAssistProcessor::AsyncCompletionsAvailableHandler &finalizer)
|
||||
const IAssistProcessor::AsyncCompletionsAvailableHandler &handler)
|
||||
{
|
||||
m_asyncCompletionsAvailableHandler = finalizer;
|
||||
m_asyncCompletionsAvailableHandler = handler;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@@ -46,8 +46,9 @@ public:
|
||||
void setAsyncProposalAvailable(IAssistProposal *proposal);
|
||||
|
||||
// Internal, used by CodeAssist
|
||||
using AsyncCompletionsAvailableHandler = std::function<void (IAssistProposal *proposal)>;
|
||||
void setAsyncCompletionAvailableHandler(const AsyncCompletionsAvailableHandler &finalizer);
|
||||
using AsyncCompletionsAvailableHandler
|
||||
= std::function<void (IAssistProposal *proposal)>;
|
||||
void setAsyncCompletionAvailableHandler(const AsyncCompletionsAvailableHandler &handler);
|
||||
|
||||
virtual bool running() { return false; }
|
||||
virtual bool needsRestart() const { return false; }
|
||||
|
Reference in New Issue
Block a user