forked from qt-creator/qt-creator
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:
@@ -46,7 +46,7 @@ public:
|
||||
unsigned completionOperator, CustomAssistMode mode);
|
||||
|
||||
private:
|
||||
IAssistProposal *perform(AssistInterface *interface) override;
|
||||
IAssistProposal *perform() override;
|
||||
|
||||
AssistProposalItemInterface *createItem(const QString &text, const QIcon &icon) const;
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
~ClangdCompletionAssistProcessor();
|
||||
|
||||
private:
|
||||
IAssistProposal *perform(AssistInterface *interface) override;
|
||||
IAssistProposal *perform() override;
|
||||
QList<AssistProposalItemInterface *> generateCompletionItems(
|
||||
const QList<LanguageServerProtocol::CompletionItem> &items) const override;
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
ClangdFunctionHintProcessor(ClangdClient *client);
|
||||
|
||||
private:
|
||||
IAssistProposal *perform(AssistInterface *interface) override;
|
||||
IAssistProposal *perform() override;
|
||||
|
||||
ClangdClient * const m_client;
|
||||
};
|
||||
@@ -152,11 +152,7 @@ IAssistProcessor *ClangdCompletionAssistProvider::createProcessor(
|
||||
if (contextAnalyzer.completionAction()
|
||||
!= ClangCompletionContextAnalyzer::CompleteIncludePath) {
|
||||
class NoOpProcessor : public IAssistProcessor {
|
||||
IAssistProposal *perform(AssistInterface *interface) override
|
||||
{
|
||||
delete interface;
|
||||
return nullptr;
|
||||
}
|
||||
IAssistProposal *perform() override { return nullptr; }
|
||||
};
|
||||
return new NoOpProcessor;
|
||||
}
|
||||
@@ -406,7 +402,7 @@ CustomAssistProcessor::CustomAssistProcessor(ClangdClient *client, int position,
|
||||
, m_mode(mode)
|
||||
{}
|
||||
|
||||
IAssistProposal *CustomAssistProcessor::perform(AssistInterface *interface)
|
||||
IAssistProposal *CustomAssistProcessor::perform()
|
||||
{
|
||||
QList<AssistProposalItemInterface *> completions;
|
||||
switch (m_mode) {
|
||||
@@ -422,21 +418,20 @@ IAssistProposal *CustomAssistProcessor::perform(AssistInterface *interface)
|
||||
: CppCompletionAssistProcessor::preprocessorCompletions()) {
|
||||
completions << createItem(completion, macroIcon);
|
||||
}
|
||||
if (ProjectFile::isObjC(interface->filePath().toString()))
|
||||
if (ProjectFile::isObjC(interface()->filePath().toString()))
|
||||
completions << createItem("import", macroIcon);
|
||||
break;
|
||||
}
|
||||
case CustomAssistMode::IncludePath: {
|
||||
HeaderPaths headerPaths;
|
||||
const ProjectPart::ConstPtr projectPart
|
||||
= projectPartForFile(interface->filePath().toString());
|
||||
= projectPartForFile(interface()->filePath().toString());
|
||||
if (projectPart)
|
||||
headerPaths = projectPart->headerPaths;
|
||||
completions = completeInclude(m_endPos, m_completionOperator, interface, headerPaths);
|
||||
completions = completeInclude(m_endPos, m_completionOperator, interface(), headerPaths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete interface;
|
||||
GenericProposalModelPtr model(new GenericProposalModel);
|
||||
model->loadContent(completions);
|
||||
const auto proposal = new GenericProposal(m_position, model);
|
||||
@@ -572,14 +567,14 @@ ClangdCompletionAssistProcessor::~ClangdCompletionAssistProcessor()
|
||||
<< "ClangdCompletionAssistProcessor took: " << m_timer.elapsed() << " ms";
|
||||
}
|
||||
|
||||
IAssistProposal *ClangdCompletionAssistProcessor::perform(AssistInterface *interface)
|
||||
IAssistProposal *ClangdCompletionAssistProcessor::perform()
|
||||
{
|
||||
if (m_client->testingEnabled()) {
|
||||
setAsyncCompletionAvailableHandler([this](IAssistProposal *proposal) {
|
||||
emit m_client->proposalReady(proposal);
|
||||
});
|
||||
}
|
||||
return LanguageClientCompletionAssistProcessor::perform(interface);
|
||||
return LanguageClientCompletionAssistProcessor::perform();
|
||||
}
|
||||
|
||||
QList<AssistProposalItemInterface *> ClangdCompletionAssistProcessor::generateCompletionItems(
|
||||
@@ -618,14 +613,14 @@ ClangdFunctionHintProcessor::ClangdFunctionHintProcessor(ClangdClient *client)
|
||||
, m_client(client)
|
||||
{}
|
||||
|
||||
IAssistProposal *ClangdFunctionHintProcessor::perform(AssistInterface *interface)
|
||||
IAssistProposal *ClangdFunctionHintProcessor::perform()
|
||||
{
|
||||
if (m_client->testingEnabled()) {
|
||||
setAsyncCompletionAvailableHandler([this](IAssistProposal *proposal) {
|
||||
emit m_client->proposalReady(proposal);
|
||||
});
|
||||
}
|
||||
return FunctionHintProcessor::perform(interface);
|
||||
return FunctionHintProcessor::perform();
|
||||
}
|
||||
|
||||
ClangdCompletionCapabilities::ClangdCompletionCapabilities(const JsonObject &object)
|
||||
|
@@ -42,9 +42,8 @@ public:
|
||||
void resetData(bool resetFollowSymbolData);
|
||||
|
||||
private:
|
||||
IAssistProposal *perform(AssistInterface *interface) override
|
||||
IAssistProposal *perform() override
|
||||
{
|
||||
delete interface;
|
||||
return createProposal(false);
|
||||
}
|
||||
|
||||
@@ -61,7 +60,7 @@ public:
|
||||
: m_followSymbol(followSymbol) {}
|
||||
|
||||
private:
|
||||
IAssistProcessor *createProcessor(const AssistInterface *) const override;
|
||||
IAssistProcessor *createProcessor(const AssistInterface *interface) const override;
|
||||
|
||||
const QPointer<ClangdFollowSymbol> m_followSymbol;
|
||||
};
|
||||
|
@@ -47,15 +47,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
IAssistProposal *perform(AssistInterface *interface) override
|
||||
IAssistProposal *perform() override
|
||||
{
|
||||
m_interface = interface;
|
||||
|
||||
// Step 1: Collect clangd code actions asynchronously
|
||||
LanguageClientQuickFixAssistProcessor::perform(interface);
|
||||
LanguageClientQuickFixAssistProcessor::perform();
|
||||
|
||||
// Step 2: Collect built-in quickfixes synchronously
|
||||
m_builtinOps = CppEditor::quickFixOperations(interface);
|
||||
m_builtinOps = CppEditor::quickFixOperations(interface());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -82,13 +80,12 @@ private:
|
||||
ops << op;
|
||||
}
|
||||
}
|
||||
return GenericProposal::createProposal(m_interface, ops + m_builtinOps);
|
||||
return GenericProposal::createProposal(interface(), ops + m_builtinOps);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QuickFixOperations m_builtinOps;
|
||||
const AssistInterface *m_interface = nullptr;
|
||||
};
|
||||
|
||||
ClangdQuickFixProvider::ClangdQuickFixProvider(ClangdClient *client)
|
||||
|
@@ -24,7 +24,7 @@ class CMakeFileCompletionAssist : public KeywordsCompletionAssistProcessor
|
||||
public:
|
||||
CMakeFileCompletionAssist();
|
||||
|
||||
IAssistProposal *performAsync(AssistInterface *interface) final;
|
||||
IAssistProposal *performAsync() final;
|
||||
};
|
||||
|
||||
CMakeFileCompletionAssist::CMakeFileCompletionAssist() :
|
||||
@@ -34,10 +34,10 @@ CMakeFileCompletionAssist::CMakeFileCompletionAssist() :
|
||||
setDynamicCompletionFunction(&TextEditor::pathComplete);
|
||||
}
|
||||
|
||||
IAssistProposal *CMakeFileCompletionAssist::performAsync(AssistInterface *interface)
|
||||
IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
||||
{
|
||||
Keywords kw;
|
||||
const Utils::FilePath &filePath = interface->filePath();
|
||||
const Utils::FilePath &filePath = interface()->filePath();
|
||||
if (!filePath.isEmpty() && filePath.toFileInfo().isFile()) {
|
||||
Project *p = SessionManager::projectForFile(filePath);
|
||||
if (p && p->activeTarget()) {
|
||||
@@ -48,7 +48,7 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync(AssistInterface *interf
|
||||
}
|
||||
|
||||
setKeywords(kw);
|
||||
return KeywordsCompletionAssistProcessor::performAsync(interface);
|
||||
return KeywordsCompletionAssistProcessor::performAsync();
|
||||
}
|
||||
|
||||
IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor(const AssistInterface *) const
|
||||
|
@@ -86,17 +86,19 @@ public:
|
||||
QTextCursor textCursor = m_editorWidget->textCursor();
|
||||
textCursor.setPosition(m_position);
|
||||
m_editorWidget->setTextCursor(textCursor);
|
||||
CppCompletionAssistInterface *ai
|
||||
= new CppCompletionAssistInterface(m_editorWidget->textDocument()->filePath(),
|
||||
m_editorWidget,
|
||||
ExplicitlyInvoked, m_snapshot,
|
||||
ProjectExplorer::HeaderPaths(),
|
||||
languageFeatures);
|
||||
std::unique_ptr<CppCompletionAssistInterface> ai(
|
||||
new CppCompletionAssistInterface(m_editorWidget->textDocument()->filePath(),
|
||||
m_editorWidget,
|
||||
ExplicitlyInvoked,
|
||||
m_snapshot,
|
||||
ProjectExplorer::HeaderPaths(),
|
||||
languageFeatures));
|
||||
ai->prepareForAsyncUse();
|
||||
ai->recreateTextDocument();
|
||||
InternalCppCompletionAssistProcessor processor;
|
||||
processor.setupAssistInterface(std::move(ai));
|
||||
|
||||
const QScopedPointer<IAssistProposal> proposal(processor.performAsync(ai));
|
||||
const QScopedPointer<IAssistProposal> proposal(processor.performAsync());
|
||||
if (!proposal)
|
||||
return completions;
|
||||
ProposalModelPtr model = proposal->model();
|
||||
|
@@ -400,7 +400,7 @@ IAssistProcessor *InternalCompletionAssistProvider::createProcessor(const Assist
|
||||
return new InternalCppCompletionAssistProcessor;
|
||||
}
|
||||
|
||||
AssistInterface *InternalCompletionAssistProvider::createAssistInterface(
|
||||
std::unique_ptr<AssistInterface> InternalCompletionAssistProvider::createAssistInterface(
|
||||
const Utils::FilePath &filePath,
|
||||
const TextEditorWidget *textEditorWidget,
|
||||
const LanguageFeatures &languageFeatures,
|
||||
@@ -408,12 +408,13 @@ AssistInterface *InternalCompletionAssistProvider::createAssistInterface(
|
||||
{
|
||||
QTC_ASSERT(textEditorWidget, return nullptr);
|
||||
|
||||
return new CppCompletionAssistInterface(filePath,
|
||||
textEditorWidget,
|
||||
BuiltinEditorDocumentParser::get(filePath.toString()),
|
||||
languageFeatures,
|
||||
reason,
|
||||
CppModelManager::instance()->workingCopy());
|
||||
return std::make_unique<CppCompletionAssistInterface>(
|
||||
filePath,
|
||||
textEditorWidget,
|
||||
BuiltinEditorDocumentParser::get(filePath.toString()),
|
||||
languageFeatures,
|
||||
reason,
|
||||
CppModelManager::instance()->workingCopy());
|
||||
}
|
||||
|
||||
// -----------------
|
||||
@@ -794,11 +795,9 @@ InternalCppCompletionAssistProcessor::InternalCppCompletionAssistProcessor()
|
||||
|
||||
InternalCppCompletionAssistProcessor::~InternalCppCompletionAssistProcessor() = default;
|
||||
|
||||
IAssistProposal * InternalCppCompletionAssistProcessor::performAsync(AssistInterface *interface)
|
||||
IAssistProposal * InternalCppCompletionAssistProcessor::performAsync()
|
||||
{
|
||||
m_interface.reset(static_cast<const CppCompletionAssistInterface *>(interface));
|
||||
|
||||
if (interface->reason() != ExplicitlyInvoked && !accepts())
|
||||
if (interface()->reason() != ExplicitlyInvoked && !accepts())
|
||||
return nullptr;
|
||||
|
||||
int index = startCompletionHelper();
|
||||
@@ -814,13 +813,13 @@ IAssistProposal * InternalCppCompletionAssistProcessor::performAsync(AssistInter
|
||||
|
||||
bool InternalCppCompletionAssistProcessor::accepts() const
|
||||
{
|
||||
const int pos = m_interface->position();
|
||||
const int pos = interface()->position();
|
||||
unsigned token = T_EOF_SYMBOL;
|
||||
|
||||
const int start = startOfOperator(pos, &token, /*want function call=*/ true);
|
||||
if (start != pos) {
|
||||
if (token == T_POUND) {
|
||||
const int column = pos - m_interface->textDocument()->findBlock(start).position();
|
||||
const int column = pos - interface()->textDocument()->findBlock(start).position();
|
||||
if (column != 1)
|
||||
return false;
|
||||
}
|
||||
@@ -828,15 +827,15 @@ bool InternalCppCompletionAssistProcessor::accepts() const
|
||||
return true;
|
||||
} else {
|
||||
// Trigger completion after n characters of a name have been typed, when not editing an existing name
|
||||
QChar characterUnderCursor = m_interface->characterAt(pos);
|
||||
QChar characterUnderCursor = interface()->characterAt(pos);
|
||||
|
||||
if (!isValidIdentifierChar(characterUnderCursor)) {
|
||||
const int startOfName = findStartOfName(pos);
|
||||
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
|
||||
const QChar firstCharacter = m_interface->characterAt(startOfName);
|
||||
const QChar firstCharacter = interface()->characterAt(startOfName);
|
||||
if (isValidFirstIdentifierChar(firstCharacter)) {
|
||||
return !isInCommentOrString(m_interface.data(),
|
||||
m_interface->languageFeatures());
|
||||
return !isInCommentOrString(interface(),
|
||||
cppInterface()->languageFeatures());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -887,9 +886,9 @@ int InternalCppCompletionAssistProcessor::startOfOperator(int positionInDocument
|
||||
unsigned *kind,
|
||||
bool wantFunctionCall) const
|
||||
{
|
||||
const QChar ch = m_interface->characterAt(positionInDocument - 1);
|
||||
const QChar ch2 = m_interface->characterAt(positionInDocument - 2);
|
||||
const QChar ch3 = m_interface->characterAt(positionInDocument - 3);
|
||||
const QChar ch = interface()->characterAt(positionInDocument - 1);
|
||||
const QChar ch2 = interface()->characterAt(positionInDocument - 2);
|
||||
const QChar ch3 = interface()->characterAt(positionInDocument - 3);
|
||||
|
||||
int start = positionInDocument
|
||||
- CppCompletionAssistProvider::activationSequenceChar(ch, ch2, ch3, kind,
|
||||
@@ -898,18 +897,18 @@ int InternalCppCompletionAssistProcessor::startOfOperator(int positionInDocument
|
||||
|
||||
const auto dotAtIncludeCompletionHandler = [this](int &start, unsigned *kind) {
|
||||
start = findStartOfName(start);
|
||||
const QChar ch4 = m_interface->characterAt(start - 1);
|
||||
const QChar ch5 = m_interface->characterAt(start - 2);
|
||||
const QChar ch6 = m_interface->characterAt(start - 3);
|
||||
const QChar ch4 = interface()->characterAt(start - 1);
|
||||
const QChar ch5 = interface()->characterAt(start - 2);
|
||||
const QChar ch6 = interface()->characterAt(start - 3);
|
||||
start = start - CppCompletionAssistProvider::activationSequenceChar(
|
||||
ch4, ch5, ch6, kind, false, false);
|
||||
};
|
||||
|
||||
CppCompletionAssistProcessor::startOfOperator(m_interface->textDocument(),
|
||||
CppCompletionAssistProcessor::startOfOperator(cppInterface()->textDocument(),
|
||||
positionInDocument,
|
||||
kind,
|
||||
start,
|
||||
m_interface->languageFeatures(),
|
||||
cppInterface()->languageFeatures(),
|
||||
/*adjustForQt5SignalSlotCompletion=*/ true,
|
||||
dotAtIncludeCompletionHandler);
|
||||
return start;
|
||||
@@ -918,12 +917,12 @@ int InternalCppCompletionAssistProcessor::startOfOperator(int positionInDocument
|
||||
int InternalCppCompletionAssistProcessor::findStartOfName(int pos) const
|
||||
{
|
||||
if (pos == -1)
|
||||
pos = m_interface->position();
|
||||
pos = interface()->position();
|
||||
QChar chr;
|
||||
|
||||
// Skip to the start of a name
|
||||
do {
|
||||
chr = m_interface->characterAt(--pos);
|
||||
chr = interface()->characterAt(--pos);
|
||||
} while (isValidIdentifierChar(chr));
|
||||
|
||||
return pos + 1;
|
||||
@@ -931,7 +930,7 @@ int InternalCppCompletionAssistProcessor::findStartOfName(int pos) const
|
||||
|
||||
int InternalCppCompletionAssistProcessor::startCompletionHelper()
|
||||
{
|
||||
if (m_interface->languageFeatures().objCEnabled) {
|
||||
if (cppInterface()->languageFeatures().objCEnabled) {
|
||||
if (tryObjCCompletion())
|
||||
return m_positionForProposal;
|
||||
}
|
||||
@@ -943,7 +942,7 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper()
|
||||
int endOfOperator = m_positionForProposal;
|
||||
|
||||
// Skip whitespace preceding this position
|
||||
while (m_interface->characterAt(endOfOperator - 1).isSpace())
|
||||
while (interface()->characterAt(endOfOperator - 1).isSpace())
|
||||
--endOfOperator;
|
||||
|
||||
int endOfExpression = startOfOperator(endOfOperator,
|
||||
@@ -968,15 +967,15 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper()
|
||||
|| m_model->m_completionOperator == T_ANGLE_STRING_LITERAL
|
||||
|| m_model->m_completionOperator == T_SLASH) {
|
||||
|
||||
QTextCursor c(m_interface->textDocument());
|
||||
QTextCursor c(interface()->textDocument());
|
||||
c.setPosition(endOfExpression);
|
||||
if (completeInclude(c))
|
||||
m_positionForProposal = endOfExpression + 1;
|
||||
return m_positionForProposal;
|
||||
}
|
||||
|
||||
ExpressionUnderCursor expressionUnderCursor(m_interface->languageFeatures());
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
ExpressionUnderCursor expressionUnderCursor(cppInterface()->languageFeatures());
|
||||
QTextCursor tc(interface()->textDocument());
|
||||
|
||||
if (m_model->m_completionOperator == T_COMMA) {
|
||||
tc.setPosition(endOfExpression);
|
||||
@@ -992,7 +991,7 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper()
|
||||
}
|
||||
|
||||
QString expression;
|
||||
int startOfExpression = m_interface->position();
|
||||
int startOfExpression = interface()->position();
|
||||
tc.setPosition(endOfExpression);
|
||||
|
||||
if (m_model->m_completionOperator) {
|
||||
@@ -1004,7 +1003,7 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper()
|
||||
// "connect(sender, &" or
|
||||
// "connect(otherSender, &Foo::signal1, receiver, &"
|
||||
const int beforeExpression = startOfExpression - 1;
|
||||
if (canCompleteClassNameAt2ndOr4thConnectArgument(m_interface.data(),
|
||||
if (canCompleteClassNameAt2ndOr4thConnectArgument(cppInterface(),
|
||||
beforeExpression)) {
|
||||
m_model->m_completionOperator = CompleteQt5SignalOrSlotClassNameTrigger;
|
||||
} else { // Ensure global completion
|
||||
@@ -1017,45 +1016,45 @@ int InternalCppCompletionAssistProcessor::startCompletionHelper()
|
||||
// "connect(sender, &Foo::" or
|
||||
// "connect(sender, &Bar::signal1, receiver, &Foo::"
|
||||
const int beforeExpression = startOfExpression - 1;
|
||||
if (canCompleteConnectSignalAt2ndArgument(m_interface.data(), beforeExpression))
|
||||
if (canCompleteConnectSignalAt2ndArgument(cppInterface(), beforeExpression))
|
||||
m_model->m_completionOperator = CompleteQt5SignalTrigger;
|
||||
else if (canCompleteConnectSignalAt4thArgument(m_interface.data(), beforeExpression))
|
||||
else if (canCompleteConnectSignalAt4thArgument(cppInterface(), beforeExpression))
|
||||
m_model->m_completionOperator = CompleteQt5SlotTrigger;
|
||||
} else if (m_model->m_completionOperator == T_LPAREN) {
|
||||
if (expression.endsWith(QLatin1String("SIGNAL"))) {
|
||||
m_model->m_completionOperator = T_SIGNAL;
|
||||
} else if (expression.endsWith(QLatin1String("SLOT"))) {
|
||||
m_model->m_completionOperator = T_SLOT;
|
||||
} else if (m_interface->position() != endOfOperator) {
|
||||
} else if (interface()->position() != endOfOperator) {
|
||||
// We don't want a function completion when the cursor isn't at the opening brace
|
||||
expression.clear();
|
||||
m_model->m_completionOperator = T_EOF_SYMBOL;
|
||||
m_positionForProposal = startOfName;
|
||||
startOfExpression = m_interface->position();
|
||||
startOfExpression = interface()->position();
|
||||
}
|
||||
}
|
||||
} else if (expression.isEmpty()) {
|
||||
while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
|
||||
while (startOfExpression > 0 && interface()->characterAt(startOfExpression).isSpace())
|
||||
--startOfExpression;
|
||||
}
|
||||
|
||||
int line = 0, column = 0;
|
||||
Utils::Text::convertPosition(m_interface->textDocument(), startOfExpression, &line, &column);
|
||||
const QString fileName = m_interface->filePath().toString();
|
||||
Utils::Text::convertPosition(interface()->textDocument(), startOfExpression, &line, &column);
|
||||
const QString fileName = interface()->filePath().toString();
|
||||
return startCompletionInternal(fileName, line, column - 1, expression, endOfExpression);
|
||||
}
|
||||
|
||||
bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
|
||||
{
|
||||
int end = m_interface->position();
|
||||
while (m_interface->characterAt(end).isSpace())
|
||||
int end = interface()->position();
|
||||
while (interface()->characterAt(end).isSpace())
|
||||
++end;
|
||||
if (m_interface->characterAt(end) != QLatin1Char(']'))
|
||||
if (interface()->characterAt(end) != QLatin1Char(']'))
|
||||
return false;
|
||||
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
QTextCursor tc(interface()->textDocument());
|
||||
tc.setPosition(end);
|
||||
BackwardsScanner tokens(tc, m_interface->languageFeatures());
|
||||
BackwardsScanner tokens(tc, cppInterface()->languageFeatures());
|
||||
if (tokens[tokens.startToken() - 1].isNot(T_RBRACKET))
|
||||
return false;
|
||||
|
||||
@@ -1064,23 +1063,23 @@ bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
|
||||
return false;
|
||||
|
||||
const int startPos = tokens[start].bytesBegin() + tokens.startPosition();
|
||||
const QString expr = m_interface->textAt(startPos, m_interface->position() - startPos);
|
||||
const QString expr = interface()->textAt(startPos, interface()->position() - startPos);
|
||||
|
||||
Document::Ptr thisDocument = m_interface->snapshot().document(m_interface->filePath());
|
||||
Document::Ptr thisDocument = cppInterface()->snapshot().document(interface()->filePath());
|
||||
if (!thisDocument)
|
||||
return false;
|
||||
|
||||
m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
|
||||
m_model->m_typeOfExpression->init(thisDocument, cppInterface()->snapshot());
|
||||
|
||||
int line = 0, column = 0;
|
||||
Utils::Text::convertPosition(m_interface->textDocument(), m_interface->position(), &line,
|
||||
Utils::Text::convertPosition(interface()->textDocument(), interface()->position(), &line,
|
||||
&column);
|
||||
Scope *scope = thisDocument->scopeAt(line, column - 1);
|
||||
if (!scope)
|
||||
return false;
|
||||
|
||||
const QList<LookupItem> items = (*m_model->m_typeOfExpression)(expr.toUtf8(), scope);
|
||||
LookupContext lookupContext(thisDocument, m_interface->snapshot());
|
||||
LookupContext lookupContext(thisDocument, cppInterface()->snapshot());
|
||||
|
||||
for (const LookupItem &item : items) {
|
||||
FullySpecifiedType ty = item.type().simplified();
|
||||
@@ -1102,7 +1101,7 @@ bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
|
||||
if (m_completions.isEmpty())
|
||||
return false;
|
||||
|
||||
m_positionForProposal = m_interface->position();
|
||||
m_positionForProposal = interface()->position();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1210,9 +1209,9 @@ bool InternalCppCompletionAssistProcessor::completeInclude(const QTextCursor &cu
|
||||
}
|
||||
|
||||
// Make completion for all relevant includes
|
||||
ProjectExplorer::HeaderPaths headerPaths = m_interface->headerPaths();
|
||||
ProjectExplorer::HeaderPaths headerPaths = cppInterface()->headerPaths();
|
||||
const auto currentFilePath = ProjectExplorer::HeaderPath::makeUser(
|
||||
m_interface->filePath().toFileInfo().path());
|
||||
interface()->filePath().toFileInfo().path());
|
||||
if (!headerPaths.contains(currentFilePath))
|
||||
headerPaths.append(currentFilePath);
|
||||
|
||||
@@ -1261,10 +1260,10 @@ void InternalCppCompletionAssistProcessor::completePreprocessor()
|
||||
|
||||
bool InternalCppCompletionAssistProcessor::objcKeywordsWanted() const
|
||||
{
|
||||
if (!m_interface->languageFeatures().objCEnabled)
|
||||
if (!cppInterface()->languageFeatures().objCEnabled)
|
||||
return false;
|
||||
|
||||
const Utils::MimeType mt = Utils::mimeTypeForFile(m_interface->filePath());
|
||||
const Utils::MimeType mt = Utils::mimeTypeForFile(interface()->filePath());
|
||||
return mt.matchesName(QLatin1String(CppEditor::Constants::OBJECTIVE_C_SOURCE_MIMETYPE))
|
||||
|| mt.matchesName(QLatin1String(CppEditor::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
|
||||
}
|
||||
@@ -1277,11 +1276,11 @@ int InternalCppCompletionAssistProcessor::startCompletionInternal(const QString
|
||||
{
|
||||
QString expression = expr.trimmed();
|
||||
|
||||
Document::Ptr thisDocument = m_interface->snapshot().document(fileName);
|
||||
Document::Ptr thisDocument = cppInterface()->snapshot().document(fileName);
|
||||
if (!thisDocument)
|
||||
return -1;
|
||||
|
||||
m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
|
||||
m_model->m_typeOfExpression->init(thisDocument, cppInterface()->snapshot());
|
||||
|
||||
Scope *scope = thisDocument->scopeAt(line, positionInBlock);
|
||||
QTC_ASSERT(scope, return -1);
|
||||
@@ -1315,14 +1314,14 @@ int InternalCppCompletionAssistProcessor::startCompletionInternal(const QString
|
||||
} else if (m_model->m_completionOperator == T_LPAREN) {
|
||||
// Find the expression that precedes the current name
|
||||
int index = endOfExpression;
|
||||
while (m_interface->characterAt(index - 1).isSpace())
|
||||
while (interface()->characterAt(index - 1).isSpace())
|
||||
--index;
|
||||
index = findStartOfName(index);
|
||||
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
QTextCursor tc(interface()->textDocument());
|
||||
tc.setPosition(index);
|
||||
|
||||
ExpressionUnderCursor expressionUnderCursor(m_interface->languageFeatures());
|
||||
ExpressionUnderCursor expressionUnderCursor(cppInterface()->languageFeatures());
|
||||
const QString baseExpression = expressionUnderCursor(tc);
|
||||
|
||||
// Resolve the type of this expression
|
||||
@@ -1506,7 +1505,7 @@ bool InternalCppCompletionAssistProcessor::completeMember(const QList<LookupItem
|
||||
ResolveExpression resolveExpression(context);
|
||||
|
||||
bool *replaceDotForArrow = nullptr;
|
||||
if (!m_interface->languageFeatures().objCEnabled)
|
||||
if (!cppInterface()->languageFeatures().objCEnabled)
|
||||
replaceDotForArrow = &m_model->m_replaceDotForArrow;
|
||||
|
||||
if (ClassOrNamespace *binding =
|
||||
@@ -1836,7 +1835,7 @@ void InternalCppCompletionAssistProcessor::addKeywords()
|
||||
addKeywordCompletionItem(QLatin1String(Token::name(i)));
|
||||
|
||||
// "Identifiers with special meaning"
|
||||
if (m_interface->languageFeatures().cxx11Enabled) {
|
||||
if (cppInterface()->languageFeatures().cxx11Enabled) {
|
||||
addKeywordCompletionItem(QLatin1String("override"));
|
||||
addKeywordCompletionItem(QLatin1String("final"));
|
||||
}
|
||||
@@ -1879,6 +1878,11 @@ void InternalCppCompletionAssistProcessor::addMacros_helper(const Snapshot &snap
|
||||
}
|
||||
}
|
||||
|
||||
const CppCompletionAssistInterface *InternalCppCompletionAssistProcessor::cppInterface() const
|
||||
{
|
||||
return static_cast<const CppCompletionAssistInterface *>(interface());
|
||||
}
|
||||
|
||||
bool InternalCppCompletionAssistProcessor::completeConstructorOrFunction(const QList<LookupItem> &results,
|
||||
int endOfExpression,
|
||||
bool toolTipOnly)
|
||||
@@ -1983,7 +1987,7 @@ bool InternalCppCompletionAssistProcessor::completeConstructorOrFunction(const Q
|
||||
|
||||
// get current line and column
|
||||
int lineSigned = 0, columnSigned = 0;
|
||||
Utils::Text::convertPosition(m_interface->textDocument(), m_interface->position(),
|
||||
Utils::Text::convertPosition(interface()->textDocument(), interface()->position(),
|
||||
&lineSigned, &columnSigned);
|
||||
unsigned line = lineSigned, column = columnSigned - 1;
|
||||
|
||||
@@ -1997,9 +2001,9 @@ bool InternalCppCompletionAssistProcessor::completeConstructorOrFunction(const Q
|
||||
// declaration, we should be certain that it isn't.
|
||||
bool autocompleteSignature = false;
|
||||
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
QTextCursor tc(interface()->textDocument());
|
||||
tc.setPosition(endOfExpression);
|
||||
BackwardsScanner bs(tc, m_interface->languageFeatures());
|
||||
BackwardsScanner bs(tc, cppInterface()->languageFeatures());
|
||||
const int startToken = bs.startToken();
|
||||
int lineStartToken = bs.startOfLine(startToken);
|
||||
// make sure the required tokens are actually available
|
||||
|
@@ -61,7 +61,7 @@ class InternalCompletionAssistProvider : public CppCompletionAssistProvider
|
||||
public:
|
||||
TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override;
|
||||
|
||||
TextEditor::AssistInterface *createAssistInterface(
|
||||
std::unique_ptr<TextEditor::AssistInterface> createAssistInterface(
|
||||
const Utils::FilePath &filePath,
|
||||
const TextEditor::TextEditorWidget *textEditorWidget,
|
||||
const CPlusPlus::LanguageFeatures &languageFeatures,
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
InternalCppCompletionAssistProcessor();
|
||||
~InternalCppCompletionAssistProcessor() override;
|
||||
|
||||
TextEditor::IAssistProposal *performAsync(TextEditor::AssistInterface *interface) override;
|
||||
TextEditor::IAssistProposal *performAsync() override;
|
||||
|
||||
private:
|
||||
TextEditor::IAssistProposal *createContentProposal();
|
||||
@@ -135,6 +135,7 @@ private:
|
||||
};
|
||||
|
||||
QScopedPointer<const CppCompletionAssistInterface> m_interface;
|
||||
const CppCompletionAssistInterface *cppInterface() const;
|
||||
CppAssistProposalModelPtr m_model;
|
||||
};
|
||||
|
||||
|
@@ -33,7 +33,7 @@ public:
|
||||
bool isActivationCharSequence(const QString &sequence) const override;
|
||||
bool isContinuationChar(const QChar &c) const override;
|
||||
|
||||
virtual TextEditor::AssistInterface *createAssistInterface(
|
||||
virtual std::unique_ptr<TextEditor::AssistInterface> createAssistInterface(
|
||||
const Utils::FilePath &filePath,
|
||||
const TextEditor::TextEditorWidget *textEditorWidget,
|
||||
const CPlusPlus::LanguageFeatures &languageFeatures,
|
||||
|
@@ -981,7 +981,7 @@ void CppEditorWidget::processKeyNormally(QKeyEvent *e)
|
||||
TextEditorWidget::keyPressEvent(e);
|
||||
}
|
||||
|
||||
static void addRefactoringActions(QMenu *menu, AssistInterface *iface)
|
||||
static void addRefactoringActions(QMenu *menu, std::unique_ptr<AssistInterface> iface)
|
||||
{
|
||||
if (!iface || !menu)
|
||||
return;
|
||||
@@ -990,8 +990,8 @@ static void addRefactoringActions(QMenu *menu, AssistInterface *iface)
|
||||
using Proposal = QScopedPointer<IAssistProposal>;
|
||||
|
||||
const Processor processor(
|
||||
CppEditorPlugin::instance()->quickFixProvider()->createProcessor(iface));
|
||||
const Proposal proposal(processor->perform(iface)); // OK, perform() takes ownership of iface.
|
||||
CppEditorPlugin::instance()->quickFixProvider()->createProcessor(iface.get()));
|
||||
const Proposal proposal(processor->start(std::move(iface)));
|
||||
if (proposal) {
|
||||
auto model = proposal->model().staticCast<GenericProposalModel>();
|
||||
for (int index = 0; index < model->size(); ++index) {
|
||||
@@ -1174,7 +1174,8 @@ bool CppEditorWidget::isOldStyleSignalOrSlot() const
|
||||
== CppEditor::SignalSlotType::OldStyleSignal;
|
||||
}
|
||||
|
||||
AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const
|
||||
std::unique_ptr<AssistInterface> CppEditorWidget::createAssistInterface(AssistKind kind,
|
||||
AssistReason reason) const
|
||||
{
|
||||
if (kind == Completion || kind == FunctionHint) {
|
||||
CppCompletionAssistProvider * const cap = kind == Completion
|
||||
@@ -1190,10 +1191,7 @@ AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistR
|
||||
};
|
||||
|
||||
if (cap)
|
||||
return cap->createAssistInterface(textDocument()->filePath(),
|
||||
this,
|
||||
getFeatures(),
|
||||
reason);
|
||||
return cap->createAssistInterface(textDocument()->filePath(), this, getFeatures(), reason);
|
||||
else {
|
||||
if (isOldStyleSignalOrSlot())
|
||||
return CppModelManager::instance()
|
||||
@@ -1203,7 +1201,7 @@ AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistR
|
||||
}
|
||||
} else if (kind == QuickFix) {
|
||||
if (isSemanticInfoValid())
|
||||
return new CppQuickFixInterface(const_cast<CppEditorWidget *>(this), reason);
|
||||
return std::make_unique<CppQuickFixInterface>(const_cast<CppEditorWidget *>(this), reason);
|
||||
} else {
|
||||
return TextEditorWidget::createAssistInterface(kind, reason);
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ public:
|
||||
QSharedPointer<Internal::FunctionDeclDefLink> declDefLink() const;
|
||||
void applyDeclDefLinkChanges(bool jumpToMatch);
|
||||
|
||||
TextEditor::AssistInterface *createAssistInterface(
|
||||
std::unique_ptr<TextEditor::AssistInterface> createAssistInterface(
|
||||
TextEditor::AssistKind kind,
|
||||
TextEditor::AssistReason reason) const override;
|
||||
|
||||
|
@@ -29,10 +29,9 @@ namespace Internal {
|
||||
// -------------------------
|
||||
class CppQuickFixAssistProcessor : public IAssistProcessor
|
||||
{
|
||||
IAssistProposal *perform(AssistInterface *interface) override
|
||||
IAssistProposal *perform() override
|
||||
{
|
||||
QSharedPointer<const AssistInterface> dummy(interface); // FIXME: Surely this cannot be our way of doing memory management???
|
||||
return GenericProposal::createProposal(interface, quickFixOperations(interface));
|
||||
return GenericProposal::createProposal(interface(), quickFixOperations(interface()));
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -87,7 +87,7 @@ public:
|
||||
: m_params(params)
|
||||
{}
|
||||
|
||||
IAssistProposal *immediateProposal(AssistInterface *) override
|
||||
IAssistProposal *immediateProposal() override
|
||||
{
|
||||
QTC_ASSERT(m_params.function, return nullptr);
|
||||
|
||||
@@ -102,10 +102,8 @@ public:
|
||||
return new VirtualFunctionProposal(m_params.cursorPosition, items, m_params.openInNextSplit);
|
||||
}
|
||||
|
||||
IAssistProposal *performAsync(AssistInterface *assistInterface) override
|
||||
IAssistProposal *performAsync() override
|
||||
{
|
||||
delete assistInterface;
|
||||
|
||||
QTC_ASSERT(m_params.function, return nullptr);
|
||||
QTC_ASSERT(m_params.staticClass, return nullptr);
|
||||
QTC_ASSERT(!m_params.snapshot.isEmpty(), return nullptr);
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
|
||||
#include <texteditor/codeassist/assistinterface.h>
|
||||
#include <texteditor/codeassist/asyncprocessor.h>
|
||||
#include <texteditor/codeassist/genericproposalmodel.h>
|
||||
#include <texteditor/codeassist/iassistprocessor.h>
|
||||
@@ -119,14 +120,13 @@ public:
|
||||
{
|
||||
VirtualFunctionAssistProvider::configure(params);
|
||||
|
||||
AssistInterface *assistInterface
|
||||
std::unique_ptr<AssistInterface> assistInterface
|
||||
= m_editorWidget->createAssistInterface(FollowSymbol, ExplicitlyInvoked);
|
||||
const QScopedPointer<AsyncProcessor> processor(
|
||||
dynamic_cast<AsyncProcessor *>(createProcessor(assistInterface)));
|
||||
const QScopedPointer<IAssistProposal> immediateProposal(
|
||||
processor->immediateProposal(assistInterface));
|
||||
const QScopedPointer<IAssistProposal> finalProposal(
|
||||
processor->performAsync(assistInterface));
|
||||
dynamic_cast<AsyncProcessor *>(createProcessor(assistInterface.get())));
|
||||
processor->setupAssistInterface(std::move(assistInterface));
|
||||
const QScopedPointer<IAssistProposal> immediateProposal(processor->immediateProposal());
|
||||
const QScopedPointer<IAssistProposal> finalProposal(processor->performAsync());
|
||||
|
||||
VirtualFunctionAssistProvider::clearParams();
|
||||
|
||||
|
@@ -1045,14 +1045,14 @@ public:
|
||||
: m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
|
||||
{}
|
||||
|
||||
IAssistProposal *performAsync(AssistInterface *interface) override
|
||||
IAssistProposal *performAsync() override
|
||||
{
|
||||
const QString &needle = m_provider->needle();
|
||||
|
||||
const int basePosition = interface->position() - needle.size();
|
||||
const int basePosition = interface()->position() - needle.size();
|
||||
|
||||
QTextCursor tc(interface->textDocument());
|
||||
tc.setPosition(interface->position());
|
||||
QTextCursor tc(interface()->textDocument());
|
||||
tc.setPosition(interface()->position());
|
||||
tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
||||
|
||||
QList<AssistProposalItemInterface *> items;
|
||||
@@ -1078,7 +1078,6 @@ public:
|
||||
}
|
||||
//qDebug() << "COMPLETIONS" << completions->size();
|
||||
|
||||
delete interface;
|
||||
return new GenericProposal(basePosition,
|
||||
GenericProposalModelPtr(new FakeVimAssistProposalModel(items)));
|
||||
}
|
||||
|
@@ -290,17 +290,17 @@ static AssistProposalItem *createCompletionItem(const QString &text, const QIcon
|
||||
return item;
|
||||
}
|
||||
|
||||
IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *interface)
|
||||
IAssistProposal *GlslCompletionAssistProcessor::performAsync()
|
||||
{
|
||||
m_interface.reset(static_cast<const GlslCompletionAssistInterface *>(interface));
|
||||
auto interface = static_cast<const GlslCompletionAssistInterface *>(this->interface());
|
||||
|
||||
if (interface->reason() == IdleEditor && !acceptsIdleEditor())
|
||||
return nullptr;
|
||||
|
||||
int pos = m_interface->position() - 1;
|
||||
QChar ch = m_interface->characterAt(pos);
|
||||
int pos = interface->position() - 1;
|
||||
QChar ch = interface->characterAt(pos);
|
||||
while (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
|
||||
ch = m_interface->characterAt(--pos);
|
||||
ch = interface->characterAt(--pos);
|
||||
|
||||
CPlusPlus::ExpressionUnderCursor expressionUnderCursor(
|
||||
CPlusPlus::LanguageFeatures::defaultFeatures());
|
||||
@@ -310,16 +310,16 @@ IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *in
|
||||
QStringList specialMembers;
|
||||
QList<AssistProposalItemInterface *> m_completions;
|
||||
|
||||
bool functionCall = (ch == QLatin1Char('(') && pos == m_interface->position() - 1);
|
||||
bool functionCall = (ch == QLatin1Char('(') && pos == interface->position() - 1);
|
||||
|
||||
if (ch == QLatin1Char(',')) {
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
QTextCursor tc(interface->textDocument());
|
||||
tc.setPosition(pos);
|
||||
const int start = expressionUnderCursor.startOfFunctionCall(tc);
|
||||
if (start == -1)
|
||||
return nullptr;
|
||||
|
||||
if (m_interface->characterAt(start) == QLatin1Char('(')) {
|
||||
if (interface->characterAt(start) == QLatin1Char('(')) {
|
||||
pos = start;
|
||||
ch = QLatin1Char('(');
|
||||
functionCall = true;
|
||||
@@ -328,7 +328,7 @@ IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *in
|
||||
|
||||
if (ch == QLatin1Char('.') || functionCall) {
|
||||
const bool memberCompletion = ! functionCall;
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
QTextCursor tc(interface->textDocument());
|
||||
tc.setPosition(pos);
|
||||
|
||||
// get the expression under cursor
|
||||
@@ -337,7 +337,7 @@ IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *in
|
||||
|
||||
// parse the expression
|
||||
GLSL::Engine engine;
|
||||
GLSL::Parser parser(&engine, code, code.size(), languageVariant(m_interface->mimeType()));
|
||||
GLSL::Parser parser(&engine, code, code.size(), languageVariant(interface->mimeType()));
|
||||
GLSL::ExpressionAST *expr = parser.parseExpression();
|
||||
|
||||
#if 0
|
||||
@@ -347,7 +347,7 @@ IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *in
|
||||
dump(expr);
|
||||
#endif
|
||||
|
||||
if (Document::Ptr doc = m_interface->glslDocument()) {
|
||||
if (Document::Ptr doc = interface->glslDocument()) {
|
||||
GLSL::Scope *currentScope = doc->scopeAt(pos);
|
||||
|
||||
GLSL::Semantic sem;
|
||||
@@ -396,7 +396,7 @@ IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *in
|
||||
|
||||
} else {
|
||||
// it's a global completion
|
||||
if (Document::Ptr doc = m_interface->glslDocument()) {
|
||||
if (Document::Ptr doc = interface->glslDocument()) {
|
||||
GLSL::Scope *currentScope = doc->scopeAt(pos);
|
||||
bool isGlobal = !currentScope || !currentScope->scope();
|
||||
|
||||
@@ -435,12 +435,12 @@ IAssistProposal *GlslCompletionAssistProcessor::performAsync(AssistInterface *in
|
||||
}
|
||||
}
|
||||
|
||||
// if (m_keywordVariant != languageVariant(m_interface->mimeType())) {
|
||||
QStringList keywords = GLSL::Lexer::keywords(languageVariant(m_interface->mimeType()));
|
||||
// if (m_keywordVariant != languageVariant(interface->mimeType())) {
|
||||
QStringList keywords = GLSL::Lexer::keywords(languageVariant(interface->mimeType()));
|
||||
// m_keywordCompletions.clear();
|
||||
for (int index = 0; index < keywords.size(); ++index)
|
||||
m_completions << createCompletionItem(keywords.at(index), glslIcon(IconTypeKeyword));
|
||||
// m_keywordVariant = languageVariant(m_interface->mimeType());
|
||||
// m_keywordVariant = languageVariant(interface->mimeType());
|
||||
// }
|
||||
|
||||
// m_completions += m_keywordCompletions;
|
||||
@@ -491,22 +491,22 @@ IAssistProposal *GlslCompletionAssistProcessor::createHintProposal(
|
||||
|
||||
bool GlslCompletionAssistProcessor::acceptsIdleEditor() const
|
||||
{
|
||||
const int cursorPosition = m_interface->position();
|
||||
const QChar ch = m_interface->characterAt(cursorPosition - 1);
|
||||
const int cursorPosition = interface()->position();
|
||||
const QChar ch = interface()->characterAt(cursorPosition - 1);
|
||||
|
||||
const QChar characterUnderCursor = m_interface->characterAt(cursorPosition);
|
||||
const QChar characterUnderCursor = interface()->characterAt(cursorPosition);
|
||||
|
||||
if (isIdentifierChar(ch) && (characterUnderCursor.isSpace() ||
|
||||
characterUnderCursor.isNull() ||
|
||||
isDelimiter(characterUnderCursor))) {
|
||||
int pos = m_interface->position() - 1;
|
||||
int pos = interface()->position() - 1;
|
||||
for (; pos != -1; --pos) {
|
||||
if (! isIdentifierChar(m_interface->characterAt(pos)))
|
||||
if (! isIdentifierChar(interface()->characterAt(pos)))
|
||||
break;
|
||||
}
|
||||
++pos;
|
||||
|
||||
const QString word = m_interface->textAt(pos, cursorPosition - pos);
|
||||
const QString word = interface()->textAt(pos, cursorPosition - pos);
|
||||
if (word.length() >= TextEditorSettings::completionSettings().m_characterThreshold
|
||||
&& checkStartOfIdentifier(word)) {
|
||||
for (auto character : word) {
|
||||
|
@@ -72,14 +72,13 @@ class GlslCompletionAssistProcessor : public TextEditor::AsyncProcessor
|
||||
public:
|
||||
~GlslCompletionAssistProcessor() override;
|
||||
|
||||
TextEditor::IAssistProposal *performAsync(TextEditor::AssistInterface *interface) override;
|
||||
TextEditor::IAssistProposal *performAsync() override;
|
||||
|
||||
private:
|
||||
TextEditor::IAssistProposal *createHintProposal(const QVector<GLSL::Function *> &symbols);
|
||||
bool acceptsIdleEditor() const;
|
||||
|
||||
int m_startPosition = 0;
|
||||
QScopedPointer<const GlslCompletionAssistInterface> m_interface;
|
||||
};
|
||||
|
||||
class GlslCompletionAssistInterface : public TextEditor::AssistInterface
|
||||
|
@@ -143,7 +143,8 @@ public:
|
||||
|
||||
QSet<QString> identifiers() const;
|
||||
|
||||
AssistInterface *createAssistInterface(AssistKind assistKind, AssistReason reason) const override;
|
||||
std::unique_ptr<AssistInterface> createAssistInterface(AssistKind assistKind,
|
||||
AssistReason reason) const override;
|
||||
|
||||
private:
|
||||
void updateDocumentNow();
|
||||
@@ -343,16 +344,17 @@ int languageVariant(const QString &type)
|
||||
return variant;
|
||||
}
|
||||
|
||||
AssistInterface *GlslEditorWidget::createAssistInterface(
|
||||
std::unique_ptr<AssistInterface> GlslEditorWidget::createAssistInterface(
|
||||
AssistKind kind, AssistReason reason) const
|
||||
{
|
||||
if (kind == Completion)
|
||||
return new GlslCompletionAssistInterface(textCursor(),
|
||||
textDocument()->filePath(),
|
||||
reason,
|
||||
textDocument()->mimeType(),
|
||||
m_glslDocument);
|
||||
return TextEditorWidget::createAssistInterface(kind, reason);
|
||||
if (kind != Completion)
|
||||
return TextEditorWidget::createAssistInterface(kind, reason);
|
||||
|
||||
return std::make_unique<GlslCompletionAssistInterface>(textCursor(),
|
||||
textDocument()->filePath(),
|
||||
reason,
|
||||
textDocument()->mimeType(),
|
||||
m_glslDocument);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -293,8 +293,7 @@ LanguageClientCompletionAssistProcessor::~LanguageClientCompletionAssistProcesso
|
||||
|
||||
QTextDocument *LanguageClientCompletionAssistProcessor::document() const
|
||||
{
|
||||
QTC_ASSERT(m_assistInterface, return nullptr);
|
||||
return m_assistInterface->textDocument();
|
||||
return interface()->textDocument();
|
||||
}
|
||||
|
||||
QList<AssistProposalItemInterface *> LanguageClientCompletionAssistProcessor::generateCompletionItems(
|
||||
@@ -314,26 +313,25 @@ static QString assistReasonString(AssistReason reason)
|
||||
return QString("unknown reason");
|
||||
}
|
||||
|
||||
IAssistProposal *LanguageClientCompletionAssistProcessor::perform(AssistInterface *interface)
|
||||
IAssistProposal *LanguageClientCompletionAssistProcessor::perform()
|
||||
{
|
||||
m_assistInterface.reset(interface);
|
||||
QTC_ASSERT(m_client, return nullptr);
|
||||
m_pos = interface->position();
|
||||
m_pos = interface()->position();
|
||||
m_basePos = m_pos;
|
||||
auto isIdentifierChar = [](const QChar &c) { return c.isLetterOrNumber() || c == '_'; };
|
||||
while (m_basePos > 0 && isIdentifierChar(interface->characterAt(m_basePos - 1)))
|
||||
while (m_basePos > 0 && isIdentifierChar(interface()->characterAt(m_basePos - 1)))
|
||||
--m_basePos;
|
||||
if (interface->reason() == IdleEditor) {
|
||||
if (interface()->reason() == IdleEditor) {
|
||||
// Trigger an automatic completion request only when we are on a word with at least n "identifier" characters
|
||||
if (m_pos - m_basePos < TextEditorSettings::completionSettings().m_characterThreshold)
|
||||
return nullptr;
|
||||
if (m_client->documentUpdatePostponed(interface->filePath())) {
|
||||
if (m_client->documentUpdatePostponed(interface()->filePath())) {
|
||||
m_postponedUpdateConnection
|
||||
= QObject::connect(m_client,
|
||||
&Client::documentUpdated,
|
||||
[this, interface](TextEditor::TextDocument *document) {
|
||||
if (document->filePath() == interface->filePath())
|
||||
perform(interface);
|
||||
[this](TextEditor::TextDocument *document) {
|
||||
if (document->filePath() == interface()->filePath())
|
||||
perform();
|
||||
});
|
||||
return nullptr;
|
||||
}
|
||||
@@ -341,9 +339,9 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(AssistInterfac
|
||||
if (m_postponedUpdateConnection)
|
||||
QObject::disconnect(m_postponedUpdateConnection);
|
||||
CompletionParams::CompletionContext context;
|
||||
if (interface->reason() == ActivationCharacter) {
|
||||
if (interface()->reason() == ActivationCharacter) {
|
||||
context.setTriggerKind(CompletionParams::TriggerCharacter);
|
||||
QChar triggerCharacter = interface->characterAt(interface->position() - 1);
|
||||
QChar triggerCharacter = interface()->characterAt(interface()->position() - 1);
|
||||
if (!triggerCharacter.isNull())
|
||||
context.setTriggerCharacter(triggerCharacter);
|
||||
} else {
|
||||
@@ -352,13 +350,14 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(AssistInterfac
|
||||
CompletionParams params;
|
||||
int line;
|
||||
int column;
|
||||
if (!Utils::Text::convertPosition(interface->textDocument(), m_pos, &line, &column))
|
||||
if (!Utils::Text::convertPosition(interface()->textDocument(), m_pos, &line, &column))
|
||||
return nullptr;
|
||||
--line; // line is 0 based in the protocol
|
||||
--column; // column is 0 based in the protocol
|
||||
params.setPosition({line, column});
|
||||
params.setContext(context);
|
||||
params.setTextDocument(TextDocumentIdentifier(DocumentUri::fromFilePath(interface->filePath())));
|
||||
params.setTextDocument(
|
||||
TextDocumentIdentifier(DocumentUri::fromFilePath(interface()->filePath())));
|
||||
if (const int limit = m_client->completionResultsLimit(); limit >= 0)
|
||||
params.setLimit(limit);
|
||||
CompletionRequest completionRequest(params);
|
||||
@@ -368,10 +367,10 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(AssistInterfac
|
||||
m_client->sendMessage(completionRequest);
|
||||
m_client->addAssistProcessor(this);
|
||||
m_currentRequest = completionRequest.id();
|
||||
m_filePath = interface->filePath();
|
||||
m_filePath = interface()->filePath();
|
||||
qCDebug(LOGLSPCOMPLETION) << QTime::currentTime()
|
||||
<< " : request completions at " << m_pos
|
||||
<< " by " << assistReasonString(interface->reason());
|
||||
<< " by " << assistReasonString(interface()->reason());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@@ -59,7 +59,7 @@ class LANGUAGECLIENT_EXPORT LanguageClientCompletionAssistProcessor
|
||||
public:
|
||||
LanguageClientCompletionAssistProcessor(Client *client, const QString &snippetsGroup);
|
||||
~LanguageClientCompletionAssistProcessor() override;
|
||||
TextEditor::IAssistProposal *perform(TextEditor::AssistInterface *interface) override;
|
||||
TextEditor::IAssistProposal *perform() override;
|
||||
bool running() override;
|
||||
bool needsRestart() const override { return true; }
|
||||
void cancel() override;
|
||||
@@ -74,7 +74,6 @@ protected:
|
||||
private:
|
||||
void handleCompletionResponse(const LanguageServerProtocol::CompletionRequest::Response &response);
|
||||
|
||||
QScopedPointer<const TextEditor::AssistInterface> m_assistInterface;
|
||||
Utils::FilePath m_filePath;
|
||||
QPointer<Client> m_client;
|
||||
std::optional<LanguageServerProtocol::MessageId> m_currentRequest;
|
||||
|
@@ -66,14 +66,13 @@ FunctionHintProcessor::FunctionHintProcessor(Client *client)
|
||||
: m_client(client)
|
||||
{}
|
||||
|
||||
IAssistProposal *FunctionHintProcessor::perform(AssistInterface *interface)
|
||||
IAssistProposal *FunctionHintProcessor::perform()
|
||||
{
|
||||
const QScopedPointer<const AssistInterface> deleter(interface);
|
||||
QTC_ASSERT(m_client, return nullptr);
|
||||
m_pos = interface->position();
|
||||
QTextCursor cursor(interface->textDocument());
|
||||
m_pos = interface()->position();
|
||||
QTextCursor cursor(interface()->textDocument());
|
||||
cursor.setPosition(m_pos);
|
||||
auto uri = DocumentUri::fromFilePath(interface->filePath());
|
||||
auto uri = DocumentUri::fromFilePath(interface()->filePath());
|
||||
SignatureHelpRequest request((TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(cursor))));
|
||||
request.setResponseCallback([this](auto response) { this->handleSignatureResponse(response); });
|
||||
m_client->addAssistProcessor(this);
|
||||
@@ -118,7 +117,7 @@ FunctionHintAssistProvider::FunctionHintAssistProvider(Client *client)
|
||||
, m_client(client)
|
||||
{}
|
||||
|
||||
TextEditor::IAssistProcessor *FunctionHintAssistProvider::createProcessor(
|
||||
IAssistProcessor *FunctionHintAssistProvider::createProcessor(
|
||||
const AssistInterface *) const
|
||||
{
|
||||
return new FunctionHintProcessor(m_client);
|
||||
|
@@ -44,7 +44,7 @@ class LANGUAGECLIENT_EXPORT FunctionHintProcessor : public TextEditor::IAssistPr
|
||||
{
|
||||
public:
|
||||
explicit FunctionHintProcessor(Client *client);
|
||||
TextEditor::IAssistProposal *perform(TextEditor::AssistInterface *interface) override;
|
||||
TextEditor::IAssistProposal *perform() override;
|
||||
bool running() override { return m_currentRequest.has_value(); }
|
||||
bool needsRestart() const override { return true; }
|
||||
void cancel() override;
|
||||
|
@@ -45,13 +45,11 @@ void CommandQuickFixOperation::perform()
|
||||
m_client->executeCommand(m_command);
|
||||
}
|
||||
|
||||
IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(AssistInterface *interface)
|
||||
IAssistProposal *LanguageClientQuickFixAssistProcessor::perform()
|
||||
{
|
||||
m_assistInterface = QSharedPointer<const AssistInterface>(interface);
|
||||
|
||||
CodeActionParams params;
|
||||
params.setContext({});
|
||||
QTextCursor cursor = interface->cursor();
|
||||
QTextCursor cursor = interface()->cursor();
|
||||
if (!cursor.hasSelection()) {
|
||||
if (cursor.atBlockEnd() || cursor.atBlockStart())
|
||||
cursor.select(QTextCursor::LineUnderCursor);
|
||||
@@ -62,7 +60,7 @@ IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(AssistInterface
|
||||
cursor.select(QTextCursor::LineUnderCursor);
|
||||
Range range(cursor);
|
||||
params.setRange(range);
|
||||
auto uri = DocumentUri::fromFilePath(interface->filePath());
|
||||
auto uri = DocumentUri::fromFilePath(interface()->filePath());
|
||||
params.setTextDocument(TextDocumentIdentifier(uri));
|
||||
CodeActionParams::CodeActionContext context;
|
||||
context.setDiagnostics(m_client->diagnosticsAt(uri, cursor));
|
||||
@@ -110,7 +108,7 @@ GenericProposal *LanguageClientQuickFixAssistProcessor::handleCodeActionResult(c
|
||||
else if (auto command = std::get_if<Command>(&item))
|
||||
ops << new CommandQuickFixOperation(*command, m_client);
|
||||
}
|
||||
return GenericProposal::createProposal(m_assistInterface.data(), ops);
|
||||
return GenericProposal::createProposal(interface(), ops);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -63,7 +63,7 @@ class LANGUAGECLIENT_EXPORT LanguageClientQuickFixAssistProcessor
|
||||
public:
|
||||
explicit LanguageClientQuickFixAssistProcessor(Client *client) : m_client(client) {}
|
||||
bool running() override { return m_currentRequest.has_value(); }
|
||||
TextEditor::IAssistProposal *perform(TextEditor::AssistInterface *interface) override;
|
||||
TextEditor::IAssistProposal *perform() override;
|
||||
void cancel() override;
|
||||
|
||||
protected:
|
||||
@@ -76,7 +76,6 @@ private:
|
||||
virtual TextEditor::GenericProposal *handleCodeActionResult(
|
||||
const LanguageServerProtocol::CodeActionResult &result);
|
||||
|
||||
QSharedPointer<const TextEditor::AssistInterface> m_assistInterface;
|
||||
Client *m_client = nullptr; // not owned
|
||||
std::optional<LanguageServerProtocol::MessageId> m_currentRequest;
|
||||
};
|
||||
|
@@ -43,25 +43,24 @@ class NimCompletionAssistProcessor : public QObject, public TextEditor::IAssistP
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TextEditor::IAssistProposal *perform(TextEditor::AssistInterface *interface) final
|
||||
TextEditor::IAssistProposal *perform() final
|
||||
{
|
||||
QTC_ASSERT(this->thread() == qApp->thread(), return nullptr);
|
||||
|
||||
if (interface->reason() == IdleEditor && !acceptsIdleEditor(interface))
|
||||
if (interface()->reason() == IdleEditor && !acceptsIdleEditor(interface()))
|
||||
return nullptr;
|
||||
|
||||
Suggest::NimSuggest *suggest = nimSuggestInstance(interface);
|
||||
Suggest::NimSuggest *suggest = nimSuggestInstance(interface());
|
||||
QTC_ASSERT(suggest, return nullptr);
|
||||
|
||||
if (suggest->executablePath().isEmpty() || suggest->projectFile().isEmpty())
|
||||
return nullptr;
|
||||
|
||||
if (!suggest->isReady()) {
|
||||
m_interface = interface;
|
||||
QObject::connect(suggest, &Suggest::NimSuggest::readyChanged, this,
|
||||
[this, suggest](bool ready) { onNimSuggestReady(suggest, ready); });
|
||||
} else {
|
||||
doPerform(interface, suggest);
|
||||
doPerform(interface(), suggest);
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
@@ -76,13 +75,13 @@ public:
|
||||
private:
|
||||
void onNimSuggestReady(Suggest::NimSuggest *suggest, bool ready)
|
||||
{
|
||||
QTC_ASSERT(m_interface, return);
|
||||
QTC_ASSERT(interface(), return);
|
||||
|
||||
if (!ready) {
|
||||
m_running = false;
|
||||
setAsyncProposalAvailable(nullptr);
|
||||
} else {
|
||||
doPerform(m_interface, suggest);
|
||||
doPerform(interface(), suggest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +230,6 @@ private:
|
||||
std::weak_ptr<Suggest::NimSuggest> m_suggest;
|
||||
std::shared_ptr<Suggest::NimSuggestClientRequest> m_request;
|
||||
std::unique_ptr<QTemporaryFile> m_dirtyFile;
|
||||
const TextEditor::AssistInterface *m_interface = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -76,10 +76,10 @@ bool BindingEditorWidget::event(QEvent *event)
|
||||
return QmlJSEditor::QmlJSEditorWidget::event(event);
|
||||
}
|
||||
|
||||
TextEditor::AssistInterface *BindingEditorWidget::createAssistInterface(
|
||||
std::unique_ptr<TextEditor::AssistInterface> BindingEditorWidget::createAssistInterface(
|
||||
[[maybe_unused]] TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const
|
||||
{
|
||||
return new QmlJSEditor::QmlJSCompletionAssistInterface(
|
||||
return std::make_unique<QmlJSEditor::QmlJSCompletionAssistInterface>(
|
||||
textCursor(), Utils::FilePath(),
|
||||
assistReason, qmljsdocument->semanticInfo());
|
||||
}
|
||||
|
@@ -29,8 +29,8 @@ public:
|
||||
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
TextEditor::AssistInterface *createAssistInterface(TextEditor::AssistKind assistKind,
|
||||
TextEditor::AssistReason assistReason) const override;
|
||||
std::unique_ptr<TextEditor::AssistInterface> createAssistInterface(
|
||||
TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const override;
|
||||
|
||||
signals:
|
||||
void returnKeyClicked();
|
||||
|
@@ -315,14 +315,9 @@ QStringList qmlJSAutoComplete(QTextDocument *textDocument,
|
||||
QmlJSCompletionAssistProcessor processor;
|
||||
QTextCursor cursor(textDocument);
|
||||
cursor.setPosition(position);
|
||||
QScopedPointer<IAssistProposal> proposal(processor.perform( /* The processor takes ownership. */
|
||||
new QmlJSCompletionAssistInterface(
|
||||
cursor,
|
||||
fileName,
|
||||
reason,
|
||||
info)));
|
||||
|
||||
if (proposal) {
|
||||
std::unique_ptr<QmlJSCompletionAssistInterface>
|
||||
interface = std::make_unique<QmlJSCompletionAssistInterface>(cursor, fileName, reason, info);
|
||||
if (QScopedPointer<IAssistProposal> proposal{processor.start(std::move(interface))}) {
|
||||
GenericProposalModelPtr model = proposal->model().staticCast<GenericProposalModel>();
|
||||
|
||||
int basePosition = proposal->basePosition();
|
||||
@@ -525,21 +520,19 @@ IAssistProposal *QmlJSCompletionAssistProcessor::createHintProposal(
|
||||
return new FunctionHintProposal(m_startPosition, model);
|
||||
}
|
||||
|
||||
IAssistProposal *QmlJSCompletionAssistProcessor::performAsync(AssistInterface *assistInterface)
|
||||
IAssistProposal *QmlJSCompletionAssistProcessor::performAsync()
|
||||
{
|
||||
m_interface.reset(static_cast<const QmlJSCompletionAssistInterface *>(assistInterface));
|
||||
|
||||
if (assistInterface->reason() == IdleEditor && !acceptsIdleEditor())
|
||||
if (interface()->reason() == IdleEditor && !acceptsIdleEditor())
|
||||
return nullptr;
|
||||
|
||||
m_startPosition = assistInterface->position();
|
||||
while (isIdentifierChar(m_interface->textDocument()->characterAt(m_startPosition - 1), false, false))
|
||||
m_startPosition = interface()->position();
|
||||
while (isIdentifierChar(interface()->textDocument()->characterAt(m_startPosition - 1), false, false))
|
||||
--m_startPosition;
|
||||
const bool onIdentifier = m_startPosition != assistInterface->position();
|
||||
const bool onIdentifier = m_startPosition != interface()->position();
|
||||
|
||||
m_completions.clear();
|
||||
|
||||
auto qmlInterface = static_cast<const QmlJSCompletionAssistInterface *>(assistInterface);
|
||||
auto qmlInterface = static_cast<const QmlJSCompletionAssistInterface *>(interface());
|
||||
const SemanticInfo &semanticInfo = qmlInterface->semanticInfo();
|
||||
if (!semanticInfo.isValid())
|
||||
return nullptr;
|
||||
@@ -547,10 +540,10 @@ IAssistProposal *QmlJSCompletionAssistProcessor::performAsync(AssistInterface *a
|
||||
const Document::Ptr document = semanticInfo.document;
|
||||
|
||||
bool isQmlFile = false;
|
||||
if (m_interface->filePath().endsWith(".qml"))
|
||||
if (interface()->filePath().endsWith(".qml"))
|
||||
isQmlFile = true;
|
||||
|
||||
const QList<AST::Node *> path = semanticInfo.rangePath(m_interface->position());
|
||||
const QList<AST::Node *> path = semanticInfo.rangePath(interface()->position());
|
||||
const ContextPtr &context = semanticInfo.context;
|
||||
const ScopeChain &scopeChain = semanticInfo.scopeChain(path);
|
||||
|
||||
@@ -561,7 +554,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::performAsync(AssistInterface *a
|
||||
// a +b<complete> -> '+'
|
||||
QChar completionOperator;
|
||||
if (m_startPosition > 0)
|
||||
completionOperator = m_interface->textDocument()->characterAt(m_startPosition - 1);
|
||||
completionOperator = interface()->textDocument()->characterAt(m_startPosition - 1);
|
||||
|
||||
QTextCursor startPositionCursor(qmlInterface->textDocument());
|
||||
startPositionCursor.setPosition(m_startPosition);
|
||||
@@ -709,7 +702,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::performAsync(AssistInterface *a
|
||||
}
|
||||
} else if (value
|
||||
&& completionOperator == QLatin1Char('(')
|
||||
&& m_startPosition == m_interface->position()) {
|
||||
&& m_startPosition == interface()->position()) {
|
||||
// function completion
|
||||
if (const FunctionValue *f = value->asFunctionValue()) {
|
||||
QString functionName = expressionUnderCursor.text();
|
||||
@@ -733,7 +726,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::performAsync(AssistInterface *a
|
||||
}
|
||||
|
||||
// global completion
|
||||
if (onIdentifier || assistInterface->reason() == ExplicitlyInvoked) {
|
||||
if (onIdentifier || interface()->reason() == ExplicitlyInvoked) {
|
||||
|
||||
bool doGlobalCompletion = true;
|
||||
bool doQmlKeywordCompletion = true;
|
||||
@@ -845,14 +838,14 @@ IAssistProposal *QmlJSCompletionAssistProcessor::performAsync(AssistInterface *a
|
||||
|
||||
bool QmlJSCompletionAssistProcessor::acceptsIdleEditor() const
|
||||
{
|
||||
const int cursorPos = m_interface->position();
|
||||
const int cursorPos = interface()->position();
|
||||
|
||||
bool maybeAccept = false;
|
||||
const QChar &charBeforeCursor = m_interface->textDocument()->characterAt(cursorPos - 1);
|
||||
const QChar &charBeforeCursor = interface()->textDocument()->characterAt(cursorPos - 1);
|
||||
if (isActivationChar(charBeforeCursor)) {
|
||||
maybeAccept = true;
|
||||
} else {
|
||||
const QChar &charUnderCursor = m_interface->textDocument()->characterAt(cursorPos);
|
||||
const QChar &charUnderCursor = interface()->textDocument()->characterAt(cursorPos);
|
||||
if (isValidIdentifierChar(charUnderCursor))
|
||||
return false;
|
||||
if (isIdentifierChar(charBeforeCursor)
|
||||
@@ -863,12 +856,12 @@ bool QmlJSCompletionAssistProcessor::acceptsIdleEditor() const
|
||||
|
||||
int startPos = cursorPos - 1;
|
||||
for (; startPos != -1; --startPos) {
|
||||
if (!isIdentifierChar(m_interface->textDocument()->characterAt(startPos)))
|
||||
if (!isIdentifierChar(interface()->textDocument()->characterAt(startPos)))
|
||||
break;
|
||||
}
|
||||
++startPos;
|
||||
|
||||
const QString &word = m_interface->textAt(startPos, cursorPos - startPos);
|
||||
const QString &word = interface()->textAt(startPos, cursorPos - startPos);
|
||||
if (word.length() >= TextEditorSettings::completionSettings().m_characterThreshold
|
||||
&& isIdentifierChar(word.at(0), true)) {
|
||||
for (int i = 1; i < word.length(); ++i) {
|
||||
@@ -881,15 +874,15 @@ bool QmlJSCompletionAssistProcessor::acceptsIdleEditor() const
|
||||
}
|
||||
|
||||
if (maybeAccept) {
|
||||
QTextCursor tc(m_interface->textDocument());
|
||||
tc.setPosition(m_interface->position());
|
||||
QTextCursor tc(interface()->textDocument());
|
||||
tc.setPosition(interface()->position());
|
||||
const QTextBlock &block = tc.block();
|
||||
const QString &blockText = block.text();
|
||||
const int blockState = qMax(0, block.previous().userState()) & 0xff;
|
||||
|
||||
Scanner scanner;
|
||||
const QList<Token> tokens = scanner(blockText, blockState);
|
||||
const int column = block.position() - m_interface->position();
|
||||
const int column = block.position() - interface()->position();
|
||||
for (const Token &tk : tokens) {
|
||||
if (column >= tk.begin() && column <= tk.end()) {
|
||||
if (charBeforeCursor == QLatin1Char('/') && tk.is(Token::String))
|
||||
|
@@ -57,7 +57,7 @@ public:
|
||||
QmlJSCompletionAssistProcessor();
|
||||
~QmlJSCompletionAssistProcessor() override;
|
||||
|
||||
TextEditor::IAssistProposal *performAsync(TextEditor::AssistInterface *interface) override;
|
||||
TextEditor::IAssistProposal *performAsync() override;
|
||||
|
||||
private:
|
||||
TextEditor::IAssistProposal *createContentProposal() const;
|
||||
@@ -73,7 +73,6 @@ private:
|
||||
const QStringList &patterns = QStringList());
|
||||
|
||||
int m_startPosition;
|
||||
QScopedPointer<const QmlJSCompletionAssistInterface> m_interface;
|
||||
QList<TextEditor::AssistProposalItemInterface *> m_completions;
|
||||
TextEditor::SnippetAssistCollector m_snippetCollector;
|
||||
};
|
||||
|
@@ -857,11 +857,11 @@ void QmlJSEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
||||
QMenu *refactoringMenu = new QMenu(Tr::tr("Refactoring"), menu);
|
||||
|
||||
if (!m_qmlJsEditorDocument->isSemanticInfoOutdated()) {
|
||||
AssistInterface *interface = createAssistInterface(QuickFix, ExplicitlyInvoked);
|
||||
std::unique_ptr<AssistInterface> interface = createAssistInterface(QuickFix, ExplicitlyInvoked);
|
||||
if (interface) {
|
||||
QScopedPointer<IAssistProcessor> processor(
|
||||
Internal::QmlJSEditorPlugin::quickFixAssistProvider()->createProcessor(interface));
|
||||
QScopedPointer<IAssistProposal> proposal(processor->perform(interface));
|
||||
Internal::QmlJSEditorPlugin::quickFixAssistProvider()->createProcessor(interface.get()));
|
||||
QScopedPointer<IAssistProposal> proposal(processor->start(std::move(interface)));
|
||||
if (!proposal.isNull()) {
|
||||
GenericProposalModelPtr model = proposal->model().staticCast<GenericProposalModel>();
|
||||
for (int index = 0; index < model->size(); ++index) {
|
||||
@@ -999,15 +999,16 @@ bool QmlJSEditorWidget::hideContextPane()
|
||||
return b;
|
||||
}
|
||||
|
||||
AssistInterface *QmlJSEditorWidget::createAssistInterface(
|
||||
std::unique_ptr<AssistInterface> QmlJSEditorWidget::createAssistInterface(
|
||||
AssistKind assistKind,
|
||||
AssistReason reason) const
|
||||
{
|
||||
if (assistKind == Completion) {
|
||||
return new QmlJSCompletionAssistInterface(textCursor(), textDocument()->filePath(),
|
||||
reason, m_qmlJsEditorDocument->semanticInfo());
|
||||
return std::make_unique<QmlJSCompletionAssistInterface>(
|
||||
textCursor(), textDocument()->filePath(), reason, m_qmlJsEditorDocument->semanticInfo());
|
||||
} else if (assistKind == QuickFix) {
|
||||
return new Internal::QmlJSQuickFixAssistInterface(const_cast<QmlJSEditorWidget *>(this), reason);
|
||||
return std::make_unique<Internal::QmlJSQuickFixAssistInterface>(
|
||||
const_cast<QmlJSEditorWidget *>(this), reason);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -44,8 +44,8 @@ public:
|
||||
void updateOutlineIndexNow();
|
||||
bool isOutlineCursorChangesBlocked();
|
||||
|
||||
TextEditor::AssistInterface *createAssistInterface(TextEditor::AssistKind assistKind,
|
||||
TextEditor::AssistReason reason) const override;
|
||||
std::unique_ptr<TextEditor::AssistInterface> createAssistInterface(
|
||||
TextEditor::AssistKind assistKind, TextEditor::AssistReason reason) const override;
|
||||
|
||||
void inspectElementUnderCursor() const;
|
||||
|
||||
|
@@ -48,9 +48,9 @@ QmlJSRefactoringFilePtr QmlJSQuickFixAssistInterface::currentFile() const
|
||||
// ---------------------------
|
||||
class QmlJSQuickFixAssistProcessor : public IAssistProcessor
|
||||
{
|
||||
IAssistProposal *perform(AssistInterface *interface) override
|
||||
IAssistProposal *perform() override
|
||||
{
|
||||
return GenericProposal::createProposal(interface, findQmlJSQuickFixes(interface));
|
||||
return GenericProposal::createProposal(interface(), findQmlJSQuickFixes(interface()));
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -63,12 +63,8 @@ private:
|
||||
class ClipboardAssistProcessor: public IAssistProcessor
|
||||
{
|
||||
public:
|
||||
IAssistProposal *perform(AssistInterface *interface) override
|
||||
IAssistProposal *perform() override
|
||||
{
|
||||
if (!interface)
|
||||
return nullptr;
|
||||
const QScopedPointer<const AssistInterface> AssistInterface(interface);
|
||||
|
||||
QIcon icon = QIcon::fromTheme(QLatin1String("edit-paste"), Utils::Icons::PASTE.icon()).pixmap(16);
|
||||
CircularClipboard * clipboard = CircularClipboard::instance();
|
||||
QList<AssistProposalItemInterface *> items;
|
||||
@@ -82,7 +78,7 @@ public:
|
||||
items.append(item);
|
||||
}
|
||||
|
||||
return new GenericProposal(interface->position(), items);
|
||||
return new GenericProposal(interface()->position(), items);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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.*/
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -8534,11 +8534,11 @@ void TextEditorWidget::invokeAssist(AssistKind kind, IAssistProvider *provider)
|
||||
setOverwriteMode(previousMode);
|
||||
}
|
||||
|
||||
AssistInterface *TextEditorWidget::createAssistInterface(AssistKind kind,
|
||||
AssistReason reason) const
|
||||
std::unique_ptr<AssistInterface> TextEditorWidget::createAssistInterface(AssistKind kind,
|
||||
AssistReason reason) const
|
||||
{
|
||||
Q_UNUSED(kind)
|
||||
return new AssistInterface(textCursor(), d->m_document->filePath(), reason);
|
||||
return std::make_unique<AssistInterface>(textCursor(), d->m_document->filePath(), reason);
|
||||
}
|
||||
|
||||
QString TextEditorWidget::foldReplacementText(const QTextBlock &) const
|
||||
|
@@ -23,7 +23,9 @@
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QToolBar;
|
||||
@@ -259,8 +261,8 @@ public:
|
||||
|
||||
void invokeAssist(AssistKind assistKind, IAssistProvider *provider = nullptr);
|
||||
|
||||
virtual TextEditor::AssistInterface *createAssistInterface(AssistKind assistKind,
|
||||
AssistReason assistReason) const;
|
||||
virtual std::unique_ptr<AssistInterface> createAssistInterface(AssistKind assistKind,
|
||||
AssistReason assistReason) const;
|
||||
static QMimeData *duplicateMimeData(const QMimeData *source);
|
||||
|
||||
static QString msgTextTooLarge(quint64 size);
|
||||
|
Reference in New Issue
Block a user