diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index f21f256a06ee0765dc4e097db9e1b1e5df0bafd8..3ae4a1e730c4899456ca85b824c57c435cc65109 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -159,7 +159,7 @@ static bool isValidFileNameChar(const QChar &c) } CMakeEditorWidget::Link CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, - bool/* resolveTarget*/) + bool/* resolveTarget*/, bool /*inNextSplit*/) { Link link; diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.h b/src/plugins/cmakeprojectmanager/cmakeeditor.h index c35eebb26d987472243bb1dde864fa006da9abbf..b5102dabfbe4ea5718bd56cbdff69071375c1cac 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.h +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.h @@ -78,8 +78,7 @@ public: CMakeEditorFactory *factory() { return m_factory; } TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; } - Link findLinkAt(const QTextCursor &cursor, - bool resolveTarget = true); + Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true, bool inNextSplit = false); protected: TextEditor::BaseTextEditor *createEditor(); diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index bf323e1b9fbcff8b5a1d2124084ea49469f67616..4ec6cb4e5a9f2345fd574a5a5d5d2bcc37f1a680 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -511,6 +511,7 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent) , m_firstRenameChange(false) , m_objcEnabled(false) , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings()) + , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(this)) { qRegisterMetaType<SemanticInfo>("CppTools::SemanticInfo"); @@ -1239,14 +1240,14 @@ QString CPPEditorWidget::identifierUnderCursor(QTextCursor *macroCursor) return macroCursor->selectedText(); } -CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget) +CPPEditorWidget::Link CPPEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget, + bool inNextSplit) { if (!m_modelManager) return Link(); - FollowSymbolUnderCursor followSymbolUnderCursor(this, cursor, resolveTarget, - m_modelManager->snapshot(), m_lastSemanticInfo.doc, symbolFinder()); - return followSymbolUnderCursor.findLink(); + return m_followSymbolUnderCursor->findLink(cursor, resolveTarget, m_modelManager->snapshot(), + m_lastSemanticInfo.doc, symbolFinder(), inNextSplit); } unsigned CPPEditorWidget::editorRevision() const @@ -1720,6 +1721,8 @@ TextEditor::IAssistInterface *CPPEditorWidget::createAssistInterface( if (!semanticInfo().doc || isOutdated()) return 0; return new CppQuickFixAssistInterface(const_cast<CPPEditorWidget *>(this), reason); + } else { + return BaseTextEditorWidget::createAssistInterface(kind, reason); } return 0; } @@ -1812,6 +1815,11 @@ void CPPEditorWidget::updateContentsChangedSignal() this, SLOT(onContentsChanged(int,int,int))); } +FollowSymbolUnderCursor *CPPEditorWidget::followSymbolUnderCursorDelegate() +{ + return m_followSymbolUnderCursor.data(); +} + void CPPEditorWidget::abortDeclDefLink() { if (!m_declDefLink) diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 4714886f25d4f6c0a900ff97c7a5eb3750e9ac64..ba8f85db77f6df43419120f39216f11f1ef2d760 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -30,6 +30,7 @@ #ifndef CPPEDITOR_H #define CPPEDITOR_H +#include "cppfollowsymbolundercursor.h" #include "cppfunctiondecldeflink.h" #include <cpptools/commentssettings.h> @@ -132,6 +133,8 @@ public: void updateContentsChangedSignal(); + FollowSymbolUnderCursor *followSymbolUnderCursorDelegate(); // exposed for tests + Q_SIGNALS: void outlineModelIndexChanged(const QModelIndex &index); @@ -204,7 +207,7 @@ private: Q_SLOT void abortDeclDefLink(); - Link findLinkAt(const QTextCursor &, bool resolveTarget = true); + Link findLinkAt(const QTextCursor &, bool resolveTarget = true, bool inNextSplit = false); bool openCppEditorAt(const Link &, bool inNextSplit = false); QModelIndex indexForPosition(int line, int column, @@ -254,6 +257,8 @@ private: QSharedPointer<FunctionDeclDefLink> m_declDefLink; CppTools::CommentsSettings m_commentsSettings; + + QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor; }; } // namespace Internal diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 740a70d2b98cbd6a6c45f8a0bb6163da7b491144..e793b5d4296192f642ed01017efd5edbb995bb04 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -23,7 +23,8 @@ HEADERS += cppeditorplugin.h \ cppincludehierarchy.h \ cppincludehierarchymodel.h \ cppincludehierarchyitem.h \ - cppincludehierarchytreeview.h + cppincludehierarchytreeview.h \ + cppvirtualfunctionassistprovider.h SOURCES += cppeditorplugin.cpp \ cppautocompleter.cpp \ @@ -45,7 +46,8 @@ SOURCES += cppeditorplugin.cpp \ cppincludehierarchy.cpp \ cppincludehierarchymodel.cpp \ cppincludehierarchyitem.cpp \ - cppincludehierarchytreeview.cpp + cppincludehierarchytreeview.cpp \ + cppvirtualfunctionassistprovider.cpp RESOURCES += cppeditor.qrc diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index fd3da1fe8408e456229a6c9ebf6fc8a6b2e441a5..941600b6b24df5762e507dc81aa9c75c4bd8e5e7 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -60,6 +60,8 @@ QtcPlugin { "cppsnippetprovider.h", "cpptypehierarchy.cpp", "cpptypehierarchy.h", + "cppvirtualfunctionassistprovider.cpp", + "cppvirtualfunctionassistprovider.h", ] Group { diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index 7442d6692403312974605ee540e139c56f71b898..389ccd43e65ce09df8ee5d773ba8343b3ed9b8cf 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -128,6 +128,13 @@ private slots: void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_globalNamespace(); void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_namespace(); void test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_insideFunction(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_allOverrides(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides1(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides2(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnQualified(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDeclaration(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDefinition(); + void test_FollowSymbolUnderCursor_virtualFunctionCall_notOnNonPointerNonReference(); void test_doxygen_comments_qt_style(); void test_doxygen_comments_qt_style_continuation(); diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp index c7cc6dfa68f15499f5d2fdd1e70d31f4bc7e6749..fe7dacbbd8a29ff758ed99cea14e9be590ea33aa 100644 --- a/src/plugins/cppeditor/cppelementevaluator.cpp +++ b/src/plugins/cppeditor/cppelementevaluator.cpp @@ -272,10 +272,11 @@ CppMacro::CppMacro(const Macro ¯o) // CppDeclarableElement -CppDeclarableElement::CppDeclarableElement(Symbol *declaration) : CppElement() +CppDeclarableElement::CppDeclarableElement(Symbol *declaration) + : CppElement() + , declaration(declaration) + , icon(Icons().iconForSymbol(declaration)) { - icon = Icons().iconForSymbol(declaration); - Overview overview; overview.showArgumentNames = true; overview.showReturnTypes = true; @@ -309,6 +310,11 @@ CppClass::CppClass(Symbol *declaration) : CppDeclarableElement(declaration) tooltip = qualifiedName; } +bool CppClass::operator==(const CppClass &other) +{ + return this->declaration == other.declaration; +} + void CppClass::lookupBases(Symbol *declaration, const CPlusPlus::LookupContext &context) { typedef QPair<ClassOrNamespace *, CppClass *> Data; diff --git a/src/plugins/cppeditor/cppelementevaluator.h b/src/plugins/cppeditor/cppelementevaluator.h index 33baf0eac67fb7e695334d6713544fe1cdc4dfd5..498d58ba1f44f0a21f9ceec22afe368513bcf69f 100644 --- a/src/plugins/cppeditor/cppelementevaluator.h +++ b/src/plugins/cppeditor/cppelementevaluator.h @@ -135,6 +135,7 @@ public: explicit CppDeclarableElement(CPlusPlus::Symbol *declaration); public: + CPlusPlus::Symbol *declaration; QString name; QString qualifiedName; QString type; @@ -153,6 +154,8 @@ public: CppClass(); explicit CppClass(CPlusPlus::Symbol *declaration); + bool operator==(const CppClass &other); + void lookupBases(CPlusPlus::Symbol *declaration, const CPlusPlus::LookupContext &context); void lookupDerived(CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot); diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index 4446c14ac06fa8dc5e58e3b0e3655e9678677220..4b91d3ce82140b02037a1fc813409ac5c3f436c9 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -30,6 +30,7 @@ #include "cppfollowsymbolundercursor.h" #include "cppeditor.h" +#include "cppvirtualfunctionassistprovider.h" #include <cplusplus/ASTPath.h> #include <cplusplus/BackwardsScanner.h> @@ -54,6 +55,37 @@ typedef BaseTextEditorWidget::Link Link; namespace { +bool lookupVirtualFunctionOverrides(const QString &expression, Function *function, Scope *scope, + const Snapshot &snapshot) +{ + if (expression.isEmpty() || !function || !scope || scope->isClass() || snapshot.isEmpty()) + return false; + + bool result = false; + + Document::Ptr expressionDocument = documentForExpression(expression.toUtf8()); + if (ExpressionAST *expressionAST = extractExpressionAST(expressionDocument)) { + if (CallAST *callAST = expressionAST->asCall()) { + if (ExpressionAST *baseExpressionAST = callAST->base_expression) { + if (IdExpressionAST *idExpressionAST = baseExpressionAST->asIdExpression()) { + NameAST *name = idExpressionAST->name; + result = name && !name->asQualifiedName(); + } else if (MemberAccessAST *memberAccessAST = baseExpressionAST->asMemberAccess()) { + NameAST *name = memberAccessAST->member_name; + const bool nameIsQualified = name && name->asQualifiedName(); + + TranslationUnit *unit = expressionDocument->translationUnit(); + QTC_ASSERT(unit, return false); + const int tokenKind = unit->tokenKind(memberAccessAST->access_token); + result = tokenKind == T_ARROW && !nameIsQualified; + } + } + } + } + + return result && FunctionHelper::isVirtualFunction(function, snapshot); +} + Link findMacroLink_helper(const QByteArray &name, Document::Ptr doc, const Snapshot &snapshot, QSet<QString> *processed) { @@ -138,48 +170,139 @@ inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbo return result; } +CPPEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor, + CPPEditorWidget *widget, CPlusPlus::Snapshot snapshot, const CPlusPlus::Document::Ptr &document, + SymbolFinder *symbolFinder) +{ + snapshot.insert(document); + + Link result; + + QList<AST *> path = ASTPath(document)(cursor); + + if (path.size() < 5) + return result; + + NameAST *name = path.last()->asName(); + if (!name) + return result; + + if (QualifiedNameAST *qName = path.at(path.size() - 2)->asQualifiedName()) { + // TODO: check which part of the qualified name we're on + if (qName->unqualified_name != name) + return result; + } + + for (int i = path.size() - 1; i != -1; --i) { + AST *node = path.at(i); + + if (node->asParameterDeclaration() != 0) + return result; + } + + AST *declParent = 0; + DeclaratorAST *decl = 0; + for (int i = path.size() - 2; i > 0; --i) { + if ((decl = path.at(i)->asDeclarator()) != 0) { + declParent = path.at(i - 1); + break; + } + } + if (!decl || !declParent) + return result; + if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value) + return result; + FunctionDeclaratorAST *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator(); + if (!funcDecl) + return result; + + Symbol *target = 0; + if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) { + QList<Declaration *> candidates = + symbolFinder->findMatchingDeclaration(LookupContext(document, snapshot), + funDef->symbol); + if (!candidates.isEmpty()) // TODO: improve disambiguation + target = candidates.first(); + } else if (declParent->asSimpleDeclaration()) { + target = symbolFinder->findMatchingDefinition(funcDecl->symbol, snapshot); + } + + if (target) { + result = widget->linkToSymbol(target); + + unsigned startLine, startColumn, endLine, endColumn; + document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine, + &startColumn); + document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine, + &endColumn); + + QTextDocument *textDocument = cursor.document(); + result.linkTextStart = + textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1; + result.linkTextEnd = + textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1; + } + + return result; +} + +Symbol *findDefinition(Symbol *symbol, const Snapshot &snapshot, SymbolFinder *symbolFinder) +{ + if (symbol->isFunction()) + return 0; // symbol is a function definition. + + else if (!symbol->type()->isFunctionType()) + return 0; // not a function declaration + + return symbolFinder->findMatchingDefinition(symbol, snapshot); +} + } // anonymous namespace -FollowSymbolUnderCursor::FollowSymbolUnderCursor(CPPEditorWidget *widget, const QTextCursor &cursor, - bool resolveTarget, const Snapshot &snapshot, const Document::Ptr &documentFromSemanticInfo, - CppTools::SymbolFinder *symbolFinder) +FollowSymbolUnderCursor::FollowSymbolUnderCursor(CPPEditorWidget *widget) : m_widget(widget) - , m_cursor(cursor) - , m_resolveTarget(resolveTarget) - , m_snapshot(snapshot) - , m_document(documentFromSemanticInfo) - , m_symbolFinder(symbolFinder) + , m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider) +{ +} + +FollowSymbolUnderCursor::~FollowSymbolUnderCursor() { + delete m_virtualFunctionAssistProvider; } -BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() +BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink(const QTextCursor &cursor, + bool resolveTarget, const Snapshot &theSnapshot, const Document::Ptr &documentFromSemanticInfo, + SymbolFinder *symbolFinder, bool inNextSplit) { Link link; + Snapshot snapshot = theSnapshot; + // Move to end of identifier - QTextCursor tc = m_cursor; + QTextCursor tc = cursor; QChar ch = m_widget->document()->characterAt(tc.position()); while (ch.isLetterOrNumber() || ch == QLatin1Char('_')) { tc.movePosition(QTextCursor::NextCharacter); ch = m_widget->document()->characterAt(tc.position()); } - // Try to match decl/def. For this we need the semantic doc with the AST. - if (m_document - && m_document->translationUnit() - && m_document->translationUnit()->ast()) { + // Try to macth decl/def. For this we need the semantic doc with the AST. + if (documentFromSemanticInfo + && documentFromSemanticInfo->translationUnit() + && documentFromSemanticInfo->translationUnit()->ast()) { int pos = tc.position(); while (m_widget->document()->characterAt(pos).isSpace()) ++pos; if (m_widget->document()->characterAt(pos) == QLatin1Char('(')) { - link = attemptFuncDeclDef(m_cursor); + link = attemptFuncDeclDef(cursor, m_widget, snapshot, documentFromSemanticInfo, + symbolFinder); if (link.hasValidLinkText()) return link; } } int lineNumber = 0, positionInBlock = 0; - m_widget->convertPosition(m_cursor.position(), &lineNumber, &positionInBlock); + m_widget->convertPosition(cursor.position(), &lineNumber, &positionInBlock); const unsigned line = lineNumber; const unsigned column = positionInBlock + 1; @@ -189,9 +312,9 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() SimpleLexer tokenize; tokenize.setQtMocRunEnabled(true); - const QString blockText = m_cursor.block().text(); + const QString blockText = cursor.block().text(); const QList<Token> tokens = tokenize(blockText, - BackwardsScanner::previousBlockState(m_cursor.block())); + BackwardsScanner::previousBlockState(cursor.block())); bool recognizedQtMethod = false; @@ -223,7 +346,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() } if (j < tokens.size()) { - QTextBlock block = m_cursor.block(); + QTextBlock block = cursor.block(); beginOfToken = block.position() + tokens.at(i).begin(); endOfToken = block.position() + tokens.at(i).end(); @@ -238,17 +361,17 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() } // Now we prefer the doc from the snapshot with macros expanded. - Document::Ptr doc = m_snapshot.document(m_widget->editorDocument()->filePath()); + Document::Ptr doc = snapshot.document(m_widget->editorDocument()->filePath()); if (!doc) { - doc = m_document; + doc = documentFromSemanticInfo; if (!doc) return link; } if (!recognizedQtMethod) { const QTextBlock block = tc.block(); - int pos = m_cursor.positionInBlock(); - QChar ch = m_widget->document()->characterAt(m_cursor.position()); + int pos = cursor.positionInBlock(); + QChar ch = m_widget->document()->characterAt(cursor.position()); if (pos > 0 && !(ch.isLetterOrNumber() || ch == QLatin1Char('_'))) --pos; // positionInBlock points to a delimiter character. const Token tk = SimpleLexer::tokenAt(block.text(), pos, @@ -259,7 +382,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() // Handle include directives if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) { - const unsigned lineno = m_cursor.blockNumber() + 1; + const unsigned lineno = cursor.blockNumber() + 1; foreach (const Document::Include &incl, doc->resolvedIncludes()) { if (incl.line() == lineno) { link.targetFileName = incl.resolvedFileName(); @@ -279,7 +402,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() // Handle macro uses const Macro *macro = doc->findMacroDefinitionAt(line); if (macro) { - QTextCursor macroCursor = m_cursor; + QTextCursor macroCursor = cursor; const QByteArray name = CPPEditorWidget::identifierUnderCursor(¯oCursor).toLatin1(); if (macro->name() == name) return link; //already on definition! @@ -318,7 +441,7 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() } TypeOfExpression typeOfExpression; - typeOfExpression.init(doc, m_snapshot); + typeOfExpression.init(doc, snapshot); // make possible to instantiate templates typeOfExpression.setExpandTemplates(true); const QList<LookupItem> resolvedSymbols = @@ -355,16 +478,30 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() if (Symbol *symbol = result.declaration()) { Symbol *def = 0; - if (m_resolveTarget) { + // Consider to show a pop-up displaying overrides for the function + Function *function = symbol->type()->asFunctionType(); + if (lookupVirtualFunctionOverrides(expression, function, scope, snapshot)) { + Class *klass = symbolFinder->findMatchingClassDeclaration(function, snapshot); + QTC_CHECK(klass); + + if (m_virtualFunctionAssistProvider->configure(klass, function, snapshot, + inNextSplit)) { + m_widget->invokeAssist(TextEditor::FollowSymbol, + m_virtualFunctionAssistProvider); + } + return Link(); + } + + if (resolveTarget) { Symbol *lastVisibleSymbol = doc->lastVisibleSymbolAt(line, column); - def = findDefinition(symbol, m_snapshot); + def = findDefinition(symbol, snapshot, symbolFinder); if (def == lastVisibleSymbol) def = 0; // jump to declaration then. if (symbol->isForwardClassDeclaration()) - def = m_symbolFinder->findMatchingClassDeclaration(symbol, m_snapshot); + def = symbolFinder->findMatchingClassDeclaration(symbol, snapshot); } link = m_widget->linkToSymbol(def ? def : symbol); @@ -375,9 +512,9 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() } // Handle macro uses - QTextCursor macroCursor = m_cursor; + QTextCursor macroCursor = cursor; const QByteArray name = CPPEditorWidget::identifierUnderCursor(¯oCursor).toLatin1(); - link = findMacroLink(name, m_document); + link = findMacroLink(name, documentFromSemanticInfo); if (link.hasValidTarget()) { link.linkTextStart = macroCursor.selectionStart(); link.linkTextEnd = macroCursor.selectionEnd(); @@ -387,87 +524,12 @@ BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink() return Link(); } -CPPEditorWidget::Link FollowSymbolUnderCursor::attemptFuncDeclDef(const QTextCursor &cursor) +VirtualFunctionAssistProvider *FollowSymbolUnderCursor::virtualFunctionAssistProvider() { - m_snapshot.insert(m_document); - - Link result; - - QList<AST *> path = ASTPath(m_document)(cursor); - - if (path.size() < 5) - return result; - - NameAST *name = path.last()->asName(); - if (!name) - return result; - - if (QualifiedNameAST *qName = path.at(path.size() - 2)->asQualifiedName()) { - // TODO: check which part of the qualified name we're on - if (qName->unqualified_name != name) - return result; - } - - for (int i = path.size() - 1; i != -1; --i) { - AST *node = path.at(i); - - if (node->asParameterDeclaration() != 0) - return result; - } - - AST *declParent = 0; - DeclaratorAST *decl = 0; - for (int i = path.size() - 2; i > 0; --i) { - if ((decl = path.at(i)->asDeclarator()) != 0) { - declParent = path.at(i - 1); - break; - } - } - if (!decl || !declParent) - return result; - if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value) - return result; - FunctionDeclaratorAST *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator(); - if (!funcDecl) - return result; - - Symbol *target = 0; - if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) { - QList<Declaration *> candidates = - m_symbolFinder->findMatchingDeclaration(LookupContext(m_document, m_snapshot), - funDef->symbol); - if (!candidates.isEmpty()) // TODO: improve disambiguation - target = candidates.first(); - } else if (declParent->asSimpleDeclaration()) { - target = m_symbolFinder->findMatchingDefinition(funcDecl->symbol, m_snapshot); - } - - if (target) { - result = m_widget->linkToSymbol(target); - - unsigned startLine, startColumn, endLine, endColumn; - m_document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine, - &startColumn); - m_document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine, - &endColumn); - - QTextDocument *textDocument = cursor.document(); - result.linkTextStart = - textDocument->findBlockByNumber(startLine - 1).position() + startColumn - 1; - result.linkTextEnd = - textDocument->findBlockByNumber(endLine - 1).position() + endColumn - 1; - } - - return result; + return m_virtualFunctionAssistProvider; } -Symbol *FollowSymbolUnderCursor::findDefinition(Symbol *symbol, const Snapshot &snapshot) const +void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider) { - if (symbol->isFunction()) - return 0; // symbol is a function definition. - - else if (!symbol->type()->isFunctionType()) - return 0; // not a function declaration - - return m_symbolFinder->findMatchingDefinition(symbol, snapshot); + m_virtualFunctionAssistProvider = provider; } diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.h b/src/plugins/cppeditor/cppfollowsymbolundercursor.h index 5ef1f1902ad3de913626b0c4fb4b11131bfbee8a..4d88ecf0cd78c48026bb51815e5b130011167065 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.h +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.h @@ -34,7 +34,9 @@ #include <cplusplus/CppDocument.h> #include <texteditor/basetexteditor.h> -#include <QTextCursor> +QT_BEGIN_NAMESPACE +class QTextCursor; +QT_END_NAMESPACE namespace CppTools { class SymbolFinder; } @@ -42,32 +44,27 @@ namespace CppEditor { namespace Internal { class CPPEditorWidget; +class VirtualFunctionAssistProvider; class FollowSymbolUnderCursor { public: typedef TextEditor::BaseTextEditorWidget::Link Link; - // Ownership of widget and symbolFinder is *not* transferred. - FollowSymbolUnderCursor(CPPEditorWidget *widget, const QTextCursor &cursor, bool resolveTarget, - const CPlusPlus::Snapshot &snapshot, - const CPlusPlus::Document::Ptr &documentFromSemanticInfo, - CppTools::SymbolFinder *symbolFinder); + FollowSymbolUnderCursor(CPPEditorWidget *widget); + ~FollowSymbolUnderCursor(); - Link findLink(); + Link findLink(const QTextCursor &cursor, bool resolveTarget, + const CPlusPlus::Snapshot &snapshot, + const CPlusPlus::Document::Ptr &documentFromSemanticInfo, + CppTools::SymbolFinder *symbolFinder, bool inNextSplit); -private: - Link attemptFuncDeclDef(const QTextCursor &cursor); - CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol, - const CPlusPlus::Snapshot &snapshot) const; + VirtualFunctionAssistProvider *virtualFunctionAssistProvider(); + void setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider); private: - CppEditor::Internal::CPPEditorWidget *m_widget; - const QTextCursor m_cursor; - const bool m_resolveTarget; - CPlusPlus::Snapshot m_snapshot; - CPlusPlus::Document::Ptr m_document; // from SemanticInfo, i.e. with AST - CppTools::SymbolFinder *m_symbolFinder; + CPPEditorWidget *m_widget; + VirtualFunctionAssistProvider *m_virtualFunctionAssistProvider; }; } // namespace Internal diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3cbb4f99dff19764d8b46b295f3c744ec425e040 --- /dev/null +++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + + +#include "cppvirtualfunctionassistprovider.h" + +#include "cppeditorconstants.h" +#include "cppelementevaluator.h" + +#include <cplusplus/Icons.h> +#include <cplusplus/Overview.h> + +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/command.h> + +#include <texteditor/codeassist/basicproposalitem.h> +#include <texteditor/codeassist/basicproposalitemlistmodel.h> +#include <texteditor/codeassist/genericproposal.h> +#include <texteditor/codeassist/genericproposalwidget.h> +#include <texteditor/codeassist/iassistinterface.h> +#include <texteditor/codeassist/iassistprocessor.h> +#include <texteditor/codeassist/iassistproposal.h> + +#include <utils/qtcassert.h> + +using namespace CPlusPlus; +using namespace CppEditor::Internal; +using namespace TextEditor; + +class VirtualFunctionProposalItem: public BasicProposalItem { +public: + VirtualFunctionProposalItem(const BaseTextEditorWidget::Link &link, bool openInSplit = true) + : m_link(link), m_openInSplit(openInSplit) {} + + void apply(BaseTextEditor */*editor*/, int /*basePosition*/) const + { + if (!m_link.hasValidTarget()) + return; + + Core::EditorManager::OpenEditorFlags flags; + if (m_openInSplit) + flags |= Core::EditorManager::OpenInOtherSplit; + Core::EditorManager::openEditorAt(m_link.targetFileName, + m_link.targetLine, + m_link.targetColumn, + CppEditor::Constants::CPPEDITOR_ID, + flags); + } + +private: + BaseTextEditorWidget::Link m_link; + bool m_openInSplit; +}; + +/// Activate current item with the same shortcut that is configured for Follow Symbol Under Cursor. +/// This is limited to single-key shortcuts without modifiers. +class VirtualFunctionProposalWidget : public GenericProposalWidget +{ +public: + VirtualFunctionProposalWidget(bool openInSplit) + { + const char *id = openInSplit + ? TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT + : TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR; + if (Core::Command *command = Core::ActionManager::command(id)) + m_sequence = command->keySequence(); + } + +protected: + bool eventFilter(QObject *o, QEvent *e) + { + if (e->type() == QEvent::ShortcutOverride && m_sequence.count() == 1) { + QKeyEvent *ke = static_cast<QKeyEvent *>(e); + const QKeySequence seq(ke->key()); + if (seq == m_sequence) { + activateCurrentProposalItem(); + e->accept(); + return true; + } + } + return GenericProposalWidget::eventFilter(o, e); + } + +private: + QKeySequence m_sequence; +}; + +class VirtualFunctionProposal : public GenericProposal +{ +public: + VirtualFunctionProposal(int cursorPos, IGenericProposalModel *model, bool openInSplit) + : GenericProposal(cursorPos, model) + , m_openInSplit(openInSplit) + {} + + bool isFragile() const + { return true; } + + IAssistProposalWidget *createWidget() const + { return new VirtualFunctionProposalWidget(m_openInSplit); } + +private: + bool m_openInSplit; +}; + +class VirtualFunctionsAssistProcessor : public IAssistProcessor +{ +public: + VirtualFunctionsAssistProcessor(const VirtualFunctionAssistProvider *provider) + : m_startClass(provider->startClass()) + , m_function(provider->function()) + , m_snapshot(provider->snapshot()) + , m_openInNextSplit(provider->openInNextSplit()) + {} + + IAssistProposal *immediateProposal(const TextEditor::IAssistInterface *interface) + { + QTC_ASSERT(m_function, return 0); + + BasicProposalItem *hintItem = new VirtualFunctionProposalItem(CPPEditorWidget::Link()); + hintItem->setText(QCoreApplication::translate("VirtualFunctionsAssistProcessor", + "...searching overrides")); + hintItem->setOrder(-1000); + + QList<BasicProposalItem *> items; + items << itemFromSymbol(m_function, m_function); + items << hintItem; + return new VirtualFunctionProposal(interface->position(), + new BasicProposalItemListModel(items), + m_openInNextSplit); + } + + IAssistProposal *perform(const IAssistInterface *interface) + { + if (!interface) + return 0; + + QTC_ASSERT(m_startClass, return 0); + QTC_ASSERT(m_function, return 0); + QTC_ASSERT(!m_snapshot.isEmpty(), return 0); + + const QList<Symbol *> overrides = FunctionHelper::overrides(m_startClass, m_function, + m_snapshot); + QList<BasicProposalItem *> items; + foreach (Symbol *symbol, overrides) + items << itemFromSymbol(symbol, m_function); + + return new VirtualFunctionProposal(interface->position(), + new BasicProposalItemListModel(items), + m_openInNextSplit); + } + + BasicProposalItem *itemFromSymbol(Symbol *symbol, Symbol *firstSymbol) const + { + const QString text = m_overview.prettyName(LookupContext::fullyQualifiedName(symbol)); + const CPPEditorWidget::Link link = CPPEditorWidget::linkToSymbol(symbol); + + BasicProposalItem *item = new VirtualFunctionProposalItem(link, m_openInNextSplit); + item->setText(text); + item->setIcon(m_icons.iconForSymbol(symbol)); + if (symbol == firstSymbol) + item->setOrder(1000); // Ensure top position for function of static type + + return item; + } + +private: + Class *m_startClass; + Function *m_function; + Snapshot m_snapshot; + bool m_openInNextSplit; + Overview m_overview; + Icons m_icons; +}; + +VirtualFunctionAssistProvider::VirtualFunctionAssistProvider() + : m_function(0) + , m_openInNextSplit(false) +{ +} + +bool VirtualFunctionAssistProvider::configure(Class *startClass, Function *function, + const Snapshot &snapshot, bool openInNextSplit) +{ + m_startClass = startClass; + m_function = function; + m_snapshot = snapshot; + m_openInNextSplit = openInNextSplit; + return true; +} + +bool VirtualFunctionAssistProvider::isAsynchronous() const +{ + return true; +} + +bool VirtualFunctionAssistProvider::supportsEditor(const Core::Id &editorId) const +{ + return editorId == CppEditor::Constants::CPPEDITOR_ID; +} + +IAssistProcessor *VirtualFunctionAssistProvider::createProcessor() const +{ + return new VirtualFunctionsAssistProcessor(this); +} + +enum VirtualType { Virtual, PureVirtual }; + +static bool isVirtualFunction_helper(Function *function, const Snapshot &snapshot, + VirtualType virtualType) +{ + if (!function) + return false; + + if ((virtualType == Virtual && function->isVirtual()) + || (virtualType == PureVirtual && function->isPureVirtual())) { + return true; + } + + const QString filePath = QString::fromUtf8(function->fileName(), function->fileNameLength()); + if (Document::Ptr document = snapshot.document(filePath)) { + LookupContext context(document, snapshot); + QList<LookupItem> results = context.lookup(function->name(), function->enclosingScope()); + if (!results.isEmpty()) { + foreach (const LookupItem &item, results) { + if (Symbol *symbol = item.declaration()) { + if (Function *functionType = symbol->type()->asFunctionType()) { + if (!functionType) { + if (Template *t = item.type()->asTemplateType()) + if ((symbol = t->declaration())) + functionType = symbol->type()->asFunctionType(); + } + const bool foundSuitable = virtualType == Virtual + ? functionType->isVirtual() + : functionType->isPureVirtual(); + if (foundSuitable) + return true; + } + } + } + } + } + + return false; +} + +bool FunctionHelper::isVirtualFunction(Function *function, const Snapshot &snapshot) +{ + return isVirtualFunction_helper(function, snapshot, Virtual); +} + +bool FunctionHelper::isPureVirtualFunction(Function *function, const Snapshot &snapshot) +{ + return isVirtualFunction_helper(function, snapshot, PureVirtual); +} + +QList<Symbol *> FunctionHelper::overrides(Class *startClass, Function *function, + const Snapshot &snapshot) +{ + QList<Symbol *> result; + QTC_ASSERT(function && startClass, return result); + + FullySpecifiedType referenceType = function->type(); + const Name *referenceName = function->name(); + + // Add itself + result << function; + + // Find overrides + CppEditor::Internal::CppClass cppClass = CppClass(startClass); + cppClass.lookupDerived(startClass, snapshot); + + QList<CppClass> l; + l << cppClass; + + while (!l.isEmpty()) { + // Add derived + CppClass clazz = l.takeFirst(); + foreach (const CppClass &d, clazz.derived) { + if (!l.contains(d)) + l << d; + } + + // Check member functions + QTC_ASSERT(clazz.declaration, continue); + Class *c = clazz.declaration->asClass(); + QTC_ASSERT(c, continue); + for (int i = 0, total = c->memberCount(); i < total; ++i) { + Symbol *candidate = c->memberAt(i); + const Name *candidateName = candidate->name(); + FullySpecifiedType candidateType = candidate->type(); + if (candidateName->isEqualTo(referenceName) && candidateType.isEqualTo(referenceType)) + result << candidate; + } + } + + return result; +} diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h new file mode 100644 index 0000000000000000000000000000000000000000..b3bb76c24d1f91e6580384e3d5e075369c5f35d0 --- /dev/null +++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CPPFOLLOWVIRTUALSYMBOLS_H +#define CPPFOLLOWVIRTUALSYMBOLS_H + +#include <texteditor/codeassist/iassistprovider.h> + +#include <cplusplus/CppDocument.h> +#include <cplusplus/Symbols.h> + +namespace CppEditor { +namespace Internal { + +class VirtualFunctionAssistProvider : public TextEditor::IAssistProvider +{ +public: + VirtualFunctionAssistProvider(); + + virtual bool configure(CPlusPlus::Class *startClass, CPlusPlus::Function *function, + const CPlusPlus::Snapshot &snapshot, bool openInNextSplit); + CPlusPlus::Class *startClass() const { return m_startClass; } + CPlusPlus::Function *function() const { return m_function; } + CPlusPlus::Snapshot snapshot() const { return m_snapshot; } + bool openInNextSplit() const { return m_openInNextSplit; } + + bool isAsynchronous() const; + bool supportsEditor(const Core::Id &editorId) const; + TextEditor::IAssistProcessor *createProcessor() const; + +private: + CPlusPlus::Class *m_startClass; + CPlusPlus::Function *m_function; + CPlusPlus::Snapshot m_snapshot; + bool m_openInNextSplit; +}; + +class FunctionHelper +{ +public: + static bool isVirtualFunction(CPlusPlus::Function *function, + const CPlusPlus::Snapshot &snapshot); + + static bool isPureVirtualFunction(CPlusPlus::Function *function, + const CPlusPlus::Snapshot &snapshot); + + static QList<CPlusPlus::Symbol *> overrides(CPlusPlus::Class *startClass, + CPlusPlus::Function *function, const CPlusPlus::Snapshot &snapshot); +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPFOLLOWVIRTUALSYMBOLS_H diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index 46ff3e159ba5664222015984a444cf6f0fa5ab6b..f922b20de3c55346212dac393e8ec4d90aca6214 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -29,8 +29,13 @@ #include "cppeditor.h" #include "cppeditorplugin.h" +#include "cppelementevaluator.h" +#include "cppvirtualfunctionassistprovider.h" #include <coreplugin/plugintestutils.h> +#include <texteditor/codeassist/iassistproposal.h> +#include <texteditor/codeassist/iassistprocessor.h> +#include <texteditor/codeassist/basicproposalitemlistmodel.h> #include <utils/fileutils.h> #include <QDebug> @@ -57,6 +62,60 @@ using namespace Core; namespace { +/// A fake virtual functions assist provider that runs processor->perform() already in configure() +class VirtualFunctionTestAssistProvider : public VirtualFunctionAssistProvider +{ +public: + VirtualFunctionTestAssistProvider(CPPEditorWidget *editorWidget) + : m_editorWidget(editorWidget) + {} + + // Invoke the processor already here to calculate the proposals. Return false in order to + // indicate that configure failed, so the actual code assist invocation leading to a pop-up + // will not happen. + bool configure(CPlusPlus::Class *startClass, CPlusPlus::Function *function, + const CPlusPlus::Snapshot &snapshot, bool openInNextSplit) + { + VirtualFunctionAssistProvider::configure(startClass, function, snapshot, openInNextSplit); + + IAssistProcessor *processor = createProcessor(); + IAssistInterface *interface = m_editorWidget->createAssistInterface(FollowSymbol, + ExplicitlyInvoked); + IAssistProposal *immediateProposal = processor->immediateProposal(interface); + IAssistProposal *finalProposal = processor->perform(interface); + + m_immediateItems = itemList(immediateProposal->model()); + m_finalItems = itemList(finalProposal->model()); + + return false; + } + + static QStringList itemList(IAssistProposalModel *imodel) + { + QStringList immediateItems; + BasicProposalItemListModel *model = dynamic_cast<BasicProposalItemListModel *>(imodel); + if (!model) + return immediateItems; + if (model->isSortable(QString())) + model->sort(QString()); + model->removeDuplicates(); + + for (int i = 0, size = model->size(); i < size; ++i) { + const QString text = model->text(i); + immediateItems.append(text); + } + + return immediateItems; + } + +public: + QStringList m_immediateItems; + QStringList m_finalItems; + +private: + CPPEditorWidget *m_editorWidget; +}; + class TestDocument; typedef QSharedPointer<TestDocument> TestDocumentPtr; @@ -136,12 +195,16 @@ class TestCase { public: enum CppEditorAction { - FollowSymbolUnderCursor, - SwitchBetweenMethodDeclarationDefinition + FollowSymbolUnderCursorAction, + SwitchBetweenMethodDeclarationDefinitionAction }; - TestCase(CppEditorAction action, const QByteArray &source); - TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles); + TestCase(CppEditorAction action, const QByteArray &source, + const QStringList &expectedVirtualFunctionImmediateProposal = QStringList(), + const QStringList &expectedVirtualFunctionFinalProposal = QStringList()); + TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles, + const QStringList &expectedVirtualSymbolsImmediateProposal = QStringList(), + const QStringList &expectedVirtualSymbolsFinalProposal = QStringList()); ~TestCase(); void run(bool expectedFail = false); @@ -158,12 +221,18 @@ private: private: CppEditorAction m_action; QList<TestDocumentPtr> m_testFiles; + QStringList m_expectedVirtualSymbolsImmediateProposal; // for virtual functions + QStringList m_expectedVirtualSymbolsFinalProposals; // for virtual functions }; /// Convenience function for creating a TestDocument. /// See TestDocument. -TestCase::TestCase(CppEditorAction action, const QByteArray &source) +TestCase::TestCase(CppEditorAction action, const QByteArray &source, + const QStringList &expectedVirtualFunctionImmediateProposal, + const QStringList &expectedVirtualFunctionFinalProposal) : m_action(action) + , m_expectedVirtualSymbolsImmediateProposal(expectedVirtualFunctionImmediateProposal) + , m_expectedVirtualSymbolsFinalProposals(expectedVirtualFunctionFinalProposal) { m_testFiles << TestDocument::create(source, QLatin1String("file.cpp")); init(); @@ -173,9 +242,13 @@ TestCase::TestCase(CppEditorAction action, const QByteArray &source) /// Exactly one test document must be provided that contains '@', the initial position marker. /// Exactly one test document must be provided that contains '$', the target position marker. /// It can be the same document. -TestCase::TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles) +TestCase::TestCase(CppEditorAction action, const QList<TestDocumentPtr> theTestFiles, + const QStringList &expectedVirtualSymbolsImmediateProposal, + const QStringList &expectedVirtualSymbolsFinalProposal) : m_action(action) , m_testFiles(theTestFiles) + , m_expectedVirtualSymbolsImmediateProposal(expectedVirtualSymbolsImmediateProposal) + , m_expectedVirtualSymbolsFinalProposals(expectedVirtualSymbolsFinalProposal) { init(); } @@ -292,12 +365,29 @@ void TestCase::run(bool expectedFail) // qDebug() << "Initial line:" << initialTestFile->editor->currentLine(); // qDebug() << "Initial column:" << initialTestFile->editor->currentColumn() - 1; + QStringList immediateVirtualSymbolResults; + QStringList finalVirtualSymbolResults; + // Trigger the action switch (m_action) { - case FollowSymbolUnderCursor: + case FollowSymbolUnderCursorAction: { + CPPEditorWidget *widget = initialTestFile->editorWidget; + FollowSymbolUnderCursor *delegate = widget->followSymbolUnderCursorDelegate(); + VirtualFunctionAssistProvider *original = delegate->virtualFunctionAssistProvider(); + + // Set test provider, run and get results + QScopedPointer<VirtualFunctionTestAssistProvider> testProvider( + new VirtualFunctionTestAssistProvider(widget)); + delegate->setVirtualFunctionAssistProvider(testProvider.data()); initialTestFile->editorWidget->openLinkUnderCursor(); + immediateVirtualSymbolResults = testProvider->m_immediateItems; + finalVirtualSymbolResults = testProvider->m_finalItems; + + // Restore original test provider + delegate->setVirtualFunctionAssistProvider(original); break; - case SwitchBetweenMethodDeclarationDefinition: + } + case SwitchBetweenMethodDeclarationDefinitionAction: CppEditorPlugin::instance()->switchDeclarationDefinition(); break; default: @@ -318,10 +408,16 @@ void TestCase::run(bool expectedFail) &expectedLine, &expectedColumn); // qDebug() << "Expected line:" << expectedLine; // qDebug() << "Expected column:" << expectedColumn; + if (expectedFail) QEXPECT_FAIL("", "Contributor works on a fix.", Abort); QCOMPARE(currentTextEditor->currentLine(), expectedLine); QCOMPARE(currentTextEditor->currentColumn() - 1, expectedColumn); + +// qDebug() << immediateVirtualSymbolResults; +// qDebug() << finalVirtualSymbolResults; + QCOMPARE(immediateVirtualSymbolResults, m_expectedVirtualSymbolsImmediateProposal); + QCOMPARE(finalVirtualSymbolResults, m_expectedVirtualSymbolsFinalProposals); } } // anonymous namespace @@ -354,7 +450,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromFunctionDeclara ; testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp")); - TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles); + TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles); test.run(); } @@ -386,7 +482,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromFunctionDefinit ; testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp")); - TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles); + TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles); test.run(); } @@ -418,7 +514,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromFunctionBody() ; testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp")); - TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles); + TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles); test.run(); } @@ -450,7 +546,7 @@ void CppEditorPlugin::test_SwitchMethodDeclarationDefinition_fromReturnType() ; testFiles << TestDocument::create(sourceContents, QLatin1String("file.cpp")); - TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinition, testFiles); + TestCase test(TestCase::SwitchBetweenMethodDeclarationDefinitionAction, testFiles); test.run(); } @@ -465,7 +561,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_globalVarFromFunction() "}\n" // Line 5 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -483,7 +579,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funLocalVarHidesClassMember() "};\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -503,7 +599,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funLocalVarHidesNamespaceMemb "}\n" // Line 10 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -521,7 +617,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_loopLocalVarHidesOuterScopeVa "}\n"; ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -539,7 +635,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_loopLocalVarHidesOuterScopeVa "}\n"; ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -553,7 +649,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_subsequentDefinedClassMember( "};\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -569,7 +665,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classMemberHidesOuterTypeDef( "};\n" // Line 5 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -584,7 +680,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_globalVarFromEnum() "}\n" // Line 5 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(/*expectedFail =*/ true); } @@ -599,7 +695,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_selfInitialization() "}\n" // Line 5 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -612,7 +708,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_pointerToClassInClassDefiniti "};\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -626,7 +722,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_previouslyDefinedMemberFromAr "};\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -648,7 +744,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_outerStaticMemberVariableFrom "};\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -668,7 +764,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_memberVariableFollowingDotOpe "}\n" // Line 10 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -688,7 +784,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_memberVariableFollowingArrowO "}\n" // Line 10 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -707,7 +803,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_staticMemberVariableFollowing "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -727,7 +823,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_staticMemberVariableFollowing "}\n" // Line 10 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -748,7 +844,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_staticMemberVariableFollowing "}\n" // Line 10 ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -762,7 +858,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_previouslyDefinedEnumValueFro "};\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -785,7 +881,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_nsMemberHidesNsMemberIntroduc "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -812,7 +908,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_baseClassFunctionIntroducedBy "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -839,7 +935,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funWithSameNameAsBaseClassFun "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -858,7 +954,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_funLocalVarHidesOuterClass() "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -876,7 +972,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classConstructor() "{\n" "}\n"; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -894,7 +990,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_classDestructor() "{\n" "}\n"; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -988,7 +1084,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_QObject_connect() return; } - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -1005,7 +1101,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_global "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -1024,7 +1120,7 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_namesp "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } @@ -1041,7 +1137,177 @@ void CppEditorPlugin::test_FollowSymbolUnderCursor_using_QTCREATORBUG7903_inside "}\n" ; - TestCase test(TestCase::FollowSymbolUnderCursor, source); + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + +/// Check: Static type is base class pointer, all overrides are presented. +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_allOverrides() +{ + const QByteArray source = + "struct A { virtual void virt() = 0; };\n" + "void A::virt() {}\n" + "\n" + "struct B : A { void virt(); };\n" + "void B::virt() {}\n" + "\n" + "struct C : B { void virt(); };\n" + "void C::virt() {}\n" + "\n" + "struct CD1 : C { void virt(); };\n" + "void CD1::virt() {}\n" + "\n" + "struct CD2 : C { void virt(); };\n" + "void CD2::virt() {}\n" + "\n" + "int f(A *o)\n" + "{\n" + " o->$@virt();\n" + "}\n" + ; + + const QStringList immediateResults = QStringList() + << QLatin1String("A::virt") + << QLatin1String("...searching overrides"); + const QStringList finalResults = QStringList() + << QLatin1String("A::virt") + << QLatin1String("A::virt") // TODO: Double entry + << QLatin1String("B::virt") + << QLatin1String("C::virt") + << QLatin1String("CD1::virt") + << QLatin1String("CD2::virt"); + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source, immediateResults, finalResults); + test.run(); +} + +/// Check: Static type is derived class pointer, only overrides of sub classes are presented. +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides1() +{ + const QByteArray source = + "struct A { virtual void virt() = 0; };\n" + "void A::virt() {}\n" + "\n" + "struct B : A { void virt(); };\n" + "void B::virt() {}\n" + "\n" + "struct C : B { void virt(); };\n" + "void C::virt() {}\n" + "\n" + "struct CD1 : C { void virt(); };\n" + "void CD1::virt() {}\n" + "\n" + "struct CD2 : C { void virt(); };\n" + "void CD2::virt() {}\n" + "\n" + "int f(B *o)\n" + "{\n" + " o->$@virt();\n" + "}\n" + ; + + const QStringList immediateResults = QStringList() + << QLatin1String("B::virt") + << QLatin1String("...searching overrides"); + const QStringList finalResults = QStringList() + << QLatin1String("B::virt") + << QLatin1String("B::virt") // Double entry + << QLatin1String("C::virt") + << QLatin1String("CD1::virt") + << QLatin1String("CD2::virt"); + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source, immediateResults, finalResults); + test.run(); +} + +/// Check: Virtual function call in member of class hierarchy, only possible overrides are presented. +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_possibleOverrides2() +{ + const QByteArray source = + "struct A { virtual void f(); };\n" + "void A::f() {}\n" + "\n" + "struct B : public A { void f(); };\n" + "void B::f() {}\n" + "\n" + "struct C : public B { void g() { f$@(); } }; \n" + "\n" + "struct D : public C { void f(); };\n" + "void D::f() {}\n" + ; + + const QStringList immediateResults = QStringList() + << QLatin1String("B::f") + << QLatin1String("...searching overrides"); + const QStringList finalResults = QStringList() + << QLatin1String("B::f") + << QLatin1String("B::f") + << QLatin1String("D::f"); + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source, immediateResults, finalResults); + test.run(); +} + +/// Check: Do not trigger on qualified function calls. +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnQualified() +{ + const QByteArray source = + "struct A { virtual void f(); };\n" + "void A::$f() {}\n" + "\n" + "struct B : public A {\n" + " void f();\n" + " void g() { A::@f(); }\n" + "};\n" + ; + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + +/// Check: Do not trigger on member function declaration. +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDeclaration() +{ + const QByteArray source = + "struct A { virtual void f(); };\n" + "void A::f() {}\n" + "\n" + "struct B : public A { void f@(); };\n" + "void B::$f() {}\n" + ; + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + +/// Check: Do not trigger on function definition. +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnDefinition() +{ + const QByteArray source = + "struct A { virtual void f(); };\n" + "void A::f() {}\n" + "\n" + "struct B : public A { void $f(); };\n" + "void B::@f() {}\n" + ; + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); + test.run(); +} + +void CppEditorPlugin::test_FollowSymbolUnderCursor_virtualFunctionCall_notOnNonPointerNonReference() +{ + const QByteArray source = + "struct A { virtual void f(); };\n" + "void A::f() {}\n" + "\n" + "struct B : public A { void f(); };\n" + "void B::$f() {}\n" + "\n" + "void client(B b) { b.@f(); }\n" + ; + + TestCase test(TestCase::FollowSymbolUnderCursorAction, source); test.run(); } diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index f54d440834bab915a1fdae91ca6b65325826b056..c8b71c9bdad15ded748c18e3773e425bdd1f2128 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -1063,7 +1063,9 @@ void QmlJSTextEditorWidget::createToolBar(QmlJSEditor *editor) editor->insertExtraToolBarWidget(TextEditor::BaseTextEditor::Left, m_outlineCombo); } -TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const QTextCursor &cursor, bool /*resolveTarget*/) +TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const QTextCursor &cursor, + bool /*resolveTarget*/, + bool /*inNextSplit*/) { const SemanticInfo semanticInfo = m_semanticInfo; if (! semanticInfo.isValid()) diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index 596bbcf4276f2b7506ed7d056124485201d8e54e..5ce152d3d31fb1e99c43cceb54ff4487f6cb3830 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -161,7 +161,9 @@ protected: void scrollContentsBy(int dx, int dy); TextEditor::BaseTextEditor *createEditor(); void createToolBar(QmlJSEditor *editable); - TextEditor::BaseTextEditorWidget::Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true); + TextEditor::BaseTextEditorWidget::Link findLinkAt(const QTextCursor &cursor, + bool resolveTarget = true, + bool inNextSplit = false); QString foldReplacementText(const QTextBlock &block) const; private: diff --git a/src/plugins/qt4projectmanager/profileeditor.cpp b/src/plugins/qt4projectmanager/profileeditor.cpp index ab35944aaa720d7d201072af1ba580616c20d4bb..3460c76b982612cf35970ac1ae8a1600995e5892 100644 --- a/src/plugins/qt4projectmanager/profileeditor.cpp +++ b/src/plugins/qt4projectmanager/profileeditor.cpp @@ -112,7 +112,8 @@ static bool isValidFileNameChar(const QChar &c) } ProFileEditorWidget::Link ProFileEditorWidget::findLinkAt(const QTextCursor &cursor, - bool /* resolveTarget */) + bool /*resolveTarget*/, + bool /*inNextSplit*/) { Link link; diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h index e8a8fb3ad08a85e20ba43804d1be74964ba24869..3e4150b2aeaeda0ebca19cb146ae06df8a24b426 100644 --- a/src/plugins/qt4projectmanager/profileeditor.h +++ b/src/plugins/qt4projectmanager/profileeditor.h @@ -72,7 +72,8 @@ public: void unCommentSelection(); protected: - virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true); + virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true, + bool inNextSplit = false); TextEditor::BaseTextEditor *createEditor(); void contextMenuEvent(QContextMenuEvent *); diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 504404b97970b62bf1c76d74a29122cc1da9989e..9588585f8f4bc746f36c25d236da07e418f9584f 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -1047,16 +1047,16 @@ void BaseTextEditorWidget::unindent() void BaseTextEditorWidget::openLinkUnderCursor() { - Link symbolLink = findLinkAt(textCursor()); - - openLink(symbolLink, alwaysOpenLinksInNextSplit()); + const bool openInNextSplit = alwaysOpenLinksInNextSplit(); + Link symbolLink = findLinkAt(textCursor(), true, openInNextSplit); + openLink(symbolLink, openInNextSplit); } void BaseTextEditorWidget::openLinkUnderCursorInNextSplit() { - Link symbolLink = findLinkAt(textCursor()); - - openLink(symbolLink, !alwaysOpenLinksInNextSplit()); + const bool openInNextSplit = !alwaysOpenLinksInNextSplit(); + Link symbolLink = findLinkAt(textCursor(), true, openInNextSplit); + openLink(symbolLink, openInNextSplit); } void BaseTextEditorWidget::abortAssist() @@ -4803,7 +4803,7 @@ void BaseTextEditorWidget::reindent(QTextDocument *doc, const QTextCursor &curso d->m_indenter->reindent(doc, cursor, tabSettings()); } -BaseTextEditorWidget::Link BaseTextEditorWidget::findLinkAt(const QTextCursor &, bool) +BaseTextEditorWidget::Link BaseTextEditorWidget::findLinkAt(const QTextCursor &, bool, bool) { return Link(); } diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 2f7ee2ee9310c814499e6c405844e4559727197d..3db73fb48267cdc12f9525e80491b6f3dc4ddb28 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -503,7 +503,8 @@ protected: \a resolveTarget is set to true when the target of the link is relevant (it isn't until the link is used). */ - virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true); + virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true, + bool inNextSplit = false); /*! Reimplement this function if you want to customize the way a link is diff --git a/src/plugins/texteditor/codeassist/assistenums.h b/src/plugins/texteditor/codeassist/assistenums.h index 31447cec1bc05eb6ee2125028cf9be1bd193b261..b71a96ce467a90fc3c431ef1b45a3cb0306ffe40 100644 --- a/src/plugins/texteditor/codeassist/assistenums.h +++ b/src/plugins/texteditor/codeassist/assistenums.h @@ -35,7 +35,8 @@ namespace TextEditor { enum AssistKind { Completion, - QuickFix + QuickFix, + FollowSymbol }; enum AssistReason diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp index 22b7d158806c4b1e88aac865982a885918ad3dcb..52303c1dc0d0adc1e08a0bb9f57254a37a97d3b6 100644 --- a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp +++ b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp @@ -547,9 +547,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e) if (fe->reason() == Qt::OtherFocusReason) { // Qt/carbon workaround // focus out is received before the key press event. - if (d->m_completionListView->currentIndex().isValid()) - emit proposalItemActivated(d->m_model->proposalItem( - d->m_completionListView->currentIndex().row())); + activateCurrentProposalItem(); } } if (d->m_infoFrame) @@ -593,9 +591,7 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e) case Qt::Key_Return: if (!useCarbonWorkaround()) { abort(); - if (d->m_completionListView->currentIndex().isValid()) - emit proposalItemActivated(d->m_model->proposalItem( - d->m_completionListView->currentIndex().row())); + activateCurrentProposalItem(); } return true; @@ -658,6 +654,16 @@ bool GenericProposalWidget::eventFilter(QObject *o, QEvent *e) return false; } +bool GenericProposalWidget::activateCurrentProposalItem() +{ + if (d->m_completionListView->currentIndex().isValid()) { + const int currentRow = d->m_completionListView->currentIndex().row(); + emit proposalItemActivated(d->m_model->proposalItem(currentRow)); + return true; + } + return false; +} + #include "genericproposalwidget.moc" } // TextEditor diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.h b/src/plugins/texteditor/codeassist/genericproposalwidget.h index cc68c0253eb293aeff25c5fcb4b9eb4ddc3673e5..f1f5f5a61ece95598db42ee8ef96a8d29b36226b 100644 --- a/src/plugins/texteditor/codeassist/genericproposalwidget.h +++ b/src/plugins/texteditor/codeassist/genericproposalwidget.h @@ -32,11 +32,13 @@ #include "iassistproposalwidget.h" +#include <texteditor/texteditor_global.h> + namespace TextEditor { class GenericProposalWidgetPrivate; -class GenericProposalWidget : public IAssistProposalWidget +class TEXTEDITOR_EXPORT GenericProposalWidget : public IAssistProposalWidget { Q_OBJECT friend class GenericProposalWidgetPrivate; @@ -69,6 +71,7 @@ private slots: protected: virtual bool eventFilter(QObject *o, QEvent *e); + bool activateCurrentProposalItem(); private: GenericProposalWidgetPrivate *d;