/*************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Qt Software Information (qt-info@nokia.com) ** ** ** Non-Open Source Usage ** ** Licensees may use this file in accordance with the Qt Beta Version ** License Agreement, Agreement version 2.2 provided with the Software or, ** alternatively, in accordance with the terms contained in a written ** agreement between you and Nokia. ** ** GNU General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU General ** Public License versions 2.0 or 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the packaging ** of this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and ** http://www.gnu.org/copyleft/gpl.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt GPL Exception ** version 1.2, included in the file GPL_EXCEPTION.txt in this package. ** ***************************************************************************/ ***************************************************************************/ #include "cppcodecompletion.h" #include "cppmodelmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace CPlusPlus; namespace CppTools { namespace Internal { class FunctionArgumentWidget : public QLabel { public: FunctionArgumentWidget(Core::ICore *core); void showFunctionHint(Function *functionSymbol); protected: bool eventFilter(QObject *obj, QEvent *e); private: void update(); void close(); void updateHintText(); int m_startpos; int m_currentarg; TextEditor::ITextEditor *m_editor; QFrame *m_popupFrame; Function *m_item; }; class ConvertToCompletionItem: protected NameVisitor { // The completion collector. CppCodeCompletion *_collector; // The completion item. TextEditor::CompletionItem _item; // The current symbol. Symbol *_symbol; // The pretty printer. Overview overview; public: ConvertToCompletionItem(CppCodeCompletion *collector) : _collector(collector), _item(0), _symbol(0) { } TextEditor::CompletionItem operator()(Symbol *symbol) { if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId()) return 0; TextEditor::CompletionItem previousItem = switchCompletionItem(0); Symbol *previousSymbol = switchSymbol(symbol); accept(symbol->identity()); if (_item) _item.m_data = QVariant::fromValue(symbol); (void) switchSymbol(previousSymbol); return switchCompletionItem(previousItem); } protected: Symbol *switchSymbol(Symbol *symbol) { Symbol *previousSymbol = _symbol; _symbol = symbol; return previousSymbol; } TextEditor::CompletionItem switchCompletionItem(TextEditor::CompletionItem item) { TextEditor::CompletionItem previousItem = _item; _item = item; return previousItem; } TextEditor::CompletionItem newCompletionItem(Name *name) { TextEditor::CompletionItem item(_collector); item.m_text = overview.prettyName(name); item.m_icon = _collector->iconForSymbol(_symbol); return item; } virtual void visit(NameId *name) { _item = newCompletionItem(name); } virtual void visit(TemplateNameId *name) { _item = newCompletionItem(name); _item.m_text = QLatin1String(name->identifier()->chars()); } virtual void visit(DestructorNameId *name) { _item = newCompletionItem(name); } virtual void visit(OperatorNameId *name) { _item = newCompletionItem(name); } virtual void visit(ConversionNameId *name) { _item = newCompletionItem(name); } virtual void visit(QualifiedNameId *name) { _item = newCompletionItem(name->unqualifiedNameId()); } }; } // namespace Internal } // namespace CppTools using namespace CppTools::Internal; FunctionArgumentWidget::FunctionArgumentWidget(Core::ICore *core) : m_item(0) { QObject *editorObject = core->editorManager()->currentEditor(); m_editor = qobject_cast(editorObject); m_popupFrame = new QFrame(0, Qt::ToolTip|Qt::WindowStaysOnTopHint); m_popupFrame->setFocusPolicy(Qt::NoFocus); m_popupFrame->setAttribute(Qt::WA_DeleteOnClose); setFrameStyle(QFrame::Box); setFrameShadow(QFrame::Plain); setParent(m_popupFrame); setFocusPolicy(Qt::NoFocus); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(this); layout->setMargin(0); m_popupFrame->setLayout(layout); QPalette pal = palette(); setAutoFillBackground(true); pal.setColor(QPalette::Background, QColor(255, 255, 220)); setPalette(pal); setTextFormat(Qt::RichText); setMargin(1); } void FunctionArgumentWidget::showFunctionHint(Function *functionSymbol) { m_item = functionSymbol; m_startpos = m_editor->position(); // update the text m_currentarg = -1; update(); QPoint pos = m_editor->cursorRect().topLeft(); pos.setY(pos.y() - sizeHint().height()); m_popupFrame->move(pos); m_popupFrame->show(); QCoreApplication::instance()->installEventFilter(this); } void FunctionArgumentWidget::update() { int curpos = m_editor->position(); if (curpos < m_startpos) { close(); return; } QString str = m_editor->textAt(m_startpos, curpos - m_startpos); int argnr = 0; int parcount = 0; SimpleLexer tokenize; QList tokens = tokenize(str); for (int i = 0; i < tokens.count(); ++i) { const SimpleToken &tk = tokens.at(i); if (tk.is(T_LPAREN)) ++parcount; else if (tk.is(T_RPAREN)) --parcount; else if (! parcount && tk.is(T_COMMA)) ++argnr; } if (m_currentarg != argnr) { m_currentarg = argnr; updateHintText(); } if (parcount < 0) close(); } bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e) { switch (e->type()) { case QEvent::KeyRelease: { if (static_cast(e)->key() == Qt::Key_Escape) { close(); return false; } update(); break; } case QEvent::WindowDeactivate: case QEvent::Leave: case QEvent::FocusOut: { if (obj != m_editor->widget()) break; } case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::Wheel: close(); break; default: break; } return false; } void FunctionArgumentWidget::close() { m_popupFrame->close(); } void FunctionArgumentWidget::updateHintText() { Overview overview; overview.setShowReturnTypes(true); overview.setShowArgumentNames(true); overview.setMarkArgument(m_currentarg + 1); QString text = overview(m_item->type(), m_item->name()); setText(text); } CppCodeCompletion::CppCodeCompletion(CppModelManager *manager, Core::ICore *core) : ICompletionCollector(manager), m_core(core), m_manager(manager), m_forcedCompletion(false), m_completionOperator(T_EOF_SYMBOL) { } QIcon CppCodeCompletion::iconForSymbol(Symbol *symbol) const { return m_icons.iconForSymbol(symbol); } /* Searches beckward for an access operator. */ static int startOfOperator(TextEditor::ITextEditable *editor, int pos, unsigned *kind, bool wantFunctionCall) { const QChar ch = pos > -1 ? editor->characterAt(pos - 1) : QChar(); const QChar ch2 = pos > 0 ? editor->characterAt(pos - 2) : QChar(); const QChar ch3 = pos > 1 ? editor->characterAt(pos - 3) : QChar(); int start = pos; if (ch2 != QLatin1Char('.') && ch == QLatin1Char('.')) { if (kind) *kind = T_DOT; --start; } else if (wantFunctionCall && ch == QLatin1Char('(')) { if (kind) *kind = T_LPAREN; --start; } else if (ch2 == QLatin1Char(':') && ch == QLatin1Char(':')) { if (kind) *kind = T_COLON_COLON; start -= 2; } else if (ch2 == QLatin1Char('-') && ch == QLatin1Char('>')) { if (kind) *kind = T_ARROW; start -= 2; } else if (ch2 == QLatin1Char('.') && ch == QLatin1Char('*')) { if (kind) *kind = T_DOT_STAR; start -= 2; } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>') && ch == QLatin1Char('*')) { if (kind) *kind = T_ARROW_STAR; start -= 3; } if (start != pos) { TextEditor::BaseTextEditor *edit = qobject_cast(editor->widget()); QTextCursor tc(edit->textCursor()); tc.setPosition(pos); static CPlusPlus::TokenUnderCursor tokenUnderCursor; const SimpleToken tk = tokenUnderCursor(tc); if (tk.is(T_COMMENT) || tk.isLiteral()) { if (kind) *kind = T_EOF_SYMBOL; return pos; } } return start; } bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor) { if (! m_manager->isCppEditor(editor)) // ### remove me return false; const int pos = editor->position(); if (startOfOperator(editor, pos, /*token =*/ 0, /*want function call=*/ true) != pos) return true; return false; } int CppCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) { TextEditor::BaseTextEditor *edit = qobject_cast(editor->widget()); if (! edit) return -1; m_editor = editor; m_startPosition = findStartOfName(editor); m_completionOperator = T_EOF_SYMBOL; int endOfExpression = m_startPosition; // Skip whitespace preceding this position while (editor->characterAt(endOfExpression - 1).isSpace()) --endOfExpression; endOfExpression = startOfOperator(editor, endOfExpression, &m_completionOperator, /*want function call =*/ editor->position() == endOfExpression); Core::IFile *file = editor->file(); QString fileName = file->fileName(); int line = 0, column = 0; edit->convertPosition(editor->position(), &line, &column); // qDebug() << "line:" << line << "column:" << column; ExpressionUnderCursor expressionUnderCursor; QString expression; if (m_completionOperator) { QTextCursor tc(edit->document()); tc.setPosition(endOfExpression); expression = expressionUnderCursor(tc); if (m_completionOperator == T_LPAREN) { if (expression.endsWith(QLatin1String("SIGNAL"))) m_completionOperator = T_SIGNAL; else if (expression.endsWith(QLatin1String("SLOT"))) m_completionOperator = T_SLOT; } } //if (! expression.isEmpty()) //qDebug() << "***** expression:" << expression; if (Document::Ptr thisDocument = m_manager->document(fileName)) { Symbol *symbol = thisDocument->findSymbolAt(line, column); typeOfExpression.setDocuments(m_manager->documents()); QList resolvedTypes = typeOfExpression(expression, thisDocument, symbol); LookupContext context = typeOfExpression.lookupContext(); if (!typeOfExpression.expressionAST() && (! m_completionOperator || m_completionOperator == T_COLON_COLON)) { if (!m_completionOperator) { addKeywords(); addMacros(context); } const QList scopes = context.expand(context.visibleScopes()); foreach (Scope *scope, scopes) { for (unsigned i = 0; i < scope->symbolCount(); ++i) { addCompletionItem(scope->symbolAt(i)); } } return m_startPosition; } // qDebug() << "found" << resolvedTypes.count() << "symbols for expression:" << expression; if (resolvedTypes.isEmpty() && (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)) { // Apply signal/slot completion on 'this' expression = QLatin1String("this"); resolvedTypes = typeOfExpression(expression, thisDocument, symbol); context = typeOfExpression.lookupContext(); } if (! resolvedTypes.isEmpty()) { FullySpecifiedType exprTy = resolvedTypes.first().first; if (exprTy->isReferenceType()) exprTy = exprTy->asReferenceType()->elementType(); if (m_completionOperator == T_LPAREN && completeFunction(exprTy, resolvedTypes, context)) { return m_startPosition; } if ((m_completionOperator == T_DOT || m_completionOperator == T_ARROW) && completeMember(exprTy, resolvedTypes, context)) { return m_startPosition; } else if (m_completionOperator == T_COLON_COLON && completeScope(exprTy, resolvedTypes, context)) { return m_startPosition; } else if (m_completionOperator == T_SIGNAL && completeSignal(exprTy, resolvedTypes, context)) { return m_startPosition; } else if (m_completionOperator == T_SLOT && completeSlot(exprTy, resolvedTypes, context)) { return m_startPosition; } } } // nothing to do. return -1; } bool CppCodeCompletion::completeFunction(FullySpecifiedType exprTy, const QList &resolvedTypes, const LookupContext &) { ConvertToCompletionItem toCompletionItem(this); Overview o; o.setShowReturnTypes(true); o.setShowArgumentNames(true); if (Class *klass = exprTy->asClass()) { for (unsigned i = 0; i < klass->memberCount(); ++i) { Symbol *member = klass->memberAt(i); if (! member->type()->isFunction()) continue; else if (! member->identity()) continue; else if (! member->identity()->isEqualTo(klass->identity())) continue; if (TextEditor::CompletionItem item = toCompletionItem(member)) { item.m_text = o(member->type(), member->name()); m_completions.append(item); } } } else { QSet signatures; foreach (TypeOfExpression::Result p, resolvedTypes) { FullySpecifiedType ty = p.first; if (Function *fun = ty->asFunction()) { if (TextEditor::CompletionItem item = toCompletionItem(fun)) { QString signature; signature += overview.prettyName(fun->name()); signature += overview.prettyType(fun->type()); if (signatures.contains(signature)) continue; signatures.insert(signature); item.m_text = o(ty, fun->name()); m_completions.append(item); } } } } return ! m_completions.isEmpty(); } bool CppCodeCompletion::completeMember(FullySpecifiedType, const QList &results, const LookupContext &context) { Q_ASSERT(! results.isEmpty()); QList classObjectCandidates; TypeOfExpression::Result p = results.first(); if (m_completionOperator == T_ARROW) { FullySpecifiedType ty = p.first; if (ReferenceType *refTy = ty->asReferenceType()) ty = refTy->elementType(); if (NamedType *namedTy = ty->asNamedType()) { ResolveExpression resolveExpression(context); Name *className = namedTy->name(); const QList candidates = context.resolveClass(className, context.visibleScopes(p)); foreach (Symbol *classObject, candidates) { const QList overloads = resolveExpression.resolveArrowOperator(p, namedTy, classObject->asClass()); foreach (TypeOfExpression::Result r, overloads) { FullySpecifiedType ty = r.first; Function *funTy = ty->asFunction(); if (! funTy) continue; ty = funTy->returnType(); if (ReferenceType *refTy = ty->asReferenceType()) ty = refTy->elementType(); if (PointerType *ptrTy = ty->asPointerType()) { if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { const QList classes = context.resolveClass(namedTy->name(), context.visibleScopes(p)); foreach (Symbol *c, classes) { if (! classObjectCandidates.contains(c)) classObjectCandidates.append(c); } } } } } } else if (PointerType *ptrTy = ty->asPointerType()) { if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { const QList classes = context.resolveClass(namedTy->name(), context.visibleScopes(p)); foreach (Symbol *c, classes) { if (! classObjectCandidates.contains(c)) classObjectCandidates.append(c); } } } } else if (m_completionOperator == T_DOT) { FullySpecifiedType ty = p.first; if (ReferenceType *refTy = ty->asReferenceType()) ty = refTy->elementType(); NamedType *namedTy = 0; if (PointerType *ptrTy = ty->asPointerType()) { // Replace . with -> int length = m_editor->position() - m_startPosition + 1; m_editor->setCurPos(m_startPosition - 1); m_editor->replace(length, QLatin1String("->")); m_startPosition++; namedTy = ptrTy->elementType()->asNamedType(); } else { namedTy = ty->asNamedType(); if (! namedTy) { Function *fun = ty->asFunction(); if (fun && (fun->scope()->isBlockScope() || fun->scope()->isNamespaceScope())) namedTy = fun->returnType()->asNamedType(); } } if (namedTy) { const QList classes = context.resolveClass(namedTy->name(), context.visibleScopes(p)); foreach (Symbol *c, classes) { if (! classObjectCandidates.contains(c)) classObjectCandidates.append(c); } } } completeClass(classObjectCandidates, context, /*static lookup = */ false); if (! m_completions.isEmpty()) return true; return false; } bool CppCodeCompletion::completeScope(FullySpecifiedType exprTy, const QList &resolvedTypes, const LookupContext &context) { // Search for a class or a namespace. foreach (TypeOfExpression::Result p, resolvedTypes) { if (p.first->isClass() || p.first->isNamespace()) { exprTy = p.first; break; } } if (exprTy->asNamespace()) { QList candidates; foreach (TypeOfExpression::Result p, resolvedTypes) { if (Namespace *ns = p.first->asNamespace()) candidates.append(ns); } completeNamespace(candidates, context); } else if (exprTy->isClass()) { QList candidates; foreach (TypeOfExpression::Result p, resolvedTypes) { if (Class *k = p.first->asClass()) candidates.append(k); } completeClass(candidates, context); } return ! m_completions.isEmpty(); } void CppCodeCompletion::addKeywords() { // keyword completion items. for (int i = T_FIRST_KEYWORD; i < T_FIRST_QT_KEYWORD; ++i) { TextEditor::CompletionItem item(this); item.m_text = QLatin1String(Token::name(i)); item.m_icon = m_icons.keywordIcon(); m_completions.append(item); } } void CppCodeCompletion::addMacros(const LookupContext &context) { // macro completion items. QSet macroNames; QSet processed; QList todo; todo.append(context.thisDocument()->fileName()); while (! todo.isEmpty()) { QString fn = todo.last(); todo.removeLast(); if (processed.contains(fn)) continue; processed.insert(fn); if (Document::Ptr doc = context.document(fn)) { macroNames += doc->macroNames(); todo += doc->includedFiles(); } } foreach (const QByteArray macroName, macroNames) { TextEditor::CompletionItem item(this); item.m_text = QString::fromLatin1(macroName.constData(), macroName.length()); item.m_icon = m_icons.macroIcon(); m_completions.append(item); } } void CppCodeCompletion::addCompletionItem(Symbol *symbol) { ConvertToCompletionItem toCompletionItem(this); if (TextEditor::CompletionItem item = toCompletionItem(symbol)) m_completions.append(item); } void CppCodeCompletion::completeNamespace(const QList &candidates, const LookupContext &context) { QList todo; QList visibleScopes = context.visibleScopes(); foreach (Symbol *candidate, candidates) { if (Namespace *ns = candidate->asNamespace()) context.expand(ns->members(), visibleScopes, &todo); } foreach (Scope *scope, todo) { addCompletionItem(scope->owner()); for (unsigned i = 0; i < scope->symbolCount(); ++i) { addCompletionItem(scope->symbolAt(i)); } } } void CppCodeCompletion::completeClass(const QList &candidates, const LookupContext &context, bool staticLookup) { if (candidates.isEmpty()) return; Class *klass = candidates.first()->asClass(); QList todo; context.expand(klass->members(), context.visibleScopes(), &todo); foreach (Scope *scope, todo) { addCompletionItem(scope->owner()); for (unsigned i = 0; i < scope->symbolCount(); ++i) { Symbol *symbol = scope->symbolAt(i); if (symbol->type().isFriend()) continue; else if (! staticLookup && (symbol->isTypedef() || symbol->isEnum() || symbol->isClass())) continue; addCompletionItem(symbol); } } } bool CppCodeCompletion::completeQtMethod(CPlusPlus::FullySpecifiedType, const QList &results, const LookupContext &context, bool wantSignals) { if (results.isEmpty()) return false; ConvertToCompletionItem toCompletionItem(this); Overview o; o.setShowReturnTypes(false); o.setShowArgumentNames(false); o.setShowFunctionSignatures(true); QSet signatures; foreach (TypeOfExpression::Result p, results) { FullySpecifiedType ty = p.first; if (ReferenceType *refTy = ty->asReferenceType()) ty = refTy->elementType(); if (PointerType *ptrTy = ty->asPointerType()) ty = ptrTy->elementType(); else continue; // not a pointer or a reference to a pointer. NamedType *namedTy = ty->asNamedType(); if (! namedTy) // not a class name. continue; const QList visibleScopes = context.visibleScopes(p); const QList classObjects = context.resolveClass(namedTy->name(), visibleScopes); if (classObjects.isEmpty()) continue; Class *klass = classObjects.first()->asClass(); QList todo; context.expand(klass->members(), visibleScopes, &todo); foreach (Scope *scope, todo) { if (! scope->isClassScope()) continue; for (unsigned i = 0; i < scope->symbolCount(); ++i) { Symbol *member = scope->symbolAt(i); Function *fun = member->type()->asFunction(); if (! fun) continue; if (wantSignals && ! fun->isSignal()) continue; else if (! wantSignals && ! fun->isSlot()) continue; if (TextEditor::CompletionItem item = toCompletionItem(fun)) { unsigned count = fun->argumentCount(); while (true) { TextEditor::CompletionItem i = item; QString signature; signature += overview.prettyName(fun->name()); signature += QLatin1Char('('); for (unsigned i = 0; i < count; ++i) { Symbol *arg = fun->argumentAt(i); if (i != 0) signature += QLatin1Char(','); signature += o.prettyType(arg->type()); } signature += QLatin1Char(')'); const QByteArray normalized = QMetaObject::normalizedSignature(signature.toLatin1()); signature = QString::fromLatin1(normalized, normalized.size()); if (! signatures.contains(signature)) { signatures.insert(signature); i.m_text = signature; // fix the completion item. m_completions.append(i); } if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer()) --count; else break; } } } } } return ! m_completions.isEmpty(); } void CppCodeCompletion::completions(QList *completions) { const int length = m_editor->position() - m_startPosition; if (length == 0) *completions = m_completions; else if (length > 0) { const QString key = m_editor->textAt(m_startPosition, length); if (m_completionOperator != T_LPAREN) { /* * This code builds a regular expression in order to more intelligently match * camel-case style. This means upper-case characters will be rewritten as follows: * * A => [a-z0-9_]*A (for any but the first capital letter) * * Meaning it allows any sequence of lower-case characters to preceed an * upper-case character. So for example gAC matches getActionController. * * The match is case-sensitive as soon as at least one upper-case character is * present. */ QString keyRegExp; keyRegExp += QLatin1Char('^'); bool first = true; Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; foreach (const QChar &c, key) { if (c.isLower()) { keyRegExp.append(c); } else if (c.isUpper()) { sensitivity = Qt::CaseSensitive; if (!first) { keyRegExp.append("[a-z0-9_]*"); } keyRegExp.append(c); } else { keyRegExp.append(QRegExp::escape(c)); } first = false; } const QRegExp regExp(keyRegExp, sensitivity); foreach (TextEditor::CompletionItem item, m_completions) { if (regExp.indexIn(item.m_text) == 0) { item.m_relevance = (key.length() > 0 && item.m_text.startsWith(key, Qt::CaseInsensitive)) ? 1 : 0; (*completions) << item; } } } else if (m_completionOperator == T_LPAREN || m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { foreach (TextEditor::CompletionItem item, m_completions) { if (item.m_text.startsWith(key, Qt::CaseInsensitive)) { (*completions) << item; } } } } } void CppCodeCompletion::complete(const TextEditor::CompletionItem &item) { Symbol *symbol = 0; if (item.m_data.isValid()) symbol = item.m_data.value(); // qDebug() << "*** complete symbol:" << symbol->fileName() << symbol->line(); if (m_completionOperator == T_LPAREN) { if (symbol) { Function *function = symbol->type()->asFunction(); Q_ASSERT(function != 0); m_functionArgumentWidget = new FunctionArgumentWidget(m_core); m_functionArgumentWidget->showFunctionHint(function); } } else if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { QString toInsert = item.m_text; toInsert += QLatin1Char(')'); // Insert the remainder of the name int length = m_editor->position() - m_startPosition; m_editor->setCurPos(m_startPosition); m_editor->replace(length, toInsert); } else { QString toInsert = item.m_text; //qDebug() << "current symbol:" << overview.prettyName(symbol->name()) //<< overview.prettyType(symbol->type()); if (symbol) { if (Function *function = symbol->type()->asFunction()) { // If the member is a function, automatically place the opening parenthesis, // except when it might take template parameters. if (!function->returnType().isValid() && (function->identity() && !function->identity()->isDestructorNameId())) { // Don't insert any magic, since the user might have just wanted to select the class } else if (function->templateParameterCount() != 0) { // If there are no arguments, then we need the template specification if (function->argumentCount() == 0) { toInsert.append(QLatin1Char('<')); } } else { toInsert.append(QLatin1Char('(')); // If the function takes no arguments, automatically place the closing parenthesis if (function->argumentCount() == 0 || (function->argumentCount() == 1 && function->argumentAt(0)->type()->isVoidType())) { toInsert.append(QLatin1Char(')')); // If the function doesn't return anything, automatically place the semicolon, // unless we're doing a scope completion (then it might be function definition). if (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON) { toInsert.append(QLatin1Char(';')); } } } } } // Insert the remainder of the name int length = m_editor->position() - m_startPosition; m_editor->setCurPos(m_startPosition); m_editor->replace(length, toInsert); } } bool CppCodeCompletion::partiallyComplete(const QList &completionItems) { if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { return false; } else if (completionItems.count() == 1) { complete(completionItems.first()); return true; } else if (m_completionOperator != T_LPAREN) { // Compute common prefix QString firstKey = completionItems.first().m_text; QString lastKey = completionItems.last().m_text; const int length = qMin(firstKey.length(), lastKey.length()); firstKey.truncate(length); lastKey.truncate(length); while (firstKey != lastKey) { firstKey.chop(1); lastKey.chop(1); } int typedLength = m_editor->position() - m_startPosition; if (!firstKey.isEmpty() && firstKey.length() > typedLength) { m_editor->setCurPos(m_startPosition); m_editor->replace(typedLength, firstKey); } } return false; } void CppCodeCompletion::cleanup() { m_completions.clear(); } int CppCodeCompletion::findStartOfName(const TextEditor::ITextEditor *editor) { int pos = editor->position(); QChar chr; // Skip to the start of a name do { chr = editor->characterAt(--pos); } while (chr.isLetterOrNumber() || chr == QLatin1Char('_')); return pos + 1; }