forked from qt-creator/qt-creator
CppEditor: "Follow Symbol Under Cursor" for virtual functions
F2 on a virtual function call presents a list of overrides in derived classes. The function declaration of the static type is shown immediately at the top. Task-number: QTCREATORBUG-9611 Change-Id: I80ce906fa06272dc9fbd1662cd17500b8c77067f Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -60,6 +60,8 @@ QtcPlugin {
|
||||
"cppsnippetprovider.h",
|
||||
"cpptypehierarchy.cpp",
|
||||
"cpptypehierarchy.h",
|
||||
"cppvirtualfunctionassistprovider.cpp",
|
||||
"cppvirtualfunctionassistprovider.h",
|
||||
]
|
||||
|
||||
Group {
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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,262 +170,15 @@ inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolvedSymbo
|
||||
return result;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
FollowSymbolUnderCursor::FollowSymbolUnderCursor(CPPEditorWidget *widget, const QTextCursor &cursor,
|
||||
bool resolveTarget, const Snapshot &snapshot, const Document::Ptr &documentFromSemanticInfo,
|
||||
CppTools::SymbolFinder *symbolFinder)
|
||||
: m_widget(widget)
|
||||
, m_cursor(cursor)
|
||||
, m_resolveTarget(resolveTarget)
|
||||
, m_snapshot(snapshot)
|
||||
, m_document(documentFromSemanticInfo)
|
||||
, m_symbolFinder(symbolFinder)
|
||||
CPPEditorWidget::Link attemptFuncDeclDef(const QTextCursor &cursor,
|
||||
CPPEditorWidget *widget, CPlusPlus::Snapshot snapshot, const CPlusPlus::Document::Ptr &document,
|
||||
SymbolFinder *symbolFinder)
|
||||
{
|
||||
}
|
||||
|
||||
BaseTextEditorWidget::Link FollowSymbolUnderCursor::findLink()
|
||||
{
|
||||
Link link;
|
||||
|
||||
// Move to end of identifier
|
||||
QTextCursor tc = m_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()) {
|
||||
int pos = tc.position();
|
||||
while (m_widget->document()->characterAt(pos).isSpace())
|
||||
++pos;
|
||||
if (m_widget->document()->characterAt(pos) == QLatin1Char('(')) {
|
||||
link = attemptFuncDeclDef(m_cursor);
|
||||
if (link.hasValidLinkText())
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
int lineNumber = 0, positionInBlock = 0;
|
||||
m_widget->convertPosition(m_cursor.position(), &lineNumber, &positionInBlock);
|
||||
const unsigned line = lineNumber;
|
||||
const unsigned column = positionInBlock + 1;
|
||||
|
||||
// Try to find a signal or slot inside SIGNAL() or SLOT()
|
||||
int beginOfToken = 0;
|
||||
int endOfToken = 0;
|
||||
|
||||
SimpleLexer tokenize;
|
||||
tokenize.setQtMocRunEnabled(true);
|
||||
const QString blockText = m_cursor.block().text();
|
||||
const QList<Token> tokens = tokenize(blockText,
|
||||
BackwardsScanner::previousBlockState(m_cursor.block()));
|
||||
|
||||
bool recognizedQtMethod = false;
|
||||
|
||||
for (int i = 0; i < tokens.size(); ++i) {
|
||||
const Token &tk = tokens.at(i);
|
||||
|
||||
if (((unsigned) positionInBlock) >= tk.begin()
|
||||
&& ((unsigned) positionInBlock) <= tk.end()) {
|
||||
if (i >= 2 && tokens.at(i).is(T_IDENTIFIER) && tokens.at(i - 1).is(T_LPAREN)
|
||||
&& (tokens.at(i - 2).is(T_SIGNAL) || tokens.at(i - 2).is(T_SLOT))) {
|
||||
|
||||
// token[i] == T_IDENTIFIER
|
||||
// token[i + 1] == T_LPAREN
|
||||
// token[.....] == ....
|
||||
// token[i + n] == T_RPAREN
|
||||
|
||||
if (i + 1 < tokens.size() && tokens.at(i + 1).is(T_LPAREN)) {
|
||||
// skip matched parenthesis
|
||||
int j = i - 1;
|
||||
int depth = 0;
|
||||
|
||||
for (; j < tokens.size(); ++j) {
|
||||
if (tokens.at(j).is(T_LPAREN)) {
|
||||
++depth;
|
||||
} else if (tokens.at(j).is(T_RPAREN)) {
|
||||
if (!--depth)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < tokens.size()) {
|
||||
QTextBlock block = m_cursor.block();
|
||||
|
||||
beginOfToken = block.position() + tokens.at(i).begin();
|
||||
endOfToken = block.position() + tokens.at(i).end();
|
||||
|
||||
tc.setPosition(block.position() + tokens.at(j).end());
|
||||
recognizedQtMethod = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we prefer the doc from the snapshot with macros expanded.
|
||||
Document::Ptr doc = m_snapshot.document(m_widget->editorDocument()->filePath());
|
||||
if (!doc) {
|
||||
doc = m_document;
|
||||
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());
|
||||
if (pos > 0 && !(ch.isLetterOrNumber() || ch == QLatin1Char('_')))
|
||||
--pos; // positionInBlock points to a delimiter character.
|
||||
const Token tk = SimpleLexer::tokenAt(block.text(), pos,
|
||||
BackwardsScanner::previousBlockState(block), true);
|
||||
|
||||
beginOfToken = block.position() + tk.begin();
|
||||
endOfToken = block.position() + tk.end();
|
||||
|
||||
// Handle include directives
|
||||
if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
|
||||
const unsigned lineno = m_cursor.blockNumber() + 1;
|
||||
foreach (const Document::Include &incl, doc->resolvedIncludes()) {
|
||||
if (incl.line() == lineno) {
|
||||
link.targetFileName = incl.resolvedFileName();
|
||||
link.linkTextStart = beginOfToken + 1;
|
||||
link.linkTextEnd = endOfToken - 1;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tk.isNot(T_IDENTIFIER) && tk.kind() < T_FIRST_QT_KEYWORD && tk.kind() > T_LAST_KEYWORD)
|
||||
return link;
|
||||
|
||||
tc.setPosition(endOfToken);
|
||||
}
|
||||
|
||||
// Handle macro uses
|
||||
const Macro *macro = doc->findMacroDefinitionAt(line);
|
||||
if (macro) {
|
||||
QTextCursor macroCursor = m_cursor;
|
||||
const QByteArray name = CPPEditorWidget::identifierUnderCursor(¯oCursor).toLatin1();
|
||||
if (macro->name() == name)
|
||||
return link; //already on definition!
|
||||
} else {
|
||||
const Document::MacroUse *use = doc->findMacroUseAt(endOfToken - 1);
|
||||
if (use && use->macro().fileName() != CppModelManagerInterface::configurationFileName()) {
|
||||
const Macro ¯o = use->macro();
|
||||
link.targetFileName = macro.fileName();
|
||||
link.targetLine = macro.line();
|
||||
link.linkTextStart = use->begin();
|
||||
link.linkTextEnd = use->end();
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the last symbol up to the cursor position
|
||||
Scope *scope = doc->scopeAt(line, column);
|
||||
if (!scope)
|
||||
return link;
|
||||
|
||||
// Evaluate the type of the expression under the cursor
|
||||
ExpressionUnderCursor expressionUnderCursor;
|
||||
QString expression = expressionUnderCursor(tc);
|
||||
|
||||
for (int pos = tc.position();; ++pos) {
|
||||
const QChar ch = m_widget->document()->characterAt(pos);
|
||||
if (ch.isSpace())
|
||||
continue;
|
||||
if (ch == QLatin1Char('(') && !expression.isEmpty()) {
|
||||
tc.setPosition(pos);
|
||||
if (TextEditor::TextBlockUserData::findNextClosingParenthesis(&tc, true))
|
||||
expression.append(tc.selectedText());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TypeOfExpression typeOfExpression;
|
||||
typeOfExpression.init(doc, m_snapshot);
|
||||
// make possible to instantiate templates
|
||||
typeOfExpression.setExpandTemplates(true);
|
||||
const QList<LookupItem> resolvedSymbols =
|
||||
typeOfExpression.reference(expression.toUtf8(), scope, TypeOfExpression::Preprocess);
|
||||
|
||||
if (!resolvedSymbols.isEmpty()) {
|
||||
LookupItem result = skipForwardDeclarations(resolvedSymbols);
|
||||
|
||||
foreach (const LookupItem &r, resolvedSymbols) {
|
||||
if (Symbol *d = r.declaration()) {
|
||||
if (d->isDeclaration() || d->isFunction()) {
|
||||
const QString fileName = QString::fromUtf8(d->fileName(), d->fileNameLength());
|
||||
if (m_widget->editorDocument()->filePath() == fileName) {
|
||||
if (unsigned(lineNumber) == d->line()
|
||||
&& unsigned(positionInBlock) >= d->column()) { // TODO: check the end
|
||||
result = r; // take the symbol under cursor.
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (d->isUsingDeclaration()) {
|
||||
int tokenBeginLineNumber = 0, tokenBeginColumnNumber = 0;
|
||||
m_widget->convertPosition(beginOfToken, &tokenBeginLineNumber,
|
||||
&tokenBeginColumnNumber);
|
||||
if (unsigned(tokenBeginLineNumber) > d->line()
|
||||
|| (unsigned(tokenBeginLineNumber) == d->line()
|
||||
&& unsigned(tokenBeginColumnNumber) > d->column())) {
|
||||
result = r; // take the symbol under cursor.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Symbol *symbol = result.declaration()) {
|
||||
Symbol *def = 0;
|
||||
|
||||
if (m_resolveTarget) {
|
||||
Symbol *lastVisibleSymbol = doc->lastVisibleSymbolAt(line, column);
|
||||
|
||||
def = findDefinition(symbol, m_snapshot);
|
||||
|
||||
if (def == lastVisibleSymbol)
|
||||
def = 0; // jump to declaration then.
|
||||
|
||||
if (symbol->isForwardClassDeclaration())
|
||||
def = m_symbolFinder->findMatchingClassDeclaration(symbol, m_snapshot);
|
||||
}
|
||||
|
||||
link = m_widget->linkToSymbol(def ? def : symbol);
|
||||
link.linkTextStart = beginOfToken;
|
||||
link.linkTextEnd = endOfToken;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle macro uses
|
||||
QTextCursor macroCursor = m_cursor;
|
||||
const QByteArray name = CPPEditorWidget::identifierUnderCursor(¯oCursor).toLatin1();
|
||||
link = findMacroLink(name, m_document);
|
||||
if (link.hasValidTarget()) {
|
||||
link.linkTextStart = macroCursor.selectionStart();
|
||||
link.linkTextEnd = macroCursor.selectionEnd();
|
||||
return link;
|
||||
}
|
||||
|
||||
return Link();
|
||||
}
|
||||
|
||||
CPPEditorWidget::Link FollowSymbolUnderCursor::attemptFuncDeclDef(const QTextCursor &cursor)
|
||||
{
|
||||
m_snapshot.insert(m_document);
|
||||
snapshot.insert(document);
|
||||
|
||||
Link result;
|
||||
|
||||
QList<AST *> path = ASTPath(m_document)(cursor);
|
||||
QList<AST *> path = ASTPath(document)(cursor);
|
||||
|
||||
if (path.size() < 5)
|
||||
return result;
|
||||
@@ -434,21 +219,21 @@ CPPEditorWidget::Link FollowSymbolUnderCursor::attemptFuncDeclDef(const QTextCur
|
||||
Symbol *target = 0;
|
||||
if (FunctionDefinitionAST *funDef = declParent->asFunctionDefinition()) {
|
||||
QList<Declaration *> candidates =
|
||||
m_symbolFinder->findMatchingDeclaration(LookupContext(m_document, m_snapshot),
|
||||
symbolFinder->findMatchingDeclaration(LookupContext(document, snapshot),
|
||||
funDef->symbol);
|
||||
if (!candidates.isEmpty()) // TODO: improve disambiguation
|
||||
target = candidates.first();
|
||||
} else if (declParent->asSimpleDeclaration()) {
|
||||
target = m_symbolFinder->findMatchingDefinition(funcDecl->symbol, m_snapshot);
|
||||
target = symbolFinder->findMatchingDefinition(funcDecl->symbol, snapshot);
|
||||
}
|
||||
|
||||
if (target) {
|
||||
result = m_widget->linkToSymbol(target);
|
||||
result = widget->linkToSymbol(target);
|
||||
|
||||
unsigned startLine, startColumn, endLine, endColumn;
|
||||
m_document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine,
|
||||
document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine,
|
||||
&startColumn);
|
||||
m_document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine,
|
||||
document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine,
|
||||
&endColumn);
|
||||
|
||||
QTextDocument *textDocument = cursor.document();
|
||||
@@ -461,7 +246,7 @@ CPPEditorWidget::Link FollowSymbolUnderCursor::attemptFuncDeclDef(const QTextCur
|
||||
return result;
|
||||
}
|
||||
|
||||
Symbol *FollowSymbolUnderCursor::findDefinition(Symbol *symbol, const Snapshot &snapshot) const
|
||||
Symbol *findDefinition(Symbol *symbol, const Snapshot &snapshot, SymbolFinder *symbolFinder)
|
||||
{
|
||||
if (symbol->isFunction())
|
||||
return 0; // symbol is a function definition.
|
||||
@@ -469,5 +254,282 @@ Symbol *FollowSymbolUnderCursor::findDefinition(Symbol *symbol, const Snapshot &
|
||||
else if (!symbol->type()->isFunctionType())
|
||||
return 0; // not a function declaration
|
||||
|
||||
return m_symbolFinder->findMatchingDefinition(symbol, snapshot);
|
||||
return symbolFinder->findMatchingDefinition(symbol, snapshot);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
FollowSymbolUnderCursor::FollowSymbolUnderCursor(CPPEditorWidget *widget)
|
||||
: m_widget(widget)
|
||||
, m_virtualFunctionAssistProvider(new VirtualFunctionAssistProvider)
|
||||
{
|
||||
}
|
||||
|
||||
FollowSymbolUnderCursor::~FollowSymbolUnderCursor()
|
||||
{
|
||||
delete m_virtualFunctionAssistProvider;
|
||||
}
|
||||
|
||||
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 = 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 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(cursor, m_widget, snapshot, documentFromSemanticInfo,
|
||||
symbolFinder);
|
||||
if (link.hasValidLinkText())
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
int lineNumber = 0, positionInBlock = 0;
|
||||
m_widget->convertPosition(cursor.position(), &lineNumber, &positionInBlock);
|
||||
const unsigned line = lineNumber;
|
||||
const unsigned column = positionInBlock + 1;
|
||||
|
||||
// Try to find a signal or slot inside SIGNAL() or SLOT()
|
||||
int beginOfToken = 0;
|
||||
int endOfToken = 0;
|
||||
|
||||
SimpleLexer tokenize;
|
||||
tokenize.setQtMocRunEnabled(true);
|
||||
const QString blockText = cursor.block().text();
|
||||
const QList<Token> tokens = tokenize(blockText,
|
||||
BackwardsScanner::previousBlockState(cursor.block()));
|
||||
|
||||
bool recognizedQtMethod = false;
|
||||
|
||||
for (int i = 0; i < tokens.size(); ++i) {
|
||||
const Token &tk = tokens.at(i);
|
||||
|
||||
if (((unsigned) positionInBlock) >= tk.begin()
|
||||
&& ((unsigned) positionInBlock) <= tk.end()) {
|
||||
if (i >= 2 && tokens.at(i).is(T_IDENTIFIER) && tokens.at(i - 1).is(T_LPAREN)
|
||||
&& (tokens.at(i - 2).is(T_SIGNAL) || tokens.at(i - 2).is(T_SLOT))) {
|
||||
|
||||
// token[i] == T_IDENTIFIER
|
||||
// token[i + 1] == T_LPAREN
|
||||
// token[.....] == ....
|
||||
// token[i + n] == T_RPAREN
|
||||
|
||||
if (i + 1 < tokens.size() && tokens.at(i + 1).is(T_LPAREN)) {
|
||||
// skip matched parenthesis
|
||||
int j = i - 1;
|
||||
int depth = 0;
|
||||
|
||||
for (; j < tokens.size(); ++j) {
|
||||
if (tokens.at(j).is(T_LPAREN)) {
|
||||
++depth;
|
||||
} else if (tokens.at(j).is(T_RPAREN)) {
|
||||
if (!--depth)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < tokens.size()) {
|
||||
QTextBlock block = cursor.block();
|
||||
|
||||
beginOfToken = block.position() + tokens.at(i).begin();
|
||||
endOfToken = block.position() + tokens.at(i).end();
|
||||
|
||||
tc.setPosition(block.position() + tokens.at(j).end());
|
||||
recognizedQtMethod = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we prefer the doc from the snapshot with macros expanded.
|
||||
Document::Ptr doc = snapshot.document(m_widget->editorDocument()->filePath());
|
||||
if (!doc) {
|
||||
doc = documentFromSemanticInfo;
|
||||
if (!doc)
|
||||
return link;
|
||||
}
|
||||
|
||||
if (!recognizedQtMethod) {
|
||||
const QTextBlock block = tc.block();
|
||||
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,
|
||||
BackwardsScanner::previousBlockState(block), true);
|
||||
|
||||
beginOfToken = block.position() + tk.begin();
|
||||
endOfToken = block.position() + tk.end();
|
||||
|
||||
// Handle include directives
|
||||
if (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) {
|
||||
const unsigned lineno = cursor.blockNumber() + 1;
|
||||
foreach (const Document::Include &incl, doc->resolvedIncludes()) {
|
||||
if (incl.line() == lineno) {
|
||||
link.targetFileName = incl.resolvedFileName();
|
||||
link.linkTextStart = beginOfToken + 1;
|
||||
link.linkTextEnd = endOfToken - 1;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tk.isNot(T_IDENTIFIER) && tk.kind() < T_FIRST_QT_KEYWORD && tk.kind() > T_LAST_KEYWORD)
|
||||
return link;
|
||||
|
||||
tc.setPosition(endOfToken);
|
||||
}
|
||||
|
||||
// Handle macro uses
|
||||
const Macro *macro = doc->findMacroDefinitionAt(line);
|
||||
if (macro) {
|
||||
QTextCursor macroCursor = cursor;
|
||||
const QByteArray name = CPPEditorWidget::identifierUnderCursor(¯oCursor).toLatin1();
|
||||
if (macro->name() == name)
|
||||
return link; //already on definition!
|
||||
} else {
|
||||
const Document::MacroUse *use = doc->findMacroUseAt(endOfToken - 1);
|
||||
if (use && use->macro().fileName() != CppModelManagerInterface::configurationFileName()) {
|
||||
const Macro ¯o = use->macro();
|
||||
link.targetFileName = macro.fileName();
|
||||
link.targetLine = macro.line();
|
||||
link.linkTextStart = use->begin();
|
||||
link.linkTextEnd = use->end();
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the last symbol up to the cursor position
|
||||
Scope *scope = doc->scopeAt(line, column);
|
||||
if (!scope)
|
||||
return link;
|
||||
|
||||
// Evaluate the type of the expression under the cursor
|
||||
ExpressionUnderCursor expressionUnderCursor;
|
||||
QString expression = expressionUnderCursor(tc);
|
||||
|
||||
for (int pos = tc.position();; ++pos) {
|
||||
const QChar ch = m_widget->document()->characterAt(pos);
|
||||
if (ch.isSpace())
|
||||
continue;
|
||||
if (ch == QLatin1Char('(') && !expression.isEmpty()) {
|
||||
tc.setPosition(pos);
|
||||
if (TextEditor::TextBlockUserData::findNextClosingParenthesis(&tc, true))
|
||||
expression.append(tc.selectedText());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
TypeOfExpression typeOfExpression;
|
||||
typeOfExpression.init(doc, snapshot);
|
||||
// make possible to instantiate templates
|
||||
typeOfExpression.setExpandTemplates(true);
|
||||
const QList<LookupItem> resolvedSymbols =
|
||||
typeOfExpression.reference(expression.toUtf8(), scope, TypeOfExpression::Preprocess);
|
||||
|
||||
if (!resolvedSymbols.isEmpty()) {
|
||||
LookupItem result = skipForwardDeclarations(resolvedSymbols);
|
||||
|
||||
foreach (const LookupItem &r, resolvedSymbols) {
|
||||
if (Symbol *d = r.declaration()) {
|
||||
if (d->isDeclaration() || d->isFunction()) {
|
||||
const QString fileName = QString::fromUtf8(d->fileName(), d->fileNameLength());
|
||||
if (m_widget->editorDocument()->filePath() == fileName) {
|
||||
if (unsigned(lineNumber) == d->line()
|
||||
&& unsigned(positionInBlock) >= d->column()) { // TODO: check the end
|
||||
result = r; // take the symbol under cursor.
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (d->isUsingDeclaration()) {
|
||||
int tokenBeginLineNumber = 0, tokenBeginColumnNumber = 0;
|
||||
m_widget->convertPosition(beginOfToken, &tokenBeginLineNumber,
|
||||
&tokenBeginColumnNumber);
|
||||
if (unsigned(tokenBeginLineNumber) > d->line()
|
||||
|| (unsigned(tokenBeginLineNumber) == d->line()
|
||||
&& unsigned(tokenBeginColumnNumber) > d->column())) {
|
||||
result = r; // take the symbol under cursor.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Symbol *symbol = result.declaration()) {
|
||||
Symbol *def = 0;
|
||||
|
||||
// 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, snapshot, symbolFinder);
|
||||
|
||||
if (def == lastVisibleSymbol)
|
||||
def = 0; // jump to declaration then.
|
||||
|
||||
if (symbol->isForwardClassDeclaration())
|
||||
def = symbolFinder->findMatchingClassDeclaration(symbol, snapshot);
|
||||
}
|
||||
|
||||
link = m_widget->linkToSymbol(def ? def : symbol);
|
||||
link.linkTextStart = beginOfToken;
|
||||
link.linkTextEnd = endOfToken;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle macro uses
|
||||
QTextCursor macroCursor = cursor;
|
||||
const QByteArray name = CPPEditorWidget::identifierUnderCursor(¯oCursor).toLatin1();
|
||||
link = findMacroLink(name, documentFromSemanticInfo);
|
||||
if (link.hasValidTarget()) {
|
||||
link.linkTextStart = macroCursor.selectionStart();
|
||||
link.linkTextEnd = macroCursor.selectionEnd();
|
||||
return link;
|
||||
}
|
||||
|
||||
return Link();
|
||||
}
|
||||
|
||||
VirtualFunctionAssistProvider *FollowSymbolUnderCursor::virtualFunctionAssistProvider()
|
||||
{
|
||||
return m_virtualFunctionAssistProvider;
|
||||
}
|
||||
|
||||
void FollowSymbolUnderCursor::setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider)
|
||||
{
|
||||
m_virtualFunctionAssistProvider = provider;
|
||||
}
|
||||
|
@@ -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,
|
||||
FollowSymbolUnderCursor(CPPEditorWidget *widget);
|
||||
~FollowSymbolUnderCursor();
|
||||
|
||||
Link findLink(const QTextCursor &cursor, bool resolveTarget,
|
||||
const CPlusPlus::Snapshot &snapshot,
|
||||
const CPlusPlus::Document::Ptr &documentFromSemanticInfo,
|
||||
CppTools::SymbolFinder *symbolFinder);
|
||||
CppTools::SymbolFinder *symbolFinder, bool inNextSplit);
|
||||
|
||||
Link findLink();
|
||||
VirtualFunctionAssistProvider *virtualFunctionAssistProvider();
|
||||
void setVirtualFunctionAssistProvider(VirtualFunctionAssistProvider *provider);
|
||||
|
||||
private:
|
||||
Link attemptFuncDeclDef(const QTextCursor &cursor);
|
||||
CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol,
|
||||
const CPlusPlus::Snapshot &snapshot) const;
|
||||
|
||||
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
|
||||
|
324
src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp
Normal file
324
src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp
Normal file
@@ -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;
|
||||
}
|
80
src/plugins/cppeditor/cppvirtualfunctionassistprovider.h
Normal file
80
src/plugins/cppeditor/cppvirtualfunctionassistprovider.h
Normal file
@@ -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
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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())
|
||||
|
@@ -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:
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 *);
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -35,7 +35,8 @@ namespace TextEditor {
|
||||
enum AssistKind
|
||||
{
|
||||
Completion,
|
||||
QuickFix
|
||||
QuickFix,
|
||||
FollowSymbol
|
||||
};
|
||||
|
||||
enum AssistReason
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user