Editor: move ownership of assist interface to processor

This way the base class can manage the lifetime of the interface object
and it doesn't need to be done in each implementation of perform.

Change-Id: Ie1ce742e31b688a337533ee6c57d376146e25ace
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
David Schulz
2022-11-15 14:19:06 +01:00
parent aa6633ca21
commit 0e4b0a26d3
43 changed files with 325 additions and 321 deletions

View File

@@ -17,14 +17,13 @@ AsyncProcessor::AsyncProcessor()
});
}
IAssistProposal *AsyncProcessor::perform(AssistInterface *interface)
IAssistProposal *AsyncProcessor::perform()
{
IAssistProposal *result = immediateProposal(interface);
m_interface = interface;
m_interface->prepareForAsyncUse();
IAssistProposal *result = immediateProposal();
interface()->prepareForAsyncUse();
m_watcher.setFuture(Utils::runAsync([this]() {
m_interface->recreateTextDocument();
return performAsync(m_interface);
interface()->recreateTextDocument();
return performAsync();
}));
return result;
}
@@ -44,9 +43,8 @@ void AsyncProcessor::cancel()
});
}
IAssistProposal *AsyncProcessor::immediateProposal(AssistInterface *interface)
IAssistProposal *AsyncProcessor::immediateProposal()
{
Q_UNUSED(interface)
return nullptr;
}

View File

@@ -14,18 +14,17 @@ class TEXTEDITOR_EXPORT AsyncProcessor : public TextEditor::IAssistProcessor
public:
AsyncProcessor();
IAssistProposal *perform(AssistInterface *interface) final;
IAssistProposal *perform() final;
bool running() override;
void cancel() override;
virtual IAssistProposal *performAsync(AssistInterface *interface) = 0;
virtual IAssistProposal *immediateProposal(AssistInterface *interface);
virtual IAssistProposal *performAsync() = 0;
virtual IAssistProposal *immediateProposal();
protected:
bool isCanceled() const;
private:
AssistInterface *m_interface = nullptr;
QFutureWatcher<IAssistProposal *> m_watcher;
};

View File

@@ -176,7 +176,8 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
return;
}
AssistInterface *assistInterface = m_editorWidget->createAssistInterface(kind, reason);
std::unique_ptr<AssistInterface> assistInterface =
m_editorWidget->createAssistInterface(kind, reason);
if (!assistInterface)
return;
@@ -185,8 +186,7 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
m_assistKind = kind;
m_requestProvider = provider;
IAssistProcessor *processor = provider->createProcessor(assistInterface);
IAssistProcessor *processor = provider->createProcessor(assistInterface.get());
processor->setAsyncCompletionAvailableHandler([this, reason, processor](
IAssistProposal *newProposal) {
if (!processor->running()) {
@@ -211,7 +211,7 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason,
}
});
if (IAssistProposal *newProposal = processor->perform(assistInterface))
if (IAssistProposal *newProposal = processor->start(std::move(assistInterface)))
displayProposal(newProposal, reason);
if (!processor->running()) {
if (isUpdate)

View File

@@ -29,7 +29,7 @@ public:
DocumentContentCompletionProcessor(const QString &snippetGroupId);
~DocumentContentCompletionProcessor() final;
IAssistProposal *performAsync(AssistInterface *interface) override;
IAssistProposal *performAsync() override;
private:
QString m_snippetGroup;
@@ -53,23 +53,21 @@ DocumentContentCompletionProcessor::~DocumentContentCompletionProcessor()
cancel();
}
IAssistProposal *DocumentContentCompletionProcessor::performAsync(AssistInterface *interface)
IAssistProposal *DocumentContentCompletionProcessor::performAsync()
{
QScopedPointer<AssistInterface> interfaceDeleter(interface);
int pos = interface->position();
int pos = interface()->position();
QChar chr;
// Skip to the start of a name
do {
chr = interface->characterAt(--pos);
chr = interface()->characterAt(--pos);
} while (chr.isLetterOrNumber() || chr == '_');
++pos;
int length = interface->position() - pos;
int length = interface()->position() - pos;
if (interface->reason() == IdleEditor) {
QChar characterUnderCursor = interface->characterAt(interface->position());
if (interface()->reason() == IdleEditor) {
QChar characterUnderCursor = interface()->characterAt(interface()->position());
if (characterUnderCursor.isLetterOrNumber()
|| length < TextEditorSettings::completionSettings().m_characterThreshold) {
return nullptr;
@@ -80,8 +78,8 @@ IAssistProposal *DocumentContentCompletionProcessor::performAsync(AssistInterfac
m_snippetGroup, QIcon(":/texteditor/images/snippet.png"));
QList<AssistProposalItemInterface *> items = snippetCollector.collect();
const QString wordUnderCursor = interface->textAt(pos, length);
const QString text = interface->textDocument()->toPlainText();
const QString wordUnderCursor = interface()->textAt(pos, length);
const QString text = interface()->textDocument()->toPlainText();
const QRegularExpression wordRE("([\\p{L}_][\\p{L}0-9_]{2,})");
QSet<QString> words;

View File

@@ -17,7 +17,7 @@ public:
DocumentContentCompletionProvider(
const QString &snippetGroup = QString(Constants::TEXT_SNIPPET_GROUP_ID));
IAssistProcessor *createProcessor(const AssistInterface *) const override;
IAssistProcessor *createProcessor(const AssistInterface *interface) const override;
private:
QString m_snippetGroup;

View File

@@ -3,6 +3,10 @@
#include "iassistprocessor.h"
#include "assistinterface.h"
#include <utils/qtcassert.h>
using namespace TextEditor;
/*!
@@ -18,6 +22,14 @@ IAssistProcessor::IAssistProcessor() = default;
IAssistProcessor::~IAssistProcessor() = default;
IAssistProposal *IAssistProcessor::start(std::unique_ptr<AssistInterface> &&interface)
{
QTC_ASSERT(!running(), return nullptr);
m_interface = std::move(interface);
QTC_ASSERT(m_interface, return nullptr);
return perform();
}
void IAssistProcessor::setAsyncProposalAvailable(IAssistProposal *proposal)
{
if (m_asyncCompletionsAvailableHandler)
@@ -30,15 +42,27 @@ void IAssistProcessor::setAsyncCompletionAvailableHandler(
m_asyncCompletionsAvailableHandler = handler;
}
bool IAssistProcessor::running() { return false; }
bool IAssistProcessor::needsRestart() const { return false; }
void IAssistProcessor::cancel() {}
AssistInterface *IAssistProcessor::interface() { return m_interface.get(); }
const AssistInterface *IAssistProcessor::interface() const { return m_interface.get(); }
#ifdef WITH_TESTS
void IAssistProcessor::setupAssistInterface(std::unique_ptr<AssistInterface> &&interface)
{
m_interface = std::move(interface);
}
#endif
/*!
\fn IAssistProposal *TextEditor::IAssistProcessor::perform(const AssistInterface *interface)
\fn IAssistProposal *TextEditor::IAssistProcessor::start()
Computes a proposal and returns it. Access to the document is made through the \a interface.
If this is an asynchronous processor the \a interface will be detached.
The processor takes ownership of the interface. Also, one should be careful in the case of
sharing data across asynchronous processors since there might be more than one instance of
them computing a proposal at a particular time.
\sa AssistInterface::detach()
*/
them computing a proposal at a particular time.*/

View File

@@ -6,6 +6,7 @@
#include <texteditor/texteditor_global.h>
#include <functional>
#include <memory>
namespace TextEditor {
@@ -18,21 +19,30 @@ public:
IAssistProcessor();
virtual ~IAssistProcessor();
virtual IAssistProposal *perform(AssistInterface *interface) = 0; // takes ownership
void setAsyncProposalAvailable(IAssistProposal *proposal);
IAssistProposal *start(std::unique_ptr<AssistInterface> &&interface);
// Internal, used by CodeAssist
using AsyncCompletionsAvailableHandler
= std::function<void (IAssistProposal *proposal)>;
void setAsyncCompletionAvailableHandler(const AsyncCompletionsAvailableHandler &handler);
void setAsyncProposalAvailable(IAssistProposal *proposal);
virtual bool running() { return false; }
virtual bool needsRestart() const { return false; }
virtual void cancel() {}
virtual bool running();
virtual bool needsRestart() const;
virtual void cancel();
#ifdef WITH_TESTS
void setupAssistInterface(std::unique_ptr<AssistInterface> &&interface);
#endif
protected:
virtual IAssistProposal *perform() = 0;
AssistInterface *interface();
const AssistInterface *interface() const;
private:
AsyncCompletionsAvailableHandler m_asyncCompletionsAvailableHandler;
std::unique_ptr<AssistInterface> m_interface;
};
} // TextEditor

View File

@@ -150,34 +150,33 @@ KeywordsCompletionAssistProcessor::KeywordsCompletionAssistProcessor(const Keywo
, m_keywords(keywords)
{}
IAssistProposal *KeywordsCompletionAssistProcessor::performAsync(AssistInterface *interface)
IAssistProposal *KeywordsCompletionAssistProcessor::performAsync()
{
QScopedPointer<const AssistInterface> assistInterface(interface);
if (isInComment(interface))
if (isInComment(interface()))
return nullptr;
int pos = interface->position();
int pos = interface()->position();
// Find start position
QChar chr = interface->characterAt(pos - 1);
QChar chr = interface()->characterAt(pos - 1);
if (chr == '(')
--pos;
// Skip to the start of a name
do {
chr = interface->characterAt(--pos);
chr = interface()->characterAt(--pos);
} while (chr.isLetterOrNumber() || chr == '_');
++pos;
int startPosition = pos;
if (interface->reason() == IdleEditor) {
QChar characterUnderCursor = interface->characterAt(interface->position());
if (characterUnderCursor.isLetterOrNumber() || interface->position() - startPosition
if (interface()->reason() == IdleEditor) {
QChar characterUnderCursor = interface()->characterAt(interface()->position());
if (characterUnderCursor.isLetterOrNumber() || interface()->position() - startPosition
< TextEditorSettings::completionSettings().m_characterThreshold) {
QList<AssistProposalItemInterface *> items;
if (m_dynamicCompletionFunction)
m_dynamicCompletionFunction(interface, &items, startPosition);
m_dynamicCompletionFunction(interface(), &items, startPosition);
if (items.isEmpty())
return nullptr;
return new GenericProposal(startPosition, items);
@@ -187,11 +186,11 @@ IAssistProposal *KeywordsCompletionAssistProcessor::performAsync(AssistInterface
// extract word
QString word;
do {
word += interface->characterAt(pos);
chr = interface->characterAt(++pos);
word += interface()->characterAt(pos);
chr = interface()->characterAt(++pos);
} while ((chr.isLetterOrNumber() || chr == '_') && chr != '(');
if (m_keywords.isFunction(word) && interface->characterAt(pos) == '(') {
if (m_keywords.isFunction(word) && interface()->characterAt(pos) == '(') {
QStringList functionSymbols = m_keywords.argsForFunction(word);
if (functionSymbols.size() == 0)
return nullptr;
@@ -201,7 +200,7 @@ IAssistProposal *KeywordsCompletionAssistProcessor::performAsync(AssistInterface
const int originalStartPos = startPosition;
QList<AssistProposalItemInterface *> items;
if (m_dynamicCompletionFunction)
m_dynamicCompletionFunction(interface, &items, startPosition);
m_dynamicCompletionFunction(interface(), &items, startPosition);
if (startPosition == originalStartPos) {
items.append(m_snippetCollector.collect());
items.append(generateProposalList(m_keywords.variables(), m_variableIcon));

View File

@@ -85,7 +85,7 @@ public:
KeywordsCompletionAssistProcessor(const Keywords &keywords);
~KeywordsCompletionAssistProcessor() override = default;
IAssistProposal *performAsync(AssistInterface *interface) override;
IAssistProposal *performAsync() override;
void setSnippetGroup(const QString &id);