diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 96034c1052c..e1f0319ff83 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -751,9 +751,8 @@ ClangdClient::ClangdCompletionAssistProcessor::generateCompletionItems( return itemGenerator(items); const QString content = doc->toPlainText(); const bool requiresSignal = CppEditor::CppModelManager::instance() - ->positionRequiresSignal(filePath().toString(), - content.toUtf8(), - pos); + ->getSignalSlotType(filePath().toString(), content.toUtf8(), pos) + == CppEditor::SignalSlotType::NewStyleSignal; if (requiresSignal) return itemGenerator(Utils::filtered(items, criterion)); return itemGenerator(items); @@ -2237,6 +2236,10 @@ IAssistProcessor *ClangdClient::ClangdCompletionAssistProvider::createProcessor( contextAnalyzer.positionEndOfExpression(), contextAnalyzer.completionOperator(), CustomAssistMode::Preprocessor); + case ClangCompletionContextAnalyzer::CompleteSignal: + case ClangCompletionContextAnalyzer::CompleteSlot: + if (!interface->isBaseObject()) + return CppEditor::getCppCompletionAssistProcessor(); default: break; } diff --git a/src/plugins/cppeditor/cppcompletionassist.h b/src/plugins/cppeditor/cppcompletionassist.h index adcdcaa798a..674f6bf4a78 100644 --- a/src/plugins/cppeditor/cppcompletionassist.h +++ b/src/plugins/cppeditor/cppcompletionassist.h @@ -194,6 +194,7 @@ public: { getCppSpecifics(); return m_headerPaths; } CPlusPlus::LanguageFeatures languageFeatures() const { getCppSpecifics(); return m_languageFeatures; } + bool isBaseObject() const override { return false; } private: void getCppSpecifics() const; diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index 1129e107444..f797a59ffdd 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -1163,22 +1163,43 @@ void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo, updateFunctionDeclDefLink(); } +bool CppEditorWidget::isOldStyleSignalOrSlot() const +{ + QTextCursor tc(textCursor()); + const QString content = textDocument()->plainText(); + + return CppEditor::CppModelManager::instance() + ->getSignalSlotType(textDocument()->filePath().toString(), + content.toUtf8(), + tc.position()) + == CppEditor::SignalSlotType::OldStyleSignal; +} + AssistInterface *CppEditorWidget::createAssistInterface(AssistKind kind, AssistReason reason) const { if (kind == Completion || kind == FunctionHint) { CppCompletionAssistProvider * const cap = kind == Completion ? qobject_cast(cppEditorDocument()->completionAssistProvider()) : qobject_cast(cppEditorDocument()->functionHintAssistProvider()); - if (cap) { + + auto getFeatures = [this]() { LanguageFeatures features = LanguageFeatures::defaultFeatures(); if (Document::Ptr doc = d->m_lastSemanticInfo.doc) features = doc->languageFeatures(); features.objCEnabled |= cppEditorDocument()->isObjCEnabled(); + return features; + }; + + if (cap) return cap->createAssistInterface(textDocument()->filePath(), this, - features, + getFeatures(), reason); - } else { + else { + if (isOldStyleSignalOrSlot()) + return CppModelManager::instance() + ->completionAssistProvider() + ->createAssistInterface(textDocument()->filePath(), this, getFeatures(), reason); return TextEditorWidget::createAssistInterface(kind, reason); } } else if (kind == QuickFix) { diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h index 5b27c4e4451..5f76a40e69b 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -146,6 +146,7 @@ private: void finalizeInitializationAfterDuplication(TextEditorWidget *other) override; unsigned documentRevision() const; + bool isOldStyleSignalOrSlot() const; QMenu *createRefactorMenu(QWidget *parent) const; diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index 39bcc5118c0..4a7297815ac 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -338,11 +339,29 @@ void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend) inNextSplit); } -bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByteArray &content, - int position) const +int argumentPositionOf(const AST *last, const CallAST *callAst) +{ + if (!callAst || !callAst->expression_list) + return false; + + int num = 0; + for (ExpressionListAST *it = callAst->expression_list; it; it = it->next) { + ++num; + const ExpressionAST *const arg = it->value; + if (arg->firstToken() <= last->firstToken() + && arg->lastToken() >= last->lastToken()) { + return num; + } + } + return 0; +} + +SignalSlotType CppModelManager::getSignalSlotType(const QString &filePath, + const QByteArray &content, + int position) const { if (content.isEmpty()) - return false; + return SignalSlotType::None; // Insert a dummy prefix if we don't have a real one. Otherwise the AST path will not contain // anything after the CallAST. @@ -360,26 +379,17 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt // Are we at the second argument of a function call? const QList path = ASTPath(document)(cursor); - if (path.isEmpty() || !path.last()->asSimpleName()) - return false; + if (path.isEmpty()) + return SignalSlotType::None; const CallAST *callAst = nullptr; for (auto it = path.crbegin(); it != path.crend(); ++it) { if ((callAst = (*it)->asCall())) break; } - if (!callAst) - return false; - if (!callAst->expression_list || !callAst->expression_list->next) - return false; - const ExpressionAST * const secondArg = callAst->expression_list->next->value; - if (secondArg->firstToken() > path.last()->firstToken() - || secondArg->lastToken() < path.last()->lastToken()) { - return false; - } // Is the function called "connect" or "disconnect"? - if (!callAst->base_expression) - return false; + if (!callAst || !callAst->base_expression) + return SignalSlotType::None; Scope *scope = document->globalNamespace(); for (auto it = path.crbegin(); it != path.crend(); ++it) { if (const CompoundStatementAST * const stmtAst = (*it)->asCompoundStatement()) { @@ -398,7 +408,7 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt exprType.init(document, snapshot); const QList typeMatches = exprType(ast->base_expression, document, scope); if (typeMatches.isEmpty()) - return false; + return SignalSlotType::None; const std::function getNamedType = [&getNamedType](const FullySpecifiedType &type ) -> const NamedType * { Type * const t = type.type(); @@ -414,22 +424,22 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt if (!namedType && typeMatches.first().declaration()) namedType = getNamedType(typeMatches.first().declaration()->type()); if (!namedType) - return false; + return SignalSlotType::None; const ClassOrNamespace * const result = context.lookupType(namedType->name(), scope); if (!result) - return false; + return SignalSlotType::None; scope = result->rootClass(); if (!scope) - return false; + return SignalSlotType::None; } if (!nameAst || !nameAst->name) - return false; + return SignalSlotType::None; const Identifier * const id = nameAst->name->identifier(); if (!id) - return false; + return SignalSlotType::None; const QString funcName = QString::fromUtf8(id->chars(), id->size()); if (funcName != "connect" && funcName != "disconnect") - return false; + return SignalSlotType::None; // Is the function a member function of QObject? const QList matches = context.lookup(nameAst->name, scope); @@ -440,11 +450,29 @@ bool CppModelManager::positionRequiresSignal(const QString &filePath, const QByt if (!klass || !klass->name()) continue; const Identifier * const classId = klass->name()->identifier(); - if (classId && QString::fromUtf8(classId->chars(), classId->size()) == "QObject") - return true; - } + if (classId && QString::fromUtf8(classId->chars(), classId->size()) == "QObject") { + QString expression; + LanguageFeatures features = LanguageFeatures::defaultFeatures(); + CPlusPlus::ExpressionUnderCursor expressionUnderCursor(features); + for (int i = cursor.position(); i > 0; --i) + if (textDocument.characterAt(i) == '(') { + cursor.setPosition(i); + break; + } - return false; + expression = expressionUnderCursor(cursor); + + const int argumentPosition = argumentPositionOf(path.last(), callAst); + if ((expression.endsWith(QLatin1String("SIGNAL")) + && (argumentPosition == 2 || argumentPosition == 4)) + || (expression.endsWith(QLatin1String("SLOT")) && argumentPosition == 4)) + return SignalSlotType::OldStyleSignal; + + if (argumentPosition == 2) + return SignalSlotType::NewStyleSignal; + } + } + return SignalSlotType::None; } FollowSymbolUnderCursor &CppModelManager::builtinFollowSymbol() diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h index efccd521190..e00dccbf33d 100644 --- a/src/plugins/cppeditor/cppmodelmanager.h +++ b/src/plugins/cppeditor/cppmodelmanager.h @@ -45,7 +45,11 @@ namespace Core { class IDocument; class IEditor; } -namespace CPlusPlus { class LookupContext; } +namespace CPlusPlus { +class AST; +class CallAST; +class LookupContext; +} // namespace CPlusPlus namespace ProjectExplorer { class Project; } namespace TextEditor { class BaseHoverHandler; @@ -75,6 +79,12 @@ class CppModelManagerPrivate; namespace Tests { class ModelManagerTestHelper; } +enum class SignalSlotType { + OldStyleSignal, + NewStyleSignal, + None +}; + class CPPEDITOR_EXPORT CppModelManager final : public CPlusPlus::CppModelManagerBase { Q_OBJECT @@ -154,8 +164,9 @@ public: QList references(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); - bool positionRequiresSignal(const QString &filePath, const QByteArray &content, - int position) const; + SignalSlotType getSignalSlotType(const QString &filePath, + const QByteArray &content, + int position) const; void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, const QString &replacement = QString()); diff --git a/src/plugins/cppeditor/cppquickfixassistant.h b/src/plugins/cppeditor/cppquickfixassistant.h index d6e556c7b19..0824b82cd1f 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.h +++ b/src/plugins/cppeditor/cppquickfixassistant.h @@ -56,6 +56,7 @@ public: bool isCursorOn(unsigned tokenIndex) const; bool isCursorOn(const CPlusPlus::AST *ast) const; + bool isBaseObject() const override { return false; } private: CppEditorWidget *m_editor; diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index ac0d6c7a59c..91794a18384 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -28,6 +28,7 @@ #include "clangdiagnosticconfigsmodel.h" #include "cppautocompleter.h" #include "cppcodemodelsettings.h" +#include "cppcompletionassist.h" #include "cppeditorconstants.h" #include "cppeditorplugin.h" #include "cpphighlighter.h" @@ -336,6 +337,11 @@ TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterf return Internal::quickFixOperations(interface); } +CppCompletionAssistProcessor *getCppCompletionAssistProcessor() +{ + return new Internal::InternalCppCompletionAssistProcessor(); +} + CppCodeModelSettings *codeModelSettings() { return Internal::CppEditorPlugin::instance()->codeModelSettings(); diff --git a/src/plugins/cppeditor/cpptoolsreuse.h b/src/plugins/cppeditor/cpptoolsreuse.h index c0284f98388..4743ec3377e 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.h +++ b/src/plugins/cppeditor/cpptoolsreuse.h @@ -55,6 +55,7 @@ namespace TextEditor { class AssistInterface; } namespace CppEditor { class CppRefactoringFile; class ProjectInfo; +class CppCompletionAssistProcessor; void CPPEDITOR_EXPORT moveCursorToEndOfIdentifier(QTextCursor *tc); void CPPEDITOR_EXPORT moveCursorToStartOfIdentifier(QTextCursor *tc); @@ -80,6 +81,8 @@ bool CPPEDITOR_EXPORT isInCommentOrString(const TextEditor::AssistInterface *int TextEditor::QuickFixOperations CPPEDITOR_EXPORT quickFixOperations(const TextEditor::AssistInterface *interface); +CppCompletionAssistProcessor CPPEDITOR_EXPORT *getCppCompletionAssistProcessor(); + enum class CacheUsage { ReadWrite, ReadOnly }; QString CPPEDITOR_EXPORT correspondingHeaderOrSource(const QString &fileName, bool *wasHeader = nullptr, diff --git a/src/plugins/glsleditor/glslcompletionassist.h b/src/plugins/glsleditor/glslcompletionassist.h index c54ffe6e368..e5fe1bed2d0 100644 --- a/src/plugins/glsleditor/glslcompletionassist.h +++ b/src/plugins/glsleditor/glslcompletionassist.h @@ -114,6 +114,7 @@ public: const QString &mimeType() const { return m_mimeType; } const Document::Ptr &glslDocument() const { return m_glslDoc; } + bool isBaseObject() const override { return false; } private: QString m_mimeType; diff --git a/src/plugins/qmljseditor/qmljsquickfixassist.h b/src/plugins/qmljseditor/qmljsquickfixassist.h index a5f39688eb8..1254f2510c0 100644 --- a/src/plugins/qmljseditor/qmljsquickfixassist.h +++ b/src/plugins/qmljseditor/qmljsquickfixassist.h @@ -45,6 +45,7 @@ public: const QmlJSTools::SemanticInfo &semanticInfo() const; QmlJSTools::QmlJSRefactoringFilePtr currentFile() const; + bool isBaseObject() const override { return false; } private: QmlJSTools::SemanticInfo m_semanticInfo; diff --git a/src/plugins/texteditor/codeassist/assistinterface.h b/src/plugins/texteditor/codeassist/assistinterface.h index a7081909d14..9c49603ceac 100644 --- a/src/plugins/texteditor/codeassist/assistinterface.h +++ b/src/plugins/texteditor/codeassist/assistinterface.h @@ -54,6 +54,7 @@ public: virtual void prepareForAsyncUse(); virtual void recreateTextDocument(); virtual AssistReason reason() const; + virtual bool isBaseObject() const { return true; } private: QTextDocument *m_textDocument;