LanguageClient: move completion rerequest logic

...from the code assistant to the language client specific assist
implementation. This further reduces the complexity of the code
assistant.

Change-Id: I08ba5eecea826d3ccfe7f1f5a8791a085299d6ef
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
David Schulz
2022-11-17 14:53:47 +01:00
parent 8ff969d149
commit 0bd6d7a69f
14 changed files with 181 additions and 139 deletions

View File

@@ -81,7 +81,9 @@ private:
class ClangdCompletionAssistProcessor : public LanguageClientCompletionAssistProcessor class ClangdCompletionAssistProcessor : public LanguageClientCompletionAssistProcessor
{ {
public: public:
ClangdCompletionAssistProcessor(ClangdClient *client, const QString &snippetsGroup); ClangdCompletionAssistProcessor(ClangdClient *client,
const IAssistProvider *provider,
const QString &snippetsGroup);
~ClangdCompletionAssistProcessor(); ~ClangdCompletionAssistProcessor();
private: private:
@@ -164,7 +166,7 @@ IAssistProcessor *ClangdCompletionAssistProvider::createProcessor(
: QString(); : QString();
qCDebug(clangdLogCompletion) << "creating proper completion processor" qCDebug(clangdLogCompletion) << "creating proper completion processor"
<< (snippetsGroup.isEmpty() ? "without" : "with") << "snippets"; << (snippetsGroup.isEmpty() ? "without" : "with") << "snippets";
return new ClangdCompletionAssistProcessor(m_client, snippetsGroup); return new ClangdCompletionAssistProcessor(m_client, this, snippetsGroup);
} }
bool ClangdCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const bool ClangdCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const
@@ -554,8 +556,9 @@ QList<AssistProposalItemInterface *> CustomAssistProcessor::completeIncludePath(
} }
ClangdCompletionAssistProcessor::ClangdCompletionAssistProcessor(ClangdClient *client, ClangdCompletionAssistProcessor::ClangdCompletionAssistProcessor(ClangdClient *client,
const IAssistProvider *provider,
const QString &snippetsGroup) const QString &snippetsGroup)
: LanguageClientCompletionAssistProcessor(client, snippetsGroup) : LanguageClientCompletionAssistProcessor(client, provider, snippetsGroup)
, m_client(client) , m_client(client)
{ {
m_timer.start(); m_timer.start();

View File

@@ -42,6 +42,7 @@ public:
: TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR; : TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR;
if (Core::Command *command = Core::ActionManager::command(id)) if (Core::Command *command = Core::ActionManager::command(id))
m_sequence = command->keySequence(); m_sequence = command->keySequence();
setFragile(true);
} }
protected: protected:
@@ -169,9 +170,7 @@ IAssistProcessor *VirtualFunctionAssistProvider::createProcessor(const AssistInt
VirtualFunctionProposal::VirtualFunctionProposal( VirtualFunctionProposal::VirtualFunctionProposal(
int cursorPos, const QList<AssistProposalItemInterface *> &items, bool openInSplit) int cursorPos, const QList<AssistProposalItemInterface *> &items, bool openInSplit)
: GenericProposal(cursorPos, items), m_openInSplit(openInSplit) : GenericProposal(cursorPos, items), m_openInSplit(openInSplit)
{ { }
setFragile(true);
}
IAssistProposalWidget *VirtualFunctionProposal::createWidget() const IAssistProposalWidget *VirtualFunctionProposal::createWidget() const
{ {

View File

@@ -11,6 +11,7 @@
#include <texteditor/codeassist/assistinterface.h> #include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/genericproposal.h> #include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/genericproposalmodel.h> #include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/codeassist/genericproposalwidget.h>
#include <texteditor/snippets/snippet.h> #include <texteditor/snippets/snippet.h>
#include <texteditor/snippets/snippetassistcollector.h> #include <texteditor/snippets/snippetassistcollector.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
@@ -224,6 +225,13 @@ public:
bool supportsPrefixExpansion() const override { return false; } bool supportsPrefixExpansion() const override { return false; }
QList<AssistProposalItemInterface *> items() const { return m_currentItems; } QList<AssistProposalItemInterface *> items() const { return m_currentItems; }
bool isComplete(const QString prefix)
{ return m_completePrefix && prefix.startsWith(*m_completePrefix); }
void setCompletePrefix(const QString &completePrefix) { m_completePrefix = completePrefix; }
private:
std::optional<QString> m_completePrefix;
}; };
bool LanguageClientCompletionModel::isSortable(const QString &) const bool LanguageClientCompletionModel::isSortable(const QString &) const
@@ -252,12 +260,84 @@ void LanguageClientCompletionModel::sort(const QString &prefix)
}); });
} }
class LanguageClientCompletionWidget : public GenericProposalWidget
{
public:
LanguageClientCompletionWidget(const IAssistProvider *provider)
: m_provider(provider)
{}
~LanguageClientCompletionWidget() { deleteCurrentProcessor(); }
void deleteCurrentProcessor()
{
if (m_processor) {
m_processor->cancel();
delete m_processor;
m_processor = nullptr;
}
}
bool isComplete(const AssistInterface *interface)
{
const QString prefix = interface->textAt(basePosition(),
interface->position() - basePosition());
return static_cast<LanguageClientCompletionModel *>(model().data())->isComplete(prefix);
}
void setProposal(IAssistProposal *proposal)
{
if (!proposal)
return;
updateModel(proposal->model());
delete proposal;
}
void updateProposal(std::unique_ptr<AssistInterface> &&interface) override
{
deleteCurrentProcessor();
if (!m_provider || isComplete(interface.get())) {
GenericProposalWidget::updateProposal(std::move(interface));
return;
}
auto processor = m_provider->createProcessor(interface.get());
QTC_ASSERT(processor, return);
processor->setAsyncCompletionAvailableHandler([this, processor](IAssistProposal *proposal) {
QTC_ASSERT(processor == m_processor, return);
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);
m_processor = nullptr;
}
setProposal(proposal);
});
setProposal(processor->start(std::move(interface)));
if (processor->running())
m_processor = processor;
else
delete processor;
}
private:
QPointer<const IAssistProvider> m_provider;
std::optional<MessageId> m_currentRequestId;
IAssistProcessor *m_processor = nullptr;
};
class LanguageClientCompletionProposal : public GenericProposal class LanguageClientCompletionProposal : public GenericProposal
{ {
public: public:
LanguageClientCompletionProposal(int cursorPos, LanguageClientCompletionModel *model) LanguageClientCompletionProposal(const IAssistProvider *provider,
int cursorPos,
LanguageClientCompletionModel *model)
: GenericProposal(cursorPos, GenericProposalModelPtr(model)) : GenericProposal(cursorPos, GenericProposalModelPtr(model))
, m_model(model) , m_model(model)
, m_provider(provider)
{} {}
// IAssistProposal interface // IAssistProposal interface
@@ -274,15 +354,21 @@ public:
}); });
} }
IAssistProposalWidget *createWidget() const override
{
return new LanguageClientCompletionWidget(m_provider);
}
LanguageClientCompletionModel *m_model; LanguageClientCompletionModel *m_model;
QPointer<QTextDocument> m_document; QPointer<QTextDocument> m_document;
QPointer<const IAssistProvider> m_provider;
int m_pos = -1; int m_pos = -1;
}; };
LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor( LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(
Client *client, Client *client, const IAssistProvider *provider, const QString &snippetsGroup)
const QString &snippetsGroup)
: m_client(client) : m_client(client)
, m_provider(provider)
, m_snippetsGroup(snippetsGroup) , m_snippetsGroup(snippetsGroup)
{} {}
@@ -409,32 +495,36 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse(
return; return;
} }
const QString prefix = Utils::Text::textAt(QTextCursor(document()),
m_basePos,
m_pos - m_basePos);
QList<CompletionItem> items; QList<CompletionItem> items;
bool isComplete = false; bool isComplete = true;
if (std::holds_alternative<CompletionList>(*result)) { if (std::holds_alternative<CompletionList>(*result)) {
const auto &list = std::get<CompletionList>(*result); const auto &list = std::get<CompletionList>(*result);
items = list.items().value_or(QList<CompletionItem>()); items = list.items().value_or(QList<CompletionItem>());
isComplete = !list.isIncomplete(); if (list.isIncomplete())
isComplete = false;
} else if (std::holds_alternative<QList<CompletionItem>>(*result)) { } else if (std::holds_alternative<QList<CompletionItem>>(*result)) {
items = std::get<QList<CompletionItem>>(*result); items = std::get<QList<CompletionItem>>(*result);
} }
auto proposalItems = generateCompletionItems(items); auto proposalItems = generateCompletionItems(items);
if (!m_snippetsGroup.isEmpty()) { if (!m_snippetsGroup.isEmpty()) {
proposalItems << TextEditor::SnippetAssistCollector( proposalItems << TextEditor::SnippetAssistCollector(m_snippetsGroup,
m_snippetsGroup, QIcon(":/texteditor/images/snippet.png")).collect(); QIcon(
":/texteditor/images/snippet.png"))
.collect();
} }
auto model = new LanguageClientCompletionModel(); auto model = new LanguageClientCompletionModel();
model->loadContent(proposalItems); model->loadContent(proposalItems);
LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_basePos, if (isComplete)
model->setCompletePrefix(prefix);
LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_provider,
m_basePos,
model); model);
proposal->m_document = document(); proposal->m_document = document();
proposal->m_pos = m_pos; proposal->m_pos = m_pos;
const QString completePrefix = Utils::Text::textAt(QTextCursor(document()),
m_basePos,
m_pos - m_basePos);
proposal->setPrefixChecker([isComplete, completePrefix](const QString &candidate) {
return isComplete && candidate.startsWith(completePrefix);
});
setAsyncProposalAvailable(proposal); setAsyncProposalAvailable(proposal);
m_client->removeAssistProcessor(this); m_client->removeAssistProcessor(this);
qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : " qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : "
@@ -449,8 +539,7 @@ LanguageClientCompletionAssistProvider::LanguageClientCompletionAssistProvider(C
IAssistProcessor *LanguageClientCompletionAssistProvider::createProcessor( IAssistProcessor *LanguageClientCompletionAssistProvider::createProcessor(
const AssistInterface *) const const AssistInterface *) const
{ {
return new LanguageClientCompletionAssistProcessor(m_client, return new LanguageClientCompletionAssistProcessor(m_client, this, m_snippetsGroup);
m_snippetsGroup);
} }
int LanguageClientCompletionAssistProvider::activationCharSequenceLength() const int LanguageClientCompletionAssistProvider::activationCharSequenceLength() const

View File

@@ -57,7 +57,9 @@ class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProcessor
: public TextEditor::IAssistProcessor : public TextEditor::IAssistProcessor
{ {
public: public:
LanguageClientCompletionAssistProcessor(Client *client, const QString &snippetsGroup); LanguageClientCompletionAssistProcessor(Client *client,
const TextEditor::IAssistProvider *provider,
const QString &snippetsGroup);
~LanguageClientCompletionAssistProcessor() override; ~LanguageClientCompletionAssistProcessor() override;
TextEditor::IAssistProposal *perform() override; TextEditor::IAssistProposal *perform() override;
bool running() override; bool running() override;
@@ -76,6 +78,7 @@ private:
Utils::FilePath m_filePath; Utils::FilePath m_filePath;
QPointer<Client> m_client; QPointer<Client> m_client;
QPointer<const TextEditor::IAssistProvider> m_provider;
std::optional<LanguageServerProtocol::MessageId> m_currentRequest; std::optional<LanguageServerProtocol::MessageId> m_currentRequest;
QMetaObject::Connection m_postponedUpdateConnection; QMetaObject::Connection m_postponedUpdateConnection;
const QString m_snippetsGroup; const QString m_snippetsGroup;

View File

@@ -77,12 +77,10 @@ private:
private: private:
CodeAssistant *q = nullptr; CodeAssistant *q = nullptr;
TextEditorWidget *m_editorWidget = nullptr; TextEditorWidget *m_editorWidget = nullptr;
QMetaObject::Connection m_runnerConnection;
IAssistProvider *m_requestProvider = nullptr; IAssistProvider *m_requestProvider = nullptr;
IAssistProcessor *m_processor = nullptr; IAssistProcessor *m_processor = nullptr;
AssistKind m_assistKind = TextEditor::Completion; AssistKind m_assistKind = TextEditor::Completion;
IAssistProposalWidget *m_proposalWidget = nullptr; IAssistProposalWidget *m_proposalWidget = nullptr;
QScopedPointer<IAssistProposal> m_proposal;
bool m_receivedContentWhileWaiting = false; bool m_receivedContentWhileWaiting = false;
QTimer m_automaticProposalTimer; QTimer m_automaticProposalTimer;
CompletionSettings m_settings; CompletionSettings m_settings;
@@ -117,12 +115,11 @@ void CodeAssistantPrivate::invoke(AssistKind kind, IAssistProvider *provider)
{ {
stopAutomaticProposalTimer(); stopAutomaticProposalTimer();
if (isDisplayingProposal() && m_assistKind == kind && !m_proposal->isFragile() if (isDisplayingProposal() && m_assistKind == kind && !m_proposalWidget->isFragile()) {
&& m_proposal->supportsPrefixFiltering(proposalPrefix())) {
m_proposalWidget->setReason(ExplicitlyInvoked); m_proposalWidget->setReason(ExplicitlyInvoked);
m_proposalWidget->updateProposal(m_editorWidget->textAt( m_proposalWidget->filterProposal(m_editorWidget->textAt(
m_proposal->basePosition(), m_proposalWidget->basePosition(),
m_editorWidget->position() - m_proposal->basePosition())); m_editorWidget->position() - m_proposalWidget->basePosition()));
} else { } else {
requestProposal(ExplicitlyInvoked, kind, provider); requestProposal(ExplicitlyInvoked, kind, provider);
} }
@@ -239,14 +236,12 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
return; return;
// TODO: The proposal should own the model until someone takes it explicitly away. // TODO: The proposal should own the model until someone takes it explicitly away.
QScopedPointer<IAssistProposal> proposalCandidate(newProposal); QScopedPointer<IAssistProposal> deleter(newProposal);
if (isDisplayingProposal() && !m_proposal->isFragile() if (isDisplayingProposal() && !m_proposalWidget->isFragile())
&& !m_proposalWidget->supportsModelUpdate(proposalCandidate->id())) {
return; return;
}
int basePosition = proposalCandidate->basePosition(); int basePosition = newProposal->basePosition();
if (m_editorWidget->position() < basePosition) { if (m_editorWidget->position() < basePosition) {
destroyContext(); destroyContext();
return; return;
@@ -266,28 +261,16 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
return; return;
} }
if (m_proposalWidget
&& basePosition == proposalCandidate->basePosition()
&& m_proposalWidget->supportsModelUpdate(proposalCandidate->id())) {
m_proposal.reset(proposalCandidate.take());
m_proposal->setReason(reason);
m_proposalWidget->updateModel(m_proposal->model());
m_proposalWidget->updateProposal(prefix);
return;
}
destroyContext(); destroyContext();
clearAbortedPosition(); clearAbortedPosition();
m_proposal.reset(proposalCandidate.take());
m_proposal->setReason(reason);
if (m_proposal->isCorrective(m_editorWidget)) if (newProposal->isCorrective(m_editorWidget))
m_proposal->makeCorrection(m_editorWidget); newProposal->makeCorrection(m_editorWidget);
m_editorWidget->keepAutoCompletionHighlight(true); m_editorWidget->keepAutoCompletionHighlight(true);
basePosition = m_proposal->basePosition(); basePosition = newProposal->basePosition();
m_proposalWidget = m_proposal->createWidget(); m_proposalWidget = newProposal->createWidget();
connect(m_proposalWidget, &QObject::destroyed, connect(m_proposalWidget, &QObject::destroyed,
this, &CodeAssistantPrivate::finalizeProposal); this, &CodeAssistantPrivate::finalizeProposal);
connect(m_proposalWidget, &IAssistProposalWidget::prefixExpanded, connect(m_proposalWidget, &IAssistProposalWidget::prefixExpanded,
@@ -301,7 +284,7 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
m_proposalWidget->setKind(m_assistKind); m_proposalWidget->setKind(m_assistKind);
m_proposalWidget->setBasePosition(basePosition); m_proposalWidget->setBasePosition(basePosition);
m_proposalWidget->setUnderlyingWidget(m_editorWidget); m_proposalWidget->setUnderlyingWidget(m_editorWidget);
m_proposalWidget->setModel(m_proposal->model()); m_proposalWidget->setModel(newProposal->model());
m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition)); m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition));
m_proposalWidget->setIsSynchronized(!m_receivedContentWhileWaiting); m_proposalWidget->setIsSynchronized(!m_receivedContentWhileWaiting);
m_proposalWidget->showProposal(prefix); m_proposalWidget->showProposal(prefix);
@@ -309,9 +292,9 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR
void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem) void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *proposalItem)
{ {
QTC_ASSERT(m_proposal, return); QTC_ASSERT(m_proposalWidget, return);
TextDocumentManipulator manipulator(m_editorWidget); TextDocumentManipulator manipulator(m_editorWidget);
proposalItem->apply(manipulator, m_proposal->basePosition()); proposalItem->apply(manipulator, m_proposalWidget->basePosition());
destroyContext(); destroyContext();
m_editorWidget->encourageApply(); m_editorWidget->encourageApply();
if (!proposalItem->isSnippet()) if (!proposalItem->isSnippet())
@@ -320,34 +303,33 @@ void CodeAssistantPrivate::processProposalItem(AssistProposalItemInterface *prop
void CodeAssistantPrivate::handlePrefixExpansion(const QString &newPrefix) void CodeAssistantPrivate::handlePrefixExpansion(const QString &newPrefix)
{ {
QTC_ASSERT(m_proposal, return); QTC_ASSERT(m_proposalWidget, return);
QTextCursor cursor(m_editorWidget->document()); QTextCursor cursor(m_editorWidget->document());
cursor.setPosition(m_proposal->basePosition()); cursor.setPosition(m_proposalWidget->basePosition());
cursor.movePosition(QTextCursor::EndOfWord); cursor.movePosition(QTextCursor::EndOfWord);
int currentPosition = m_editorWidget->position(); int currentPosition = m_editorWidget->position();
const QString textAfterCursor = m_editorWidget->textAt(currentPosition, const QString textAfterCursor = m_editorWidget->textAt(currentPosition,
cursor.position() - currentPosition); cursor.position() - currentPosition);
if (!textAfterCursor.startsWith(newPrefix)) { if (!textAfterCursor.startsWith(newPrefix)) {
if (newPrefix.indexOf(textAfterCursor, currentPosition - m_proposal->basePosition()) >= 0) if (newPrefix.indexOf(textAfterCursor, currentPosition - m_proposalWidget->basePosition()) >= 0)
currentPosition = cursor.position(); currentPosition = cursor.position();
const QStringView prefixAddition = QStringView(newPrefix).mid(currentPosition const QStringView prefixAddition = QStringView(newPrefix).mid(currentPosition
- m_proposal->basePosition()); - m_proposalWidget->basePosition());
// If remaining string starts with the prefix addition // If remaining string starts with the prefix addition
if (textAfterCursor.startsWith(prefixAddition)) if (textAfterCursor.startsWith(prefixAddition))
currentPosition += prefixAddition.size(); currentPosition += prefixAddition.size();
} }
m_editorWidget->setCursorPosition(m_proposal->basePosition()); m_editorWidget->setCursorPosition(m_proposalWidget->basePosition());
m_editorWidget->replace(currentPosition - m_proposal->basePosition(), newPrefix); m_editorWidget->replace(currentPosition - m_proposalWidget->basePosition(), newPrefix);
notifyChange(); notifyChange();
} }
void CodeAssistantPrivate::finalizeProposal() void CodeAssistantPrivate::finalizeProposal()
{ {
stopAutomaticProposalTimer(); stopAutomaticProposalTimer();
m_proposal.reset();
m_proposalWidget = nullptr; m_proposalWidget = nullptr;
if (m_receivedContentWhileWaiting) if (m_receivedContentWhileWaiting)
m_receivedContentWhileWaiting = false; m_receivedContentWhileWaiting = false;
@@ -367,8 +349,8 @@ QString CodeAssistantPrivate::proposalPrefix() const
{ {
if (!isDisplayingProposal()) if (!isDisplayingProposal())
return {}; return {};
return m_editorWidget->textAt(m_proposal->basePosition(), return m_editorWidget->textAt(m_proposalWidget->basePosition(),
m_editorWidget->position() - m_proposal->basePosition()); m_editorWidget->position() - m_proposalWidget->basePosition());
} }
void CodeAssistantPrivate::invalidateCurrentRequestData() void CodeAssistantPrivate::invalidateCurrentRequestData()
@@ -409,18 +391,14 @@ void CodeAssistantPrivate::notifyChange()
stopAutomaticProposalTimer(); stopAutomaticProposalTimer();
if (isDisplayingProposal()) { if (isDisplayingProposal()) {
QTC_ASSERT(m_proposal, return); QTC_ASSERT(m_proposalWidget, return);
if (m_editorWidget->position() < m_proposal->basePosition()) { if (m_editorWidget->position() < m_proposalWidget->basePosition()) {
destroyContext(); destroyContext();
} else { } else {
const QString prefix = proposalPrefix(); m_proposalWidget->updateProposal(
if (m_proposal->supportsPrefixFiltering(prefix)) { m_editorWidget->createAssistInterface(m_assistKind, m_proposalWidget->reason()));
m_proposalWidget->updateProposal(prefix);
if (!isDisplayingProposal()) if (!isDisplayingProposal())
requestActivationCharProposal(); requestActivationCharProposal();
} else {
requestProposal(m_proposal->reason(), m_assistKind, m_requestProvider, true);
}
} }
} }
} }
@@ -466,7 +444,7 @@ void CodeAssistantPrivate::automaticProposalTimeout()
{ {
if (isWaitingForProposal() if (isWaitingForProposal()
|| m_editorWidget->multiTextCursor().hasMultipleCursors() || m_editorWidget->multiTextCursor().hasMultipleCursors()
|| (isDisplayingProposal() && !m_proposal->isFragile())) { || (isDisplayingProposal() && !m_proposalWidget->isFragile())) {
return; return;
} }
@@ -488,8 +466,8 @@ void CodeAssistantPrivate::updateFromCompletionSettings(
void CodeAssistantPrivate::explicitlyAborted() void CodeAssistantPrivate::explicitlyAborted()
{ {
QTC_ASSERT(m_proposal, return); QTC_ASSERT(m_proposalWidget, return);
m_abortedBasePosition = m_proposal->basePosition(); m_abortedBasePosition = m_proposalWidget->basePosition();
} }
void CodeAssistantPrivate::clearAbortedPosition() void CodeAssistantPrivate::clearAbortedPosition()

View File

@@ -12,9 +12,7 @@ using namespace TextEditor;
FunctionHintProposal::FunctionHintProposal(int cursorPos, FunctionHintProposalModelPtr model) FunctionHintProposal::FunctionHintProposal(int cursorPos, FunctionHintProposalModelPtr model)
: IAssistProposal(functionHintId, cursorPos) : IAssistProposal(functionHintId, cursorPos)
, m_model(model) , m_model(model)
{ {}
setFragile(true);
}
FunctionHintProposal::~FunctionHintProposal() = default; FunctionHintProposal::~FunctionHintProposal() = default;

View File

@@ -138,6 +138,7 @@ FunctionHintProposalWidget::FunctionHintProposalWidget()
}); });
setFocusPolicy(Qt::NoFocus); setFocusPolicy(Qt::NoFocus);
setFragile(true);
} }
FunctionHintProposalWidget::~FunctionHintProposalWidget() FunctionHintProposalWidget::~FunctionHintProposalWidget()
@@ -150,9 +151,6 @@ void FunctionHintProposalWidget::setAssistant(CodeAssistant *assistant)
d->m_assistant = assistant; d->m_assistant = assistant;
} }
void FunctionHintProposalWidget::setReason(AssistReason)
{}
void FunctionHintProposalWidget::setKind(AssistKind) void FunctionHintProposalWidget::setKind(AssistKind)
{} {}
@@ -190,7 +188,7 @@ void FunctionHintProposalWidget::showProposal(const QString &prefix)
d->m_popupFrame->show(); d->m_popupFrame->show();
} }
void FunctionHintProposalWidget::updateProposal(const QString &prefix) void FunctionHintProposalWidget::filterProposal(const QString &prefix)
{ {
updateAndCheck(prefix); updateAndCheck(prefix);
} }

View File

@@ -19,7 +19,6 @@ public:
~FunctionHintProposalWidget() override; ~FunctionHintProposalWidget() override;
void setAssistant(CodeAssistant *assistant) override; void setAssistant(CodeAssistant *assistant) override;
void setReason(AssistReason reason) override;
void setKind(AssistKind kind) override; void setKind(AssistKind kind) override;
void setUnderlyingWidget(const QWidget *underlyingWidget) override; void setUnderlyingWidget(const QWidget *underlyingWidget) override;
void setModel(ProposalModelPtr model) override; void setModel(ProposalModelPtr model) override;
@@ -27,7 +26,7 @@ public:
void setIsSynchronized(bool isSync) override; void setIsSynchronized(bool isSync) override;
void showProposal(const QString &prefix) override; void showProposal(const QString &prefix) override;
void updateProposal(const QString &prefix) override; void filterProposal(const QString &prefix) override;
void closeProposal() override; void closeProposal() override;
bool proposalIsVisible() const override; bool proposalIsVisible() const override;

View File

@@ -249,7 +249,6 @@ public:
QRect m_displayRect; QRect m_displayRect;
bool m_isSynchronized = true; bool m_isSynchronized = true;
bool m_explicitlySelected = false; bool m_explicitlySelected = false;
AssistReason m_reason = IdleEditor;
AssistKind m_kind = Completion; AssistKind m_kind = Completion;
bool m_justInvoked = false; bool m_justInvoked = false;
QPointer<GenericProposalInfoFrame> m_infoFrame; QPointer<GenericProposalInfoFrame> m_infoFrame;
@@ -358,9 +357,9 @@ void GenericProposalWidget::setAssistant(CodeAssistant *assistant)
void GenericProposalWidget::setReason(AssistReason reason) void GenericProposalWidget::setReason(AssistReason reason)
{ {
d->m_reason = reason; if (reason == ExplicitlyInvoked)
if (d->m_reason == ExplicitlyInvoked)
d->m_justInvoked = true; d->m_justInvoked = true;
IAssistProposalWidget::setReason(reason);
} }
void GenericProposalWidget::setKind(AssistKind kind) void GenericProposalWidget::setKind(AssistKind kind)
@@ -393,11 +392,6 @@ void GenericProposalWidget::setIsSynchronized(bool isSync)
d->m_isSynchronized = isSync; d->m_isSynchronized = isSync;
} }
bool GenericProposalWidget::supportsModelUpdate(const Utils::Id &proposalId) const
{
return proposalId == Constants::GENERIC_PROPOSAL_ID;
}
void GenericProposalWidget::updateModel(ProposalModelPtr model) void GenericProposalWidget::updateModel(ProposalModelPtr model)
{ {
QString currentText; QString currentText;
@@ -418,6 +412,7 @@ void GenericProposalWidget::updateModel(ProposalModelPtr model)
d->m_completionListView->selectRow(currentRow); d->m_completionListView->selectRow(currentRow);
else else
d->m_explicitlySelected = false; d->m_explicitlySelected = false;
updatePositionAndSize();
} }
void GenericProposalWidget::showProposal(const QString &prefix) void GenericProposalWidget::showProposal(const QString &prefix)
@@ -431,7 +426,7 @@ void GenericProposalWidget::showProposal(const QString &prefix)
d->m_completionListView->setFocus(); d->m_completionListView->setFocus();
} }
void GenericProposalWidget::updateProposal(const QString &prefix) void GenericProposalWidget::filterProposal(const QString &prefix)
{ {
if (!isVisible()) if (!isVisible())
return; return;
@@ -470,7 +465,7 @@ bool GenericProposalWidget::updateAndCheck(const QString &prefix)
if (!prefix.isEmpty()) if (!prefix.isEmpty())
d->m_model->filter(prefix); d->m_model->filter(prefix);
} }
if (!d->m_model->hasItemsToPropose(prefix, d->m_reason)) { if (!d->m_model->hasItemsToPropose(prefix, reason())) {
d->m_completionListView->reset(); d->m_completionListView->reset();
abort(); abort();
return false; return false;
@@ -660,7 +655,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e)
AssistProposalItemInterface *item = AssistProposalItemInterface *item =
d->m_model->proposalItem(d->m_completionListView->currentIndex().row()); d->m_model->proposalItem(d->m_completionListView->currentIndex().row());
if (item->prematurelyApplies(typedChar) if (item->prematurelyApplies(typedChar)
&& (d->m_reason == ExplicitlyInvoked || item->text().endsWith(typedChar))) { && (reason() == ExplicitlyInvoked || item->text().endsWith(typedChar))) {
abort(); abort();
emit proposalItemActivated(item); emit proposalItemActivated(item);
return true; return true;

View File

@@ -31,11 +31,10 @@ public:
void setDisplayRect(const QRect &rect) override; void setDisplayRect(const QRect &rect) override;
void setIsSynchronized(bool isSync) override; void setIsSynchronized(bool isSync) override;
bool supportsModelUpdate(const Utils::Id &proposalId) const override; void updateModel(ProposalModelPtr model);
void updateModel(ProposalModelPtr model) override;
void showProposal(const QString &prefix) override; void showProposal(const QString &prefix) override;
void updateProposal(const QString &prefix) override; void filterProposal(const QString &prefix) override;
void closeProposal() override; void closeProposal() override;
private: private:

View File

@@ -63,16 +63,6 @@ int IAssistProposal::basePosition() const
return m_basePosition; return m_basePosition;
} }
bool IAssistProposal::isFragile() const
{
return m_isFragile;
}
bool IAssistProposal::supportsPrefixFiltering(const QString &prefix) const
{
return !m_prefixChecker || m_prefixChecker(prefix);
}
/*! /*!
\fn bool TextEditor::IAssistProposal::isCorrective() const \fn bool TextEditor::IAssistProposal::isCorrective() const
@@ -98,16 +88,6 @@ void IAssistProposal::makeCorrection(TextEditorWidget *editorWidget)
Q_UNUSED(editorWidget) Q_UNUSED(editorWidget)
} }
void IAssistProposal::setFragile(bool fragile)
{
m_isFragile = fragile;
}
void IAssistProposal::setPrefixChecker(const PrefixChecker checker)
{
m_prefixChecker = checker;
}
/*! /*!
\fn IAssistModel *TextEditor::IAssistProposal::model() const \fn IAssistModel *TextEditor::IAssistProposal::model() const

View File

@@ -22,30 +22,17 @@ public:
virtual ~IAssistProposal(); virtual ~IAssistProposal();
int basePosition() const; int basePosition() const;
bool isFragile() const;
bool supportsPrefixFiltering(const QString &prefix) const;
virtual bool hasItemsToPropose(const QString &, AssistReason) const { return true; } virtual bool hasItemsToPropose(const QString &, AssistReason) const { return true; }
virtual bool isCorrective(TextEditorWidget *editorWidget) const; virtual bool isCorrective(TextEditorWidget *editorWidget) const;
virtual void makeCorrection(TextEditorWidget *editorWidget); virtual void makeCorrection(TextEditorWidget *editorWidget);
virtual TextEditor::ProposalModelPtr model() const = 0; virtual TextEditor::ProposalModelPtr model() const = 0;
virtual IAssistProposalWidget *createWidget() const = 0; virtual IAssistProposalWidget *createWidget() const = 0;
void setFragile(bool fragile);
Utils::Id id() const { return m_id; } Utils::Id id() const { return m_id; }
AssistReason reason() const { return m_reason; }
void setReason(const AssistReason &reason) { m_reason = reason; }
using PrefixChecker = std::function<bool(const QString &)>;
void setPrefixChecker(const PrefixChecker checker);
protected: protected:
Utils::Id m_id; Utils::Id m_id;
int m_basePosition; int m_basePosition;
bool m_isFragile = false;
PrefixChecker m_prefixChecker;
AssistReason m_reason = IdleEditor;
}; };
} // TextEditor } // TextEditor

View File

@@ -3,6 +3,8 @@
#include "iassistproposalwidget.h" #include "iassistproposalwidget.h"
#include "assistinterface.h"
using namespace TextEditor; using namespace TextEditor;
/*! /*!
@@ -30,6 +32,11 @@ IAssistProposalWidget::IAssistProposalWidget()
: QFrame(nullptr, Qt::Popup) : QFrame(nullptr, Qt::Popup)
{} {}
void IAssistProposalWidget::updateProposal(std::unique_ptr<AssistInterface> &&interface)
{
filterProposal(interface->textAt(m_basePosition, interface->position() - m_basePosition));
}
IAssistProposalWidget::~IAssistProposalWidget() = default; IAssistProposalWidget::~IAssistProposalWidget() = default;
int IAssistProposalWidget::basePosition() const int IAssistProposalWidget::basePosition() const

View File

@@ -14,8 +14,9 @@ namespace Utils { class Id; }
namespace TextEditor { namespace TextEditor {
class CodeAssistant; class AssistInterface;
class AssistProposalItemInterface; class AssistProposalItemInterface;
class CodeAssistant;
class TEXTEDITOR_EXPORT IAssistProposalWidget : public QFrame class TEXTEDITOR_EXPORT IAssistProposalWidget : public QFrame
{ {
@@ -26,7 +27,7 @@ public:
~IAssistProposalWidget() override; ~IAssistProposalWidget() override;
virtual void setAssistant(CodeAssistant *assistant) = 0; virtual void setAssistant(CodeAssistant *assistant) = 0;
virtual void setReason(AssistReason reason) = 0; virtual void setReason(AssistReason reason) { m_reason = reason; }
virtual void setKind(AssistKind kind) = 0; virtual void setKind(AssistKind kind) = 0;
virtual void setUnderlyingWidget(const QWidget *underlyingWidget) = 0; virtual void setUnderlyingWidget(const QWidget *underlyingWidget) = 0;
virtual void setModel(ProposalModelPtr model) = 0; virtual void setModel(ProposalModelPtr model) = 0;
@@ -34,16 +35,20 @@ public:
virtual void setIsSynchronized(bool isSync) = 0; virtual void setIsSynchronized(bool isSync) = 0;
virtual void showProposal(const QString &prefix) = 0; virtual void showProposal(const QString &prefix) = 0;
virtual void updateProposal(const QString &prefix) = 0; virtual void filterProposal(const QString &prefix) = 0;
virtual void updateProposal(std::unique_ptr<AssistInterface> &&interface);
virtual void closeProposal() = 0; virtual void closeProposal() = 0;
virtual bool proposalIsVisible() const { return isVisible(); } virtual bool proposalIsVisible() const { return isVisible(); }
virtual bool supportsModelUpdate(const Utils::Id &/*proposalId*/) const { return false; }
virtual void updateModel(ProposalModelPtr) {}
int basePosition() const; int basePosition() const;
void setBasePosition(int basePosition); void setBasePosition(int basePosition);
void setFragile(bool fragile) { m_isFragile = fragile; }
bool isFragile() const { return m_isFragile; }
AssistReason reason() const { return m_reason; }
signals: signals:
void prefixExpanded(const QString &newPrefix); void prefixExpanded(const QString &newPrefix);
void proposalItemActivated(AssistProposalItemInterface *proposalItem); void proposalItemActivated(AssistProposalItemInterface *proposalItem);
@@ -51,6 +56,8 @@ signals:
protected: protected:
int m_basePosition = -1; int m_basePosition = -1;
bool m_isFragile = false;
AssistReason m_reason = IdleEditor;
}; };
} // TextEditor } // TextEditor