1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "cppmodelmanager.h"
34 #include "cppcompletionassist.h"
35 #include "cppdoxygen.h"
36 #include "cppmodelmanager.h"
37 #include "cpptoolsconstants.h"
41 #include <ASTVisitor.h>
42 #include <CoreTypes.h>
45 #include <NameVisitor.h>
47 #include <SymbolVisitor.h>
49 #include <TranslationUnit.h>
50 #include <CppRewriter.h>
52 #include <cplusplus/ResolveExpression.h>
53 #include <cplusplus/MatchingText.h>
54 #include <cplusplus/Overview.h>
55 #include <cplusplus/ExpressionUnderCursor.h>
56 #include <cplusplus/BackwardsScanner.h>
57 #include <cplusplus/LookupContext.h>
59 #include <coreplugin/ifile.h>
60 #include <coreplugin/icore.h>
61 #include <coreplugin/mimedatabase.h>
62 #include <cppeditor/cppeditorconstants.h>
63 #include <texteditor/codeassist/basicproposalitem.h>
64 #include <texteditor/codeassist/basicproposalitemlistmodel.h>
65 #include <texteditor/codeassist/genericproposal.h>
66 #include <texteditor/codeassist/ifunctionhintproposalmodel.h>
67 #include <texteditor/codeassist/functionhintproposal.h>
68 #include <texteditor/convenience.h>
69 #include <texteditor/snippets/snippet.h>
70 #include <texteditor/texteditorsettings.h>
71 #include <texteditor/completionsettings.h>
73 #include <QtCore/QLatin1String>
74 #include <QtGui/QTextCursor>
75 #include <QtGui/QTextDocument>
76 #include <QtGui/QIcon>
78 using namespace CPlusPlus;
79 using namespace CppEditor;
80 using namespace CppTools;
81 using namespace Internal;
82 using namespace TextEditor;
86 int activationSequenceChar(const QChar &ch,
90 bool wantFunctionCall)
92 int referencePosition = 0;
93 int completionKind = T_EOF_SYMBOL;
94 switch (ch.toLatin1()) {
96 if (ch2 != QLatin1Char('.')) {
97 completionKind = T_DOT;
98 referencePosition = 1;
102 completionKind = T_COMMA;
103 referencePosition = 1;
106 if (wantFunctionCall) {
107 completionKind = T_LPAREN;
108 referencePosition = 1;
112 if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) {
113 completionKind = T_COLON_COLON;
114 referencePosition = 2;
118 if (ch2 == QLatin1Char('-')) {
119 completionKind = T_ARROW;
120 referencePosition = 2;
124 if (ch2 == QLatin1Char('.')) {
125 completionKind = T_DOT_STAR;
126 referencePosition = 2;
127 } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>')) {
128 completionKind = T_ARROW_STAR;
129 referencePosition = 3;
134 if (ch2.isNull() || ch2.isSpace()) {
135 completionKind = T_DOXY_COMMENT;
136 referencePosition = 1;
140 completionKind = T_ANGLE_STRING_LITERAL;
141 referencePosition = 1;
144 completionKind = T_STRING_LITERAL;
145 referencePosition = 1;
148 completionKind = T_SLASH;
149 referencePosition = 1;
152 completionKind = T_POUND;
153 referencePosition = 1;
158 *kind = completionKind;
160 return referencePosition;
168 struct CompleteFunctionDeclaration
170 explicit CompleteFunctionDeclaration(Function *f = 0)
177 // ----------------------
178 // CppAssistProposalModel
179 // ----------------------
180 class CppAssistProposalModel : public TextEditor::BasicProposalItemListModel
183 CppAssistProposalModel()
184 : TextEditor::BasicProposalItemListModel()
186 , m_completionOperator(T_EOF_SYMBOL)
187 , m_replaceDotForArrow(false)
188 , m_typeOfExpression(new TypeOfExpression)
191 virtual bool isSortable() const { return m_sortable; }
192 virtual IAssistProposalItem *proposalItem(int index) const;
195 unsigned m_completionOperator;
196 bool m_replaceDotForArrow;
197 QSharedPointer<TypeOfExpression> m_typeOfExpression;
200 // ---------------------
201 // CppAssistProposalItem
202 // ---------------------
203 class CppAssistProposalItem : public TextEditor::BasicProposalItem
206 CppAssistProposalItem() :
207 m_isOverloaded(false) {}
209 virtual bool prematurelyApplies(const QChar &c) const;
210 virtual void applyContextualContent(TextEditor::BaseTextEditor *editor,
211 int basePosition) const;
213 bool isOverloaded() const { return m_isOverloaded; }
214 void markAsOverloaded() { m_isOverloaded = true; }
215 void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; }
216 void keepTypeOfExpression(const QSharedPointer<TypeOfExpression> &typeOfExp)
217 { m_typeOfExpression = typeOfExp; }
221 unsigned m_completionOperator;
222 mutable QChar m_typedChar;
223 QSharedPointer<TypeOfExpression> m_typeOfExpression;
229 Q_DECLARE_METATYPE(CppTools::Internal::CompleteFunctionDeclaration)
231 IAssistProposalItem *CppAssistProposalModel::proposalItem(int index) const
233 BasicProposalItem *item =
234 static_cast<BasicProposalItem *>(BasicProposalItemListModel::proposalItem(index));
235 if (!item->data().canConvert<QString>()) {
236 CppAssistProposalItem *cppItem = static_cast<CppAssistProposalItem *>(item);
237 cppItem->keepCompletionOperator(m_completionOperator);
238 cppItem->keepTypeOfExpression(m_typeOfExpression);
243 bool CppAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
245 if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
246 if (typedChar == QLatin1Char('(') || typedChar == QLatin1Char(',')) {
247 m_typedChar = typedChar;
250 } else if (m_completionOperator == T_STRING_LITERAL
251 || m_completionOperator == T_ANGLE_STRING_LITERAL) {
252 if (typedChar == QLatin1Char('/') && text().endsWith(QLatin1Char('/'))) {
253 m_typedChar = typedChar;
256 } else if (data().value<Symbol *>()) {
257 if (typedChar == QLatin1Char(':')
258 || typedChar == QLatin1Char(';')
259 || typedChar == QLatin1Char('.')
260 || typedChar == QLatin1Char(',')
261 || typedChar == QLatin1Char('(')) {
262 m_typedChar = typedChar;
265 } else if (data().canConvert<CompleteFunctionDeclaration>()) {
266 if (typedChar == QLatin1Char('(')) {
267 m_typedChar = typedChar;
275 void CppAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor *editor,
276 int basePosition) const
280 if (data().isValid())
281 symbol = data().value<Symbol *>();
286 int cursorOffset = 0;
288 bool autoParenthesesEnabled = true;
290 if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
292 extraChars += QLatin1Char(')');
294 if (m_typedChar == QLatin1Char('(')) // Eat the opening parenthesis
295 m_typedChar = QChar();
296 } else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
298 if (!toInsert.endsWith(QLatin1Char('/'))) {
299 extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
301 if (m_typedChar == QLatin1Char('/')) // Eat the slash
302 m_typedChar = QChar();
307 const CompletionSettings &completionSettings =
308 TextEditorSettings::instance()->completionSettings();
309 const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;
311 if (autoInsertBrackets && symbol && symbol->type()) {
312 if (Function *function = symbol->type()->asFunctionType()) {
313 // If the member is a function, automatically place the opening parenthesis,
314 // except when it might take template parameters.
315 if (! function->hasReturnType() && (function->unqualifiedName() && !function->unqualifiedName()->isDestructorNameId())) {
316 // Don't insert any magic, since the user might have just wanted to select the class
320 } else if (function->templateParameterCount() != 0 && typedChar != QLatin1Char('(')) {
321 // If there are no arguments, then we need the template specification
322 if (function->argumentCount() == 0) {
323 extraChars += QLatin1Char('<');
326 } else if (! function->isAmbiguous()) {
327 // When the user typed the opening parenthesis, he'll likely also type the closing one,
328 // in which case it would be annoying if we put the cursor after the already automatically
329 // inserted closing parenthesis.
330 const bool skipClosingParenthesis = m_typedChar != QLatin1Char('(');
332 if (completionSettings.m_spaceAfterFunctionName)
333 extraChars += QLatin1Char(' ');
334 extraChars += QLatin1Char('(');
335 if (m_typedChar == QLatin1Char('('))
336 m_typedChar = QChar();
338 // If the function doesn't return anything, automatically place the semicolon,
339 // unless we're doing a scope completion (then it might be function definition).
340 const QChar characterAtCursor = editor->characterAt(editor->position());
341 bool endWithSemicolon = m_typedChar == QLatin1Char(';')
342 || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON);
343 const QChar semicolon = m_typedChar.isNull() ? QLatin1Char(';') : m_typedChar;
345 if (endWithSemicolon && characterAtCursor == semicolon) {
346 endWithSemicolon = false;
347 m_typedChar = QChar();
350 // If the function takes no arguments, automatically place the closing parenthesis
351 if (!isOverloaded() && ! function->hasArguments() && skipClosingParenthesis) {
352 extraChars += QLatin1Char(')');
353 if (endWithSemicolon) {
354 extraChars += semicolon;
355 m_typedChar = QChar();
357 } else if (autoParenthesesEnabled) {
358 const QChar lookAhead = editor->characterAt(editor->position() + 1);
359 if (MatchingText::shouldInsertMatchingText(lookAhead)) {
360 extraChars += QLatin1Char(')');
362 if (endWithSemicolon) {
363 extraChars += semicolon;
365 m_typedChar = QChar();
368 // TODO: When an opening parenthesis exists, the "semicolon" should really be
369 // inserted after the matching closing parenthesis.
375 if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) {
376 if (m_typedChar == QLatin1Char('('))
377 m_typedChar = QChar();
379 // everything from the closing parenthesis on are extra chars, to
380 // make sure an auto-inserted ")" gets replaced by ") const" if necessary
381 int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
382 extraChars = toInsert.mid(closingParen);
383 toInsert.truncate(closingParen);
387 // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
388 if (!m_typedChar.isNull()) {
389 extraChars += m_typedChar;
390 if (cursorOffset != 0)
394 // Avoid inserting characters that are already there
395 const int endsPosition = editor->position(TextEditor::ITextEditor::EndOfLine);
396 const QString text = editor->textAt(editor->position(), endsPosition - editor->position());
398 if (!text.isEmpty()) {
399 // Calculate the exist length in front of the extra chars
400 existLength = toInsert.length() - (editor->position() - basePosition);
401 while (!text.startsWith(toInsert.right(existLength))) {
402 if (--existLength == 0)
406 for (int i = 0; i < extraChars.length(); ++i) {
407 const QChar a = extraChars.at(i);
408 const QChar b = editor->characterAt(editor->position() + i + existLength);
415 toInsert += extraChars;
417 // Insert the remainder of the name
418 const int length = editor->position() - basePosition + existLength + extraLength;
419 editor->setCursorPosition(basePosition);
420 editor->replace(length, toInsert);
422 editor->setCursorPosition(editor->position() + cursorOffset);
425 // --------------------
426 // CppFunctionHintModel
427 // --------------------
428 class CppFunctionHintModel : public TextEditor::IFunctionHintProposalModel
431 CppFunctionHintModel(QList<Function *> functionSymbols,
432 const QSharedPointer<TypeOfExpression> &typeOfExp)
433 : m_functionSymbols(functionSymbols)
435 , m_typeOfExpression(typeOfExp)
438 virtual void reset() {}
439 virtual int size() const { return m_functionSymbols.size(); }
440 virtual QString text(int index) const;
441 virtual int activeArgument(const QString &prefix) const;
444 QList<Function *> m_functionSymbols;
445 mutable int m_currentArg;
446 QSharedPointer<TypeOfExpression> m_typeOfExpression;
449 QString CppFunctionHintModel::text(int index) const
452 overview.setShowReturnTypes(true);
453 overview.setShowArgumentNames(true);
454 overview.setMarkedArgument(m_currentArg + 1);
455 Function *f = m_functionSymbols.at(index);
457 const QString prettyMethod = overview(f->type(), f->name());
458 const int begin = overview.markedArgumentBegin();
459 const int end = overview.markedArgumentEnd();
462 hintText += Qt::escape(prettyMethod.left(begin));
464 hintText += Qt::escape(prettyMethod.mid(begin, end - begin));
466 hintText += Qt::escape(prettyMethod.mid(end));
470 int CppFunctionHintModel::activeArgument(const QString &prefix) const
474 SimpleLexer tokenize;
475 QList<Token> tokens = tokenize(prefix);
476 for (int i = 0; i < tokens.count(); ++i) {
477 const Token &tk = tokens.at(i);
480 else if (tk.is(T_RPAREN))
482 else if (! parcount && tk.is(T_COMMA))
489 if (argnr != m_currentArg)
490 m_currentArg = argnr;
495 // ---------------------------
496 // CppCompletionAssistProvider
497 // ---------------------------
498 bool CppCompletionAssistProvider::supportsEditor(const QString &editorId) const
500 return editorId == QLatin1String(CppEditor::Constants::CPPEDITOR_ID);
503 int CppCompletionAssistProvider::activationCharSequenceLength() const
508 bool CppCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const
510 const QChar &ch = sequence.at(2);
511 const QChar &ch2 = sequence.at(1);
512 const QChar &ch3 = sequence.at(0);
513 if (activationSequenceChar(ch, ch2, ch3, 0, true) != 0)
518 IAssistProcessor *CppCompletionAssistProvider::createProcessor() const
520 return new CppCompletionAssistProcessor;
526 class CppAssistProposal : public TextEditor::GenericProposal
529 CppAssistProposal(int cursorPos, TextEditor::IGenericProposalModel *model)
530 : TextEditor::GenericProposal(cursorPos, model)
531 , m_replaceDotForArrow(static_cast<CppAssistProposalModel *>(model)->m_replaceDotForArrow)
534 virtual bool isCorrective() const { return m_replaceDotForArrow; }
535 virtual void makeCorrection(BaseTextEditor *editor);
538 bool m_replaceDotForArrow;
541 void CppAssistProposal::makeCorrection(BaseTextEditor *editor)
543 editor->setCursorPosition(basePosition() - 1);
544 editor->replace(1, QLatin1String("->"));
550 class ConvertToCompletionItem: protected NameVisitor
552 // The completion item.
553 BasicProposalItem *_item;
555 // The current symbol.
558 // The pretty printer.
562 ConvertToCompletionItem()
567 BasicProposalItem *operator()(Symbol *symbol)
569 if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId())
572 BasicProposalItem *previousItem = switchCompletionItem(0);
573 Symbol *previousSymbol = switchSymbol(symbol);
574 accept(symbol->unqualifiedName());
576 _item->setData(QVariant::fromValue(symbol));
577 (void) switchSymbol(previousSymbol);
578 return switchCompletionItem(previousItem);
582 Symbol *switchSymbol(Symbol *symbol)
584 Symbol *previousSymbol = _symbol;
586 return previousSymbol;
589 BasicProposalItem *switchCompletionItem(BasicProposalItem *item)
591 BasicProposalItem *previousItem = _item;
596 BasicProposalItem *newCompletionItem(const Name *name)
598 BasicProposalItem *item = new CppAssistProposalItem;
599 item->setText(overview.prettyName(name));
603 virtual void visit(const Identifier *name)
604 { _item = newCompletionItem(name); }
606 virtual void visit(const TemplateNameId *name)
608 _item = newCompletionItem(name);
609 _item->setText(QLatin1String(name->identifier()->chars()));
612 virtual void visit(const DestructorNameId *name)
613 { _item = newCompletionItem(name); }
615 virtual void visit(const OperatorNameId *name)
616 { _item = newCompletionItem(name); }
618 virtual void visit(const ConversionNameId *name)
619 { _item = newCompletionItem(name); }
621 virtual void visit(const QualifiedNameId *name)
622 { _item = newCompletionItem(name->name()); }
625 Class *asClassOrTemplateClassType(FullySpecifiedType ty)
627 if (Class *classTy = ty->asClassType())
629 else if (Template *templ = ty->asTemplateType()) {
630 if (Symbol *decl = templ->declaration())
631 return decl->asClass();
636 Scope *enclosingNonTemplateScope(Symbol *symbol)
639 if (Scope *scope = symbol->enclosingScope()) {
640 if (Template *templ = scope->asTemplate())
641 return templ->enclosingScope();
648 Function *asFunctionOrTemplateFunctionType(FullySpecifiedType ty)
650 if (Function *funTy = ty->asFunctionType())
652 else if (Template *templ = ty->asTemplateType()) {
653 if (Symbol *decl = templ->declaration())
654 return decl->asFunction();
661 // ----------------------------
662 // CppCompletionAssistProcessor
663 // ----------------------------
664 CppCompletionAssistProcessor::CppCompletionAssistProcessor()
665 : m_startPosition(-1)
666 , m_objcEnabled(true)
667 , m_snippetCollector(CppEditor::Constants::CPP_SNIPPETS_GROUP_ID,
668 QIcon(QLatin1String(":/texteditor/images/snippet.png")))
669 , preprocessorCompletions(QStringList()
670 << QLatin1String("define")
671 << QLatin1String("error")
672 << QLatin1String("include")
673 << QLatin1String("line")
674 << QLatin1String("pragma")
675 << QLatin1String("undef")
676 << QLatin1String("if")
677 << QLatin1String("ifdef")
678 << QLatin1String("ifndef")
679 << QLatin1String("elif")
680 << QLatin1String("else")
681 << QLatin1String("endif"))
682 , m_model(new CppAssistProposalModel)
686 CppCompletionAssistProcessor::~CppCompletionAssistProcessor()
689 IAssistProposal * CppCompletionAssistProcessor::perform(const IAssistInterface *interface)
691 m_interface.reset(static_cast<const CppCompletionAssistInterface *>(interface));
693 if (interface->reason() != ExplicitlyInvoked && !accepts())
696 int index = startCompletionHelper();
699 return m_hintProposal;
701 if (m_model->m_completionOperator != T_EOF_SYMBOL)
702 m_model->m_sortable = true;
704 m_model->m_sortable = false;
705 return createContentProposal();
711 bool CppCompletionAssistProcessor::accepts() const
713 const int pos = m_interface->position();
714 unsigned token = T_EOF_SYMBOL;
716 const int start = startOfOperator(pos, &token, /*want function call=*/ true);
718 if (token == T_POUND) {
719 const int column = pos - m_interface->document()->findBlock(start).position();
726 // Trigger completion after three characters of a name have been typed, when not editing an existing name
727 QChar characterUnderCursor = m_interface->characterAt(pos);
728 if (!characterUnderCursor.isLetterOrNumber() && characterUnderCursor != QLatin1Char('_')) {
729 const int startOfName = findStartOfName(pos);
730 if (pos - startOfName >= 3) {
731 const QChar firstCharacter = m_interface->characterAt(startOfName);
732 if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
733 // Finally check that we're not inside a comment or string (code copied from startOfOperator)
734 QTextCursor tc(m_interface->document());
737 SimpleLexer tokenize;
738 tokenize.setQtMocRunEnabled(true);
739 tokenize.setObjCEnabled(true);
740 tokenize.setSkipComments(false);
741 const QList<Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
742 const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
743 const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
745 if (!tk.isComment() && !tk.isLiteral()) {
747 } else if (tk.isLiteral()
748 && tokens.size() == 3
749 && tokens.at(0).kind() == T_POUND
750 && tokens.at(1).kind() == T_IDENTIFIER) {
751 const QString &line = tc.block().text();
752 const Token &idToken = tokens.at(1);
753 const QStringRef &identifier =
754 line.midRef(idToken.begin(), idToken.end() - idToken.begin());
755 if (identifier == QLatin1String("include")
756 || identifier == QLatin1String("include_next")
757 || (m_objcEnabled && identifier == QLatin1String("import"))) {
769 IAssistProposal *CppCompletionAssistProcessor::createContentProposal()
771 // Duplicates are kept only if they are snippets.
772 QSet<QString> processed;
773 QList<BasicProposalItem *>::iterator it = m_completions.begin();
774 while (it != m_completions.end()) {
775 CppAssistProposalItem *item = static_cast<CppAssistProposalItem *>(*it);
776 if (!processed.contains(item->text()) || item->data().canConvert<QString>()) {
778 if (!item->data().canConvert<QString>()) {
779 processed.insert(item->text());
780 if (!item->isOverloaded()) {
781 if (Symbol *symbol = qvariant_cast<Symbol *>(item->data())) {
782 if (Function *funTy = symbol->type()->asFunctionType()) {
783 if (funTy->hasArguments())
784 item->markAsOverloaded();
791 it = m_completions.erase(it);
795 m_model->loadContent(m_completions);
796 return new CppAssistProposal(m_startPosition, m_model.take());
799 IAssistProposal *CppCompletionAssistProcessor::createHintProposal(
800 QList<CPlusPlus::Function *> functionSymbols) const
802 IFunctionHintProposalModel *model =
803 new CppFunctionHintModel(functionSymbols, m_model->m_typeOfExpression);
804 IAssistProposal *proposal = new FunctionHintProposal(m_startPosition, model);
808 int CppCompletionAssistProcessor::startOfOperator(int pos,
810 bool wantFunctionCall) const
812 const QChar ch = pos > -1 ? m_interface->characterAt(pos - 1) : QChar();
813 const QChar ch2 = pos > 0 ? m_interface->characterAt(pos - 2) : QChar();
814 const QChar ch3 = pos > 1 ? m_interface->characterAt(pos - 3) : QChar();
816 int start = pos - activationSequenceChar(ch, ch2, ch3, kind, wantFunctionCall);
818 QTextCursor tc(m_interface->document());
821 // Include completion: make sure the quote character is the first one on the line
822 if (*kind == T_STRING_LITERAL) {
824 s.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
825 QString sel = s.selectedText();
826 if (sel.indexOf(QLatin1Char('"')) < sel.length() - 1) {
827 *kind = T_EOF_SYMBOL;
832 if (*kind == T_COMMA) {
833 ExpressionUnderCursor expressionUnderCursor;
834 if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
835 *kind = T_EOF_SYMBOL;
840 SimpleLexer tokenize;
841 tokenize.setQtMocRunEnabled(true);
842 tokenize.setObjCEnabled(true);
843 tokenize.setSkipComments(false);
844 const QList<Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
845 const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1)); // get the token at the left of the cursor
846 const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
848 if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
849 *kind = T_EOF_SYMBOL;
852 // Don't complete in comments or strings, but still check for include completion
853 else if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT) ||
854 (tk.isLiteral() && (*kind != T_STRING_LITERAL
855 && *kind != T_ANGLE_STRING_LITERAL
856 && *kind != T_SLASH))) {
857 *kind = T_EOF_SYMBOL;
860 // Include completion: can be triggered by slash, but only in a string
861 else if (*kind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) {
862 *kind = T_EOF_SYMBOL;
865 else if (*kind == T_LPAREN) {
867 const Token &previousToken = tokens.at(tokenIdx - 1); // look at the token at the left of T_LPAREN
868 switch (previousToken.kind()) {
876 // that's a bad token :)
877 *kind = T_EOF_SYMBOL;
882 // Check for include preprocessor directive
883 else if (*kind == T_STRING_LITERAL || *kind == T_ANGLE_STRING_LITERAL || *kind == T_SLASH) {
884 bool include = false;
885 if (tokens.size() >= 3) {
886 if (tokens.at(0).is(T_POUND) && tokens.at(1).is(T_IDENTIFIER) && (tokens.at(2).is(T_STRING_LITERAL) ||
887 tokens.at(2).is(T_ANGLE_STRING_LITERAL))) {
888 const Token &directiveToken = tokens.at(1);
889 QString directive = tc.block().text().mid(directiveToken.begin(),
890 directiveToken.length());
891 if (directive == QLatin1String("include") ||
892 directive == QLatin1String("include_next") ||
893 directive == QLatin1String("import")) {
900 *kind = T_EOF_SYMBOL;
909 int CppCompletionAssistProcessor::findStartOfName(int pos) const
912 pos = m_interface->position();
915 // Skip to the start of a name
917 chr = m_interface->characterAt(--pos);
918 } while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
923 int CppCompletionAssistProcessor::startCompletionHelper()
926 if (tryObjCCompletion())
927 return m_startPosition;
930 const int startOfName = findStartOfName();
931 m_startPosition = startOfName;
932 m_model->m_completionOperator = T_EOF_SYMBOL;
934 int endOfOperator = m_startPosition;
936 // Skip whitespace preceding this position
937 while (m_interface->characterAt(endOfOperator - 1).isSpace())
940 int endOfExpression = startOfOperator(endOfOperator,
941 &m_model->m_completionOperator,
942 /*want function call =*/ true);
944 const Core::IFile *file = m_interface->file();
945 QString fileName = file->fileName();
947 if (m_model->m_completionOperator == T_DOXY_COMMENT) {
948 for (int i = 1; i < T_DOXY_LAST_TAG; ++i)
949 addCompletionItem(QString::fromLatin1(doxygenTagSpell(i)), m_icons.keywordIcon());
950 return m_startPosition;
953 // Pre-processor completion
954 if (m_model->m_completionOperator == T_POUND) {
955 completePreprocessor();
956 m_startPosition = startOfName;
957 return m_startPosition;
960 // Include completion
961 if (m_model->m_completionOperator == T_STRING_LITERAL
962 || m_model->m_completionOperator == T_ANGLE_STRING_LITERAL
963 || m_model->m_completionOperator == T_SLASH) {
965 QTextCursor c(m_interface->document());
966 c.setPosition(endOfExpression);
967 if (completeInclude(c))
968 m_startPosition = startOfName;
969 return m_startPosition;
972 ExpressionUnderCursor expressionUnderCursor;
973 QTextCursor tc(m_interface->document());
975 if (m_model->m_completionOperator == T_COMMA) {
976 tc.setPosition(endOfExpression);
977 const int start = expressionUnderCursor.startOfFunctionCall(tc);
979 m_model->m_completionOperator = T_EOF_SYMBOL;
983 endOfExpression = start;
984 m_startPosition = start + 1;
985 m_model->m_completionOperator = T_LPAREN;
989 int startOfExpression = m_interface->position();
990 tc.setPosition(endOfExpression);
992 if (m_model->m_completionOperator) {
993 expression = expressionUnderCursor(tc);
994 startOfExpression = endOfExpression - expression.length();
996 if (m_model->m_completionOperator == T_LPAREN) {
997 if (expression.endsWith(QLatin1String("SIGNAL")))
998 m_model->m_completionOperator = T_SIGNAL;
1000 else if (expression.endsWith(QLatin1String("SLOT")))
1001 m_model->m_completionOperator = T_SLOT;
1003 else if (m_interface->position() != endOfOperator) {
1004 // We don't want a function completion when the cursor isn't at the opening brace
1006 m_model->m_completionOperator = T_EOF_SYMBOL;
1007 m_startPosition = startOfName;
1008 startOfExpression = m_interface->position();
1011 } else if (expression.isEmpty()) {
1012 while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
1013 --startOfExpression;
1016 int line = 0, column = 0;
1017 Convenience::convertPosition(m_interface->document(), startOfExpression, &line, &column);
1018 return startCompletionInternal(fileName, line, column, expression, endOfExpression);
1021 bool CppCompletionAssistProcessor::tryObjCCompletion()
1023 int end = m_interface->position();
1024 while (m_interface->characterAt(end).isSpace())
1026 if (m_interface->characterAt(end) != QLatin1Char(']'))
1029 QTextCursor tc(m_interface->document());
1030 tc.setPosition(end);
1031 BackwardsScanner tokens(tc);
1032 if (tokens[tokens.startToken() - 1].isNot(T_RBRACKET))
1035 const int start = tokens.startOfMatchingBrace(tokens.startToken());
1036 if (start == tokens.startToken())
1039 const int startPos = tokens[start].begin() + tokens.startPosition();
1040 const QString expr = m_interface->textAt(startPos, m_interface->position() - startPos);
1042 Document::Ptr thisDocument = m_interface->snapshot().document(m_interface->file()->fileName());
1046 m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
1048 int line = 0, column = 0;
1049 Convenience::convertPosition(m_interface->document(), m_interface->position(), &line, &column);
1050 Scope *scope = thisDocument->scopeAt(line, column);
1054 const QList<LookupItem> items = (*m_model->m_typeOfExpression)(expr, scope);
1055 LookupContext lookupContext(thisDocument, m_interface->snapshot());
1057 foreach (const LookupItem &item, items) {
1058 FullySpecifiedType ty = item.type().simplified();
1059 if (ty->isPointerType()) {
1060 ty = ty->asPointerType()->elementType().simplified();
1062 if (NamedType *namedTy = ty->asNamedType()) {
1063 ClassOrNamespace *binding = lookupContext.lookupType(namedTy->name(), item.scope());
1064 completeObjCMsgSend(binding, false);
1067 if (ObjCClass *clazz = ty->asObjCClassType()) {
1068 ClassOrNamespace *binding = lookupContext.lookupType(clazz->name(), item.scope());
1069 completeObjCMsgSend(binding, true);
1074 if (m_completions.isEmpty())
1077 m_startPosition = m_interface->position();
1081 void CppCompletionAssistProcessor::addCompletionItem(const QString &text,
1084 const QVariant &data)
1086 BasicProposalItem *item = new CppAssistProposalItem;
1087 item->setText(text);
1088 item->setIcon(icon);
1089 item->setOrder(order);
1090 item->setData(data);
1091 m_completions.append(item);
1094 void CppCompletionAssistProcessor::addCompletionItem(CPlusPlus::Symbol *symbol)
1096 ConvertToCompletionItem toCompletionItem;
1097 BasicProposalItem *item = toCompletionItem(symbol);
1099 item->setIcon(m_icons.iconForSymbol(symbol));
1100 m_completions.append(item);
1104 void CppCompletionAssistProcessor::completeObjCMsgSend(CPlusPlus::ClassOrNamespace *binding,
1105 bool staticClassAccess)
1107 QList<Scope*> memberScopes;
1108 foreach (Symbol *s, binding->symbols()) {
1109 if (ObjCClass *c = s->asObjCClass())
1110 memberScopes.append(c);
1113 foreach (Scope *scope, memberScopes) {
1114 for (unsigned i = 0; i < scope->memberCount(); ++i) {
1115 Symbol *symbol = scope->memberAt(i);
1117 if (ObjCMethod *method = symbol->type()->asObjCMethodType()) {
1118 if (method->isStatic() == staticClassAccess) {
1120 const SelectorNameId *selectorName =
1121 method->name()->asSelectorNameId();
1124 if (selectorName->hasArguments()) {
1125 for (unsigned i = 0; i < selectorName->nameCount(); ++i) {
1127 text += QLatin1Char(' ');
1128 Symbol *arg = method->argumentAt(i);
1129 text += selectorName->nameAt(i)->identifier()->chars();
1130 text += QLatin1Char(':');
1131 text += TextEditor::Snippet::kVariableDelimiter;
1132 text += QLatin1Char('(');
1133 text += oo(arg->type());
1134 text += QLatin1Char(')');
1135 text += oo(arg->name());
1136 text += TextEditor::Snippet::kVariableDelimiter;
1139 text = selectorName->identifier()->chars();
1143 if (!text.isEmpty())
1144 addCompletionItem(text, QIcon(), 0, QVariant::fromValue(data));
1151 bool CppCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
1153 QString directoryPrefix;
1154 if (m_model->m_completionOperator == T_SLASH) {
1155 QTextCursor c = cursor;
1156 c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
1157 QString sel = c.selectedText();
1158 int startCharPos = sel.indexOf(QLatin1Char('"'));
1159 if (startCharPos == -1) {
1160 startCharPos = sel.indexOf(QLatin1Char('<'));
1161 m_model->m_completionOperator = T_ANGLE_STRING_LITERAL;
1163 m_model->m_completionOperator = T_STRING_LITERAL;
1165 if (startCharPos != -1)
1166 directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
1169 // Make completion for all relevant includes
1170 QStringList includePaths = m_interface->includePaths();
1171 const QString ¤tFilePath = QFileInfo(m_interface->file()->fileName()).path();
1172 if (!includePaths.contains(currentFilePath))
1173 includePaths.append(currentFilePath);
1175 const Core::MimeType mimeType =
1176 Core::ICore::instance()->mimeDatabase()->findByType(QLatin1String("text/x-c++hdr"));
1177 const QStringList suffixes = mimeType.suffixes();
1179 foreach (const QString &includePath, includePaths) {
1180 QString realPath = includePath;
1181 if (!directoryPrefix.isEmpty()) {
1182 realPath += QLatin1Char('/');
1183 realPath += directoryPrefix;
1185 completeInclude(realPath, suffixes);
1188 foreach (const QString &frameworkPath, m_interface->frameworkPaths()) {
1189 QString realPath = frameworkPath;
1190 if (!directoryPrefix.isEmpty()) {
1191 realPath += QLatin1Char('/');
1192 realPath += directoryPrefix;
1193 realPath += QLatin1String(".framework/Headers");
1195 completeInclude(realPath, suffixes);
1198 return !m_completions.isEmpty();
1201 void CppCompletionAssistProcessor::completeInclude(const QString &realPath,
1202 const QStringList &suffixes)
1204 QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
1205 while (i.hasNext()) {
1206 const QString fileName = i.next();
1207 const QFileInfo fileInfo = i.fileInfo();
1208 const QString suffix = fileInfo.suffix();
1209 if (suffix.isEmpty() || suffixes.contains(suffix)) {
1210 QString text = fileName.mid(realPath.length() + 1);
1211 if (fileInfo.isDir())
1212 text += QLatin1Char('/');
1213 addCompletionItem(text, m_icons.keywordIcon());
1218 void CppCompletionAssistProcessor::completePreprocessor()
1220 foreach (const QString &preprocessorCompletion, preprocessorCompletions)
1221 addCompletionItem(preprocessorCompletion);
1223 if (objcKeywordsWanted())
1224 addCompletionItem(QLatin1String("import"));
1227 bool CppCompletionAssistProcessor::objcKeywordsWanted() const
1232 const Core::IFile *file = m_interface->file();
1233 QString fileName = file->fileName();
1235 const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase();
1236 return mdb->findByFile(fileName).type() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE;
1239 int CppCompletionAssistProcessor::startCompletionInternal(const QString fileName,
1240 unsigned line, unsigned column,
1241 const QString &expr,
1242 int endOfExpression)
1244 QString expression = expr.trimmed();
1246 Document::Ptr thisDocument = m_interface->snapshot().document(fileName);
1250 m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
1252 Scope *scope = thisDocument->scopeAt(line, column);
1253 Q_ASSERT(scope != 0);
1255 if (expression.isEmpty()) {
1256 if (m_model->m_completionOperator == T_EOF_SYMBOL || m_model->m_completionOperator == T_COLON_COLON) {
1257 (void) (*m_model->m_typeOfExpression)(expression, scope);
1258 globalCompletion(scope);
1259 if (m_completions.isEmpty())
1261 return m_startPosition;
1264 else if (m_model->m_completionOperator == T_SIGNAL || m_model->m_completionOperator == T_SLOT) {
1265 // Apply signal/slot completion on 'this'
1266 expression = QLatin1String("this");
1270 QList<LookupItem> results =
1271 (*m_model->m_typeOfExpression)(expression, scope, TypeOfExpression::Preprocess);
1273 if (results.isEmpty()) {
1274 if (m_model->m_completionOperator == T_SIGNAL || m_model->m_completionOperator == T_SLOT) {
1275 if (! (expression.isEmpty() || expression == QLatin1String("this"))) {
1276 expression = QLatin1String("this");
1277 results = (*m_model->m_typeOfExpression)(expression, scope);
1280 if (results.isEmpty())
1283 } else if (m_model->m_completionOperator == T_LPAREN) {
1284 // Find the expression that precedes the current name
1285 int index = endOfExpression;
1286 while (m_interface->characterAt(index - 1).isSpace())
1288 index = findStartOfName(index);
1290 QTextCursor tc(m_interface->document());
1291 tc.setPosition(index);
1293 ExpressionUnderCursor expressionUnderCursor;
1294 const QString baseExpression = expressionUnderCursor(tc);
1296 // Resolve the type of this expression
1297 const QList<LookupItem> results =
1298 (*m_model->m_typeOfExpression)(baseExpression, scope,
1299 TypeOfExpression::Preprocess);
1301 // If it's a class, add completions for the constructors
1302 foreach (const LookupItem &result, results) {
1303 if (result.type()->isClassType()) {
1304 if (completeConstructorOrFunction(results, endOfExpression, true))
1305 return m_startPosition;
1319 switch (m_model->m_completionOperator) {
1321 if (completeConstructorOrFunction(results, endOfExpression, false))
1322 return m_startPosition;
1327 if (completeMember(results))
1328 return m_startPosition;
1332 if (completeScope(results))
1333 return m_startPosition;
1337 if (completeSignal(results))
1338 return m_startPosition;
1342 if (completeSlot(results))
1343 return m_startPosition;
1354 void CppCompletionAssistProcessor::globalCompletion(CPlusPlus::Scope *currentScope)
1356 const LookupContext &context = m_model->m_typeOfExpression->context();
1358 if (m_model->m_completionOperator == T_COLON_COLON) {
1359 completeNamespace(context.globalNamespace());
1363 QList<ClassOrNamespace *> usingBindings;
1364 ClassOrNamespace *currentBinding = 0;
1366 for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
1367 if (scope->isBlock()) {
1368 if (ClassOrNamespace *binding = context.lookupType(scope)) {
1369 for (unsigned i = 0; i < scope->memberCount(); ++i) {
1370 Symbol *member = scope->memberAt(i);
1371 if (! member->name())
1373 else if (UsingNamespaceDirective *u = member->asUsingNamespaceDirective()) {
1374 if (ClassOrNamespace *b = binding->lookupType(u->name()))
1375 usingBindings.append(b);
1379 } else if (scope->isFunction() || scope->isClass() || scope->isNamespace()) {
1380 currentBinding = context.lookupType(scope);
1385 for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
1386 if (scope->isBlock()) {
1387 for (unsigned i = 0; i < scope->memberCount(); ++i) {
1388 addCompletionItem(scope->memberAt(i));
1390 } else if (scope->isFunction()) {
1391 Function *fun = scope->asFunction();
1392 for (unsigned i = 0; i < fun->argumentCount(); ++i) {
1393 addCompletionItem(fun->argumentAt(i));
1401 for (; currentBinding; currentBinding = currentBinding->parent()) {
1402 const QList<Symbol *> symbols = currentBinding->symbols();
1404 if (! symbols.isEmpty()) {
1405 if (symbols.first()->isNamespace())
1406 completeNamespace(currentBinding);
1408 completeClass(currentBinding);
1412 foreach (ClassOrNamespace *b, usingBindings)
1413 completeNamespace(b);
1416 addMacros(QLatin1String("<configuration>"), context.snapshot());
1417 addMacros(context.thisDocument()->fileName(), context.snapshot());
1421 bool CppCompletionAssistProcessor::completeMember(const QList<CPlusPlus::LookupItem> &baseResults)
1423 const LookupContext &context = m_model->m_typeOfExpression->context();
1425 if (baseResults.isEmpty())
1428 ResolveExpression resolveExpression(context);
1430 if (ClassOrNamespace *binding =
1431 resolveExpression.baseExpression(baseResults,
1432 m_model->m_completionOperator,
1433 &m_model->m_replaceDotForArrow)) {
1435 completeClass(binding, /*static lookup = */ false);
1437 return ! m_completions.isEmpty();
1443 bool CppCompletionAssistProcessor::completeScope(const QList<CPlusPlus::LookupItem> &results)
1445 const LookupContext &context = m_model->m_typeOfExpression->context();
1446 if (results.isEmpty())
1449 foreach (const LookupItem &result, results) {
1450 FullySpecifiedType ty = result.type();
1451 Scope *scope = result.scope();
1453 if (NamedType *namedTy = ty->asNamedType()) {
1454 if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
1459 } else if (Class *classTy = ty->asClassType()) {
1460 if (ClassOrNamespace *b = context.lookupType(classTy)) {
1465 } else if (Namespace *nsTy = ty->asNamespaceType()) {
1466 if (ClassOrNamespace *b = context.lookupType(nsTy)) {
1467 completeNamespace(b);
1474 return ! m_completions.isEmpty();
1477 void CppCompletionAssistProcessor::completeNamespace(CPlusPlus::ClassOrNamespace *b)
1479 QSet<ClassOrNamespace *> bindingsVisited;
1480 QList<ClassOrNamespace *> bindingsToVisit;
1481 bindingsToVisit.append(b);
1483 while (! bindingsToVisit.isEmpty()) {
1484 ClassOrNamespace *binding = bindingsToVisit.takeFirst();
1485 if (! binding || bindingsVisited.contains(binding))
1488 bindingsVisited.insert(binding);
1489 bindingsToVisit += binding->usings();
1491 QList<Scope *> scopesToVisit;
1492 QSet<Scope *> scopesVisited;
1494 foreach (Symbol *bb, binding->symbols()) {
1495 if (Namespace *ns = bb->asNamespace())
1496 scopesToVisit.append(ns);
1499 foreach (Enum *e, binding->enums()) {
1500 scopesToVisit.append(e);
1503 while (! scopesToVisit.isEmpty()) {
1504 Scope *scope = scopesToVisit.takeFirst();
1505 if (! scope || scopesVisited.contains(scope))
1508 scopesVisited.insert(scope);
1510 for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
1511 Symbol *member = *it;
1512 addCompletionItem(member);
1518 void CppCompletionAssistProcessor::completeClass(CPlusPlus::ClassOrNamespace *b, bool staticLookup)
1520 QSet<ClassOrNamespace *> bindingsVisited;
1521 QList<ClassOrNamespace *> bindingsToVisit;
1522 bindingsToVisit.append(b);
1524 while (! bindingsToVisit.isEmpty()) {
1525 ClassOrNamespace *binding = bindingsToVisit.takeFirst();
1526 if (! binding || bindingsVisited.contains(binding))
1529 bindingsVisited.insert(binding);
1530 bindingsToVisit += binding->usings();
1532 QList<Scope *> scopesToVisit;
1533 QSet<Scope *> scopesVisited;
1535 foreach (Symbol *bb, binding->symbols()) {
1536 if (Class *k = bb->asClass())
1537 scopesToVisit.append(k);
1540 foreach (Enum *e, binding->enums())
1541 scopesToVisit.append(e);
1543 while (! scopesToVisit.isEmpty()) {
1544 Scope *scope = scopesToVisit.takeFirst();
1545 if (! scope || scopesVisited.contains(scope))
1548 scopesVisited.insert(scope);
1550 addCompletionItem(scope); // add a completion item for the injected class name.
1552 for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
1553 Symbol *member = *it;
1554 if (member->isFriend()
1555 || member->isQtPropertyDeclaration()
1556 || member->isQtEnum()) {
1558 } else if (! staticLookup && (member->isTypedef() ||
1560 member->isClass())) {
1564 addCompletionItem(member);
1570 bool CppCompletionAssistProcessor::completeQtMethod(const QList<CPlusPlus::LookupItem> &results, bool wantSignals)
1572 if (results.isEmpty())
1575 const LookupContext &context = m_model->m_typeOfExpression->context();
1577 ConvertToCompletionItem toCompletionItem;
1579 o.setShowReturnTypes(false);
1580 o.setShowArgumentNames(false);
1581 o.setShowFunctionSignatures(true);
1583 QSet<QString> signatures;
1584 foreach (const LookupItem &p, results) {
1585 FullySpecifiedType ty = p.type().simplified();
1587 if (PointerType *ptrTy = ty->asPointerType())
1588 ty = ptrTy->elementType().simplified();
1590 continue; // not a pointer or a reference to a pointer.
1592 NamedType *namedTy = ty->asNamedType();
1593 if (! namedTy) // not a class name.
1596 ClassOrNamespace *b = context.lookupType(namedTy->name(), p.scope());
1600 QList<ClassOrNamespace *>todo;
1601 QSet<ClassOrNamespace *> processed;
1602 QList<Scope *> scopes;
1604 while (!todo.isEmpty()) {
1605 ClassOrNamespace *binding = todo.takeLast();
1606 if (!processed.contains(binding)) {
1607 processed.insert(binding);
1609 foreach (Symbol *s, binding->symbols())
1610 if (Class *clazz = s->asClass())
1611 scopes.append(clazz);
1613 todo.append(binding->usings());
1617 foreach (Scope *scope, scopes) {
1618 if (! scope->isClass())
1621 for (unsigned i = 0; i < scope->memberCount(); ++i) {
1622 Symbol *member = scope->memberAt(i);
1623 Function *fun = member->type()->asFunctionType();
1626 if (wantSignals && ! fun->isSignal())
1628 else if (! wantSignals && ! fun->isSlot())
1631 unsigned count = fun->argumentCount();
1634 signature += Overview().prettyName(fun->name());
1635 signature += QLatin1Char('(');
1636 for (unsigned i = 0; i < count; ++i) {
1637 Symbol *arg = fun->argumentAt(i);
1639 signature += QLatin1Char(',');
1640 signature += o.prettyType(arg->type());
1642 signature += QLatin1Char(')');
1644 const QByteArray normalized =
1645 QMetaObject::normalizedSignature(signature.toLatin1());
1647 signature = QString::fromLatin1(normalized, normalized.size());
1649 if (! signatures.contains(signature)) {
1650 BasicProposalItem *ci = toCompletionItem(fun);
1653 signatures.insert(signature);
1654 ci->setText(signature); // fix the completion item.
1655 m_completions.append(ci);
1658 if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer())
1667 return ! m_completions.isEmpty();
1670 void CppCompletionAssistProcessor::addSnippets()
1672 m_completions.append(m_snippetCollector.collect());
1675 void CppCompletionAssistProcessor::addKeywords()
1677 int keywordLimit = T_FIRST_OBJC_AT_KEYWORD;
1678 if (objcKeywordsWanted())
1679 keywordLimit = T_LAST_OBJC_AT_KEYWORD + 1;
1681 // keyword completion items.
1682 for (int i = T_FIRST_KEYWORD; i < keywordLimit; ++i)
1683 addCompletionItem(QLatin1String(Token::name(i)), m_icons.keywordIcon());
1686 void CppCompletionAssistProcessor::addMacros(const QString &fileName, const CPlusPlus::Snapshot &snapshot)
1688 QSet<QString> processed;
1689 QSet<QString> definedMacros;
1691 addMacros_helper(snapshot, fileName, &processed, &definedMacros);
1693 foreach (const QString ¯oName, definedMacros)
1694 addCompletionItem(macroName, m_icons.macroIcon());
1697 void CppCompletionAssistProcessor::addMacros_helper(const CPlusPlus::Snapshot &snapshot,
1698 const QString &fileName,
1699 QSet<QString> *processed,
1700 QSet<QString> *definedMacros)
1702 Document::Ptr doc = snapshot.document(fileName);
1704 if (! doc || processed->contains(doc->fileName()))
1707 processed->insert(doc->fileName());
1709 foreach (const Document::Include &i, doc->includes()) {
1710 addMacros_helper(snapshot, i.fileName(), processed, definedMacros);
1713 foreach (const Macro ¯o, doc->definedMacros()) {
1714 const QString macroName = QString::fromUtf8(macro.name().constData(), macro.name().length());
1715 if (! macro.isHidden())
1716 definedMacros->insert(macroName);
1718 definedMacros->remove(macroName);
1722 bool CppCompletionAssistProcessor::completeConstructorOrFunction(const QList<CPlusPlus::LookupItem> &results,
1723 int endOfExpression,
1726 const LookupContext &context = m_model->m_typeOfExpression->context();
1727 QList<Function *> functions;
1729 foreach (const LookupItem &result, results) {
1730 FullySpecifiedType exprTy = result.type().simplified();
1732 if (Class *klass = asClassOrTemplateClassType(exprTy)) {
1733 const Name *className = klass->name();
1735 continue; // nothing to do for anonymous classes.
1737 for (unsigned i = 0; i < klass->memberCount(); ++i) {
1738 Symbol *member = klass->memberAt(i);
1739 const Name *memberName = member->name();
1742 continue; // skip anonymous member.
1744 else if (memberName->isQualifiedNameId())
1747 if (Function *funTy = member->type()->asFunctionType()) {
1748 if (memberName->isEqualTo(className)) {
1750 functions.append(funTy);
1759 if (functions.isEmpty()) {
1760 foreach (const LookupItem &result, results) {
1761 FullySpecifiedType ty = result.type().simplified();
1763 if (Function *fun = asFunctionOrTemplateFunctionType(ty)) {
1767 else if (! functions.isEmpty() && enclosingNonTemplateScope(functions.first()) != enclosingNonTemplateScope(fun))
1768 continue; // skip fun, it's an hidden declaration.
1770 bool newOverload = true;
1772 foreach (Function *f, functions) {
1773 if (fun->isEqualTo(f)) {
1774 newOverload = false;
1780 functions.append(fun);
1785 if (functions.isEmpty()) {
1786 const Name *functionCallOp = context.control()->operatorNameId(OperatorNameId::FunctionCallOp);
1788 foreach (const LookupItem &result, results) {
1789 FullySpecifiedType ty = result.type().simplified();
1790 Scope *scope = result.scope();
1792 if (NamedType *namedTy = ty->asNamedType()) {
1793 if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
1794 foreach (const LookupItem &r, b->lookup(functionCallOp)) {
1795 Symbol *overload = r.declaration();
1796 FullySpecifiedType overloadTy = overload->type().simplified();
1798 if (Function *funTy = overloadTy->asFunctionType()) {
1799 functions.append(funTy);
1807 // There are two different kinds of completion we want to provide:
1808 // 1. If this is a function call, we want to pop up a tooltip that shows the user
1809 // the possible overloads with their argument types and names.
1810 // 2. If this is a function definition, we want to offer autocompletion of
1811 // the function signature.
1813 // check if function signature autocompletion is appropriate
1814 // Also check if the function name is a destructor name.
1815 bool isDestructor = false;
1816 if (! functions.isEmpty() && ! toolTipOnly) {
1818 // function definitions will only happen in class or namespace scope,
1819 // so get the current location's enclosing scope.
1821 // get current line and column
1822 int lineSigned = 0, columnSigned = 0;
1823 Convenience::convertPosition(m_interface->document(), m_interface->position(), &lineSigned, &columnSigned);
1824 unsigned line = lineSigned, column = columnSigned;
1826 // find a scope that encloses the current location, starting from the lastVisibileSymbol
1827 // and moving outwards
1829 Scope *sc = context.thisDocument()->scopeAt(line, column);
1831 if (sc && (sc->isClass() || sc->isNamespace())) {
1832 // It may still be a function call. If the whole line parses as a function
1833 // declaration, we should be certain that it isn't.
1834 bool autocompleteSignature = false;
1836 QTextCursor tc(m_interface->document());
1837 tc.setPosition(endOfExpression);
1838 BackwardsScanner bs(tc);
1839 const int startToken = bs.startToken();
1840 int lineStartToken = bs.startOfLine(startToken);
1841 // make sure the required tokens are actually available
1842 bs.LA(startToken - lineStartToken);
1843 QString possibleDecl = bs.mid(lineStartToken).trimmed().append("();");
1845 Document::Ptr doc = Document::create(QLatin1String("<completion>"));
1846 doc->setSource(possibleDecl.toLatin1());
1847 if (doc->parse(Document::ParseDeclaration)) {
1849 if (SimpleDeclarationAST *sd = doc->translationUnit()->ast()->asSimpleDeclaration()) {
1850 if (sd->declarator_list &&
1851 sd->declarator_list && sd->declarator_list->value->postfix_declarator_list
1852 && sd->declarator_list->value->postfix_declarator_list->value->asFunctionDeclarator()) {
1854 autocompleteSignature = true;
1856 CoreDeclaratorAST *coreDecl = sd->declarator_list->value->core_declarator;
1857 if (coreDecl && coreDecl->asDeclaratorId() && coreDecl->asDeclaratorId()->name) {
1858 NameAST *declName = coreDecl->asDeclaratorId()->name;
1859 if (declName->asDestructorName()) {
1860 isDestructor = true;
1861 } else if (QualifiedNameAST *qName = declName->asQualifiedName()) {
1862 if (qName->unqualified_name && qName->unqualified_name->asDestructorName())
1863 isDestructor = true;
1870 if (autocompleteSignature && !isDestructor) {
1871 // set up for rewriting function types with minimally qualified names
1872 // to do it correctly we'd need the declaration's context and scope, but
1873 // that'd be too expensive to get here. instead, we just minimize locally
1874 SubstitutionEnvironment env;
1875 env.setContext(context);
1876 env.switchScope(sc);
1877 ClassOrNamespace *targetCoN = context.lookupType(sc);
1879 targetCoN = context.globalNamespace();
1880 UseMinimalNames q(targetCoN);
1882 Control *control = context.control().data();
1884 // set up signature autocompletion
1885 foreach (Function *f, functions) {
1887 overview.setShowArgumentNames(true);
1888 overview.setShowDefaultArguments(false);
1890 const FullySpecifiedType localTy = rewriteType(f->type(), &env, control);
1892 // gets: "parameter list) cv-spec",
1893 QString completion = overview(localTy).mid(1);
1895 addCompletionItem(completion, QIcon(), 0,
1896 QVariant::fromValue(CompleteFunctionDeclaration(f)));
1903 if (! functions.empty() && !isDestructor) {
1904 m_hintProposal = createHintProposal(functions);