OSDN Git Service

61aa6d1c116fa39a9f483b7081e96b533433f365
[qt-creator-jp/qt-creator-jp.git] / src / plugins / cpptools / cppcompletionassist.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "cppmodelmanager.h"
34 #include "cppcompletionassist.h"
35 #include "cppdoxygen.h"
36 #include "cppmodelmanager.h"
37 #include "cpptoolsconstants.h"
38
39 #include <Control.h>
40 #include <AST.h>
41 #include <ASTVisitor.h>
42 #include <CoreTypes.h>
43 #include <Literals.h>
44 #include <Names.h>
45 #include <NameVisitor.h>
46 #include <Symbols.h>
47 #include <SymbolVisitor.h>
48 #include <Scope.h>
49 #include <TranslationUnit.h>
50 #include <CppRewriter.h>
51
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>
58
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>
72
73 #include <QtCore/QLatin1String>
74 #include <QtGui/QTextCursor>
75 #include <QtGui/QTextDocument>
76 #include <QtGui/QIcon>
77
78 using namespace CPlusPlus;
79 using namespace CppEditor;
80 using namespace CppTools;
81 using namespace Internal;
82 using namespace TextEditor;
83
84 namespace {
85
86 int activationSequenceChar(const QChar &ch,
87                            const QChar &ch2,
88                            const QChar &ch3,
89                            unsigned *kind,
90                            bool wantFunctionCall)
91 {
92     int referencePosition = 0;
93     int completionKind = T_EOF_SYMBOL;
94     switch (ch.toLatin1()) {
95     case '.':
96         if (ch2 != QLatin1Char('.')) {
97             completionKind = T_DOT;
98             referencePosition = 1;
99         }
100         break;
101     case ',':
102         completionKind = T_COMMA;
103         referencePosition = 1;
104         break;
105     case '(':
106         if (wantFunctionCall) {
107             completionKind = T_LPAREN;
108             referencePosition = 1;
109         }
110         break;
111     case ':':
112         if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) {
113             completionKind = T_COLON_COLON;
114             referencePosition = 2;
115         }
116         break;
117     case '>':
118         if (ch2 == QLatin1Char('-')) {
119             completionKind = T_ARROW;
120             referencePosition = 2;
121         }
122         break;
123     case '*':
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;
130         }
131         break;
132     case '\\':
133     case '@':
134         if (ch2.isNull() || ch2.isSpace()) {
135             completionKind = T_DOXY_COMMENT;
136             referencePosition = 1;
137         }
138         break;
139     case '<':
140         completionKind = T_ANGLE_STRING_LITERAL;
141         referencePosition = 1;
142         break;
143     case '"':
144         completionKind = T_STRING_LITERAL;
145         referencePosition = 1;
146         break;
147     case '/':
148         completionKind = T_SLASH;
149         referencePosition = 1;
150         break;
151     case '#':
152         completionKind = T_POUND;
153         referencePosition = 1;
154         break;
155     }
156
157     if (kind)
158         *kind = completionKind;
159
160     return referencePosition;
161 }
162
163 } // Anonymous
164
165 namespace CppTools {
166 namespace Internal {
167
168 struct CompleteFunctionDeclaration
169 {
170     explicit CompleteFunctionDeclaration(Function *f = 0)
171         : function(f)
172     {}
173
174     Function *function;
175 };
176
177 // ----------------------
178 // CppAssistProposalModel
179 // ----------------------
180 class CppAssistProposalModel : public TextEditor::BasicProposalItemListModel
181 {
182 public:
183     CppAssistProposalModel()
184         : TextEditor::BasicProposalItemListModel()
185         , m_sortable(false)
186         , m_completionOperator(T_EOF_SYMBOL)
187         , m_replaceDotForArrow(false)
188         , m_typeOfExpression(new TypeOfExpression)
189     {}
190
191     virtual bool isSortable() const { return m_sortable; }
192     virtual IAssistProposalItem *proposalItem(int index) const;
193
194     bool m_sortable;
195     unsigned m_completionOperator;
196     bool m_replaceDotForArrow;
197     QSharedPointer<TypeOfExpression> m_typeOfExpression;
198 };
199
200 // ---------------------
201 // CppAssistProposalItem
202 // ---------------------
203 class CppAssistProposalItem : public TextEditor::BasicProposalItem
204 {
205 public:
206     CppAssistProposalItem() :
207         m_isOverloaded(false) {}
208
209     virtual bool prematurelyApplies(const QChar &c) const;
210     virtual void applyContextualContent(TextEditor::BaseTextEditor *editor,
211                                         int basePosition) const;
212
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; }
218
219 private:
220     bool m_isOverloaded;
221     unsigned m_completionOperator;
222     mutable QChar m_typedChar;
223     QSharedPointer<TypeOfExpression> m_typeOfExpression;
224 };
225
226 } // Internal
227 } // CppTools
228
229 Q_DECLARE_METATYPE(CppTools::Internal::CompleteFunctionDeclaration)
230
231 IAssistProposalItem *CppAssistProposalModel::proposalItem(int index) const
232 {
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);
239     }
240     return item;
241 }
242
243 bool CppAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
244 {
245     if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
246         if (typedChar == QLatin1Char('(') || typedChar == QLatin1Char(',')) {
247             m_typedChar = typedChar;
248             return true;
249         }
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;
254             return true;
255         }
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;
263             return true;
264         }
265     } else if (data().canConvert<CompleteFunctionDeclaration>()) {
266         if (typedChar == QLatin1Char('(')) {
267             m_typedChar = typedChar;
268             return true;
269         }
270     }
271
272     return false;
273 }
274
275 void CppAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor *editor,
276                                                     int basePosition) const
277 {
278     Symbol *symbol = 0;
279
280     if (data().isValid())
281         symbol = data().value<Symbol *>();
282
283     QString toInsert;
284     QString extraChars;
285     int extraLength = 0;
286     int cursorOffset = 0;
287
288     bool autoParenthesesEnabled = true;
289
290     if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
291         toInsert = text();
292         extraChars += QLatin1Char(')');
293
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) {
297         toInsert = text();
298         if (!toInsert.endsWith(QLatin1Char('/'))) {
299             extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
300         } else {
301             if (m_typedChar == QLatin1Char('/')) // Eat the slash
302                 m_typedChar = QChar();
303         }
304     } else {
305         toInsert = text();
306
307         const CompletionSettings &completionSettings =
308                 TextEditorSettings::instance()->completionSettings();
309         const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;
310
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
317
318                     /// ### port me
319 #if 0
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('<');
324                     }
325 #endif
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('(');
331
332                     if (completionSettings.m_spaceAfterFunctionName)
333                         extraChars += QLatin1Char(' ');
334                     extraChars += QLatin1Char('(');
335                     if (m_typedChar == QLatin1Char('('))
336                         m_typedChar = QChar();
337
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;
344
345                     if (endWithSemicolon && characterAtCursor == semicolon) {
346                         endWithSemicolon = false;
347                         m_typedChar = QChar();
348                     }
349
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();
356                         }
357                     } else if (autoParenthesesEnabled) {
358                         const QChar lookAhead = editor->characterAt(editor->position() + 1);
359                         if (MatchingText::shouldInsertMatchingText(lookAhead)) {
360                             extraChars += QLatin1Char(')');
361                             --cursorOffset;
362                             if (endWithSemicolon) {
363                                 extraChars += semicolon;
364                                 --cursorOffset;
365                                 m_typedChar = QChar();
366                             }
367                         }
368                         // TODO: When an opening parenthesis exists, the "semicolon" should really be
369                         // inserted after the matching closing parenthesis.
370                     }
371                 }
372             }
373         }
374
375         if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) {
376             if (m_typedChar == QLatin1Char('('))
377                 m_typedChar = QChar();
378
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);
384         }
385     }
386
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)
391             --cursorOffset;
392     }
393
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());
397     int existLength = 0;
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)
403                 break;
404         }
405     }
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);
409         if (a == b)
410             ++extraLength;
411         else
412             break;
413     }
414
415     toInsert += extraChars;
416
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);
421     if (cursorOffset)
422         editor->setCursorPosition(editor->position() + cursorOffset);
423 }
424
425 // --------------------
426 // CppFunctionHintModel
427 // --------------------
428 class CppFunctionHintModel : public TextEditor::IFunctionHintProposalModel
429 {
430 public:
431     CppFunctionHintModel(QList<Function *> functionSymbols,
432                          const QSharedPointer<TypeOfExpression> &typeOfExp)
433         : m_functionSymbols(functionSymbols)
434         , m_currentArg(-1)
435         , m_typeOfExpression(typeOfExp)
436     {}
437
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;
442
443 private:
444     QList<Function *> m_functionSymbols;
445     mutable int m_currentArg;
446     QSharedPointer<TypeOfExpression> m_typeOfExpression;
447 };
448
449 QString CppFunctionHintModel::text(int index) const
450 {
451     Overview overview;
452     overview.setShowReturnTypes(true);
453     overview.setShowArgumentNames(true);
454     overview.setMarkedArgument(m_currentArg + 1);
455     Function *f = m_functionSymbols.at(index);
456
457     const QString prettyMethod = overview(f->type(), f->name());
458     const int begin = overview.markedArgumentBegin();
459     const int end = overview.markedArgumentEnd();
460
461     QString hintText;
462     hintText += Qt::escape(prettyMethod.left(begin));
463     hintText += "<b>";
464     hintText += Qt::escape(prettyMethod.mid(begin, end - begin));
465     hintText += "</b>";
466     hintText += Qt::escape(prettyMethod.mid(end));
467     return hintText;
468 }
469
470 int CppFunctionHintModel::activeArgument(const QString &prefix) const
471 {
472     int argnr = 0;
473     int parcount = 0;
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);
478         if (tk.is(T_LPAREN))
479             ++parcount;
480         else if (tk.is(T_RPAREN))
481             --parcount;
482         else if (! parcount && tk.is(T_COMMA))
483             ++argnr;
484     }
485
486     if (parcount < 0)
487         return -1;
488
489     if (argnr != m_currentArg)
490         m_currentArg = argnr;
491
492     return argnr;
493 }
494
495 // ---------------------------
496 // CppCompletionAssistProvider
497 // ---------------------------
498 bool CppCompletionAssistProvider::supportsEditor(const QString &editorId) const
499 {
500     return editorId == QLatin1String(CppEditor::Constants::CPPEDITOR_ID);
501 }
502
503 int CppCompletionAssistProvider::activationCharSequenceLength() const
504 {
505     return 3;
506 }
507
508 bool CppCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const
509 {
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)
514         return true;
515     return false;
516 }
517
518 IAssistProcessor *CppCompletionAssistProvider::createProcessor() const
519 {
520     return new CppCompletionAssistProcessor;
521 }
522
523 // -----------------
524 // CppAssistProposal
525 // -----------------
526 class CppAssistProposal : public TextEditor::GenericProposal
527 {
528 public:
529     CppAssistProposal(int cursorPos, TextEditor::IGenericProposalModel *model)
530         : TextEditor::GenericProposal(cursorPos, model)
531         , m_replaceDotForArrow(static_cast<CppAssistProposalModel *>(model)->m_replaceDotForArrow)
532     {}
533
534     virtual bool isCorrective() const { return m_replaceDotForArrow; }
535     virtual void makeCorrection(BaseTextEditor *editor);
536
537 private:
538     bool m_replaceDotForArrow;
539 };
540
541 void CppAssistProposal::makeCorrection(BaseTextEditor *editor)
542 {
543     editor->setCursorPosition(basePosition() - 1);
544     editor->replace(1, QLatin1String("->"));
545     moveBasePosition(1);
546 }
547
548 namespace {
549
550 class ConvertToCompletionItem: protected NameVisitor
551 {
552     // The completion item.
553     BasicProposalItem *_item;
554
555     // The current symbol.
556     Symbol *_symbol;
557
558     // The pretty printer.
559     Overview overview;
560
561 public:
562     ConvertToCompletionItem()
563         : _item(0)
564         , _symbol(0)
565     { }
566
567     BasicProposalItem *operator()(Symbol *symbol)
568     {
569         if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId())
570             return 0;
571
572         BasicProposalItem *previousItem = switchCompletionItem(0);
573         Symbol *previousSymbol = switchSymbol(symbol);
574         accept(symbol->unqualifiedName());
575         if (_item)
576             _item->setData(QVariant::fromValue(symbol));
577         (void) switchSymbol(previousSymbol);
578         return switchCompletionItem(previousItem);
579     }
580
581 protected:
582     Symbol *switchSymbol(Symbol *symbol)
583     {
584         Symbol *previousSymbol = _symbol;
585         _symbol = symbol;
586         return previousSymbol;
587     }
588
589     BasicProposalItem *switchCompletionItem(BasicProposalItem *item)
590     {
591         BasicProposalItem *previousItem = _item;
592         _item = item;
593         return previousItem;
594     }
595
596     BasicProposalItem *newCompletionItem(const Name *name)
597     {
598         BasicProposalItem *item = new CppAssistProposalItem;
599         item->setText(overview.prettyName(name));
600         return item;
601     }
602
603     virtual void visit(const Identifier *name)
604     { _item = newCompletionItem(name); }
605
606     virtual void visit(const TemplateNameId *name)
607     {
608         _item = newCompletionItem(name);
609         _item->setText(QLatin1String(name->identifier()->chars()));
610     }
611
612     virtual void visit(const DestructorNameId *name)
613     { _item = newCompletionItem(name); }
614
615     virtual void visit(const OperatorNameId *name)
616     { _item = newCompletionItem(name); }
617
618     virtual void visit(const ConversionNameId *name)
619     { _item = newCompletionItem(name); }
620
621     virtual void visit(const QualifiedNameId *name)
622     { _item = newCompletionItem(name->name()); }
623 };
624
625 Class *asClassOrTemplateClassType(FullySpecifiedType ty)
626 {
627     if (Class *classTy = ty->asClassType())
628         return classTy;
629     else if (Template *templ = ty->asTemplateType()) {
630         if (Symbol *decl = templ->declaration())
631             return decl->asClass();
632     }
633     return 0;
634 }
635
636 Scope *enclosingNonTemplateScope(Symbol *symbol)
637 {
638     if (symbol) {
639         if (Scope *scope = symbol->enclosingScope()) {
640             if (Template *templ = scope->asTemplate())
641                 return templ->enclosingScope();
642             return scope;
643         }
644     }
645     return 0;
646 }
647
648 Function *asFunctionOrTemplateFunctionType(FullySpecifiedType ty)
649 {
650     if (Function *funTy = ty->asFunctionType())
651         return funTy;
652     else if (Template *templ = ty->asTemplateType()) {
653         if (Symbol *decl = templ->declaration())
654             return decl->asFunction();
655     }
656     return 0;
657 }
658
659 } // Anonymous
660
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)
683     , m_hintProposal(0)
684 {}
685
686 CppCompletionAssistProcessor::~CppCompletionAssistProcessor()
687 {}
688
689 IAssistProposal * CppCompletionAssistProcessor::perform(const IAssistInterface *interface)
690 {
691     m_interface.reset(static_cast<const CppCompletionAssistInterface *>(interface));
692
693     if (interface->reason() != ExplicitlyInvoked && !accepts())
694         return 0;
695
696     int index = startCompletionHelper();
697     if (index != -1) {
698         if (m_hintProposal)
699             return m_hintProposal;
700
701         if (m_model->m_completionOperator != T_EOF_SYMBOL)
702             m_model->m_sortable = true;
703         else
704             m_model->m_sortable = false;
705         return createContentProposal();
706     }
707
708     return 0;
709 }
710
711 bool CppCompletionAssistProcessor::accepts() const
712 {
713     const int pos = m_interface->position();
714     unsigned token = T_EOF_SYMBOL;
715
716     const int start = startOfOperator(pos, &token, /*want function call=*/ true);
717     if (start != pos) {
718         if (token == T_POUND) {
719             const int column = pos - m_interface->document()->findBlock(start).position();
720             if (column != 1)
721                 return false;
722         }
723
724         return true;
725     } else {
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());
735                     tc.setPosition(pos);
736
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);
744
745                     if (!tk.isComment() && !tk.isLiteral()) {
746                         return true;
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"))) {
758                             return true;
759                         }
760                     }
761                 }
762             }
763         }
764     }
765
766     return false;
767 }
768
769 IAssistProposal *CppCompletionAssistProcessor::createContentProposal()
770 {
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>()) {
777             ++it;
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();
785                         }
786                     }
787                 }
788             }
789         } else {
790             delete *it;
791             it = m_completions.erase(it);
792         }
793     }
794
795     m_model->loadContent(m_completions);
796     return new CppAssistProposal(m_startPosition, m_model.take());
797 }
798
799 IAssistProposal *CppCompletionAssistProcessor::createHintProposal(
800     QList<CPlusPlus::Function *> functionSymbols) const
801 {
802     IFunctionHintProposalModel *model =
803             new CppFunctionHintModel(functionSymbols, m_model->m_typeOfExpression);
804     IAssistProposal *proposal = new FunctionHintProposal(m_startPosition, model);
805     return proposal;
806 }
807
808 int CppCompletionAssistProcessor::startOfOperator(int pos,
809                                                   unsigned *kind,
810                                                   bool wantFunctionCall) const
811 {
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();
815
816     int start = pos - activationSequenceChar(ch, ch2, ch3, kind, wantFunctionCall);
817     if (start != pos) {
818         QTextCursor tc(m_interface->document());
819         tc.setPosition(pos);
820
821         // Include completion: make sure the quote character is the first one on the line
822         if (*kind == T_STRING_LITERAL) {
823             QTextCursor s = tc;
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;
828                 start = pos;
829             }
830         }
831
832         if (*kind == T_COMMA) {
833             ExpressionUnderCursor expressionUnderCursor;
834             if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
835                 *kind = T_EOF_SYMBOL;
836                 start = pos;
837             }
838         }
839
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);
847
848         if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
849             *kind = T_EOF_SYMBOL;
850             start = pos;
851         }
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;
858             start = pos;
859         }
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;
863             start = pos;
864         }
865         else if (*kind == T_LPAREN) {
866             if (tokenIdx > 0) {
867                 const Token &previousToken = tokens.at(tokenIdx - 1); // look at the token at the left of T_LPAREN
868                 switch (previousToken.kind()) {
869                 case T_IDENTIFIER:
870                 case T_GREATER:
871                 case T_SIGNAL:
872                 case T_SLOT:
873                     break; // good
874
875                 default:
876                     // that's a bad token :)
877                     *kind = T_EOF_SYMBOL;
878                     start = pos;
879                 }
880             }
881         }
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")) {
894                         include = true;
895                     }
896                 }
897             }
898
899             if (!include) {
900                 *kind = T_EOF_SYMBOL;
901                 start = pos;
902             }
903         }
904     }
905
906     return start;
907 }
908
909 int CppCompletionAssistProcessor::findStartOfName(int pos) const
910 {
911     if (pos == -1)
912         pos = m_interface->position();
913     QChar chr;
914
915     // Skip to the start of a name
916     do {
917         chr = m_interface->characterAt(--pos);
918     } while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
919
920     return pos + 1;
921 }
922
923 int CppCompletionAssistProcessor::startCompletionHelper()
924 {
925     if (m_objcEnabled) {
926         if (tryObjCCompletion())
927             return m_startPosition;
928     }
929
930     const int startOfName = findStartOfName();
931     m_startPosition = startOfName;
932     m_model->m_completionOperator = T_EOF_SYMBOL;
933
934     int endOfOperator = m_startPosition;
935
936     // Skip whitespace preceding this position
937     while (m_interface->characterAt(endOfOperator - 1).isSpace())
938         --endOfOperator;
939
940     int endOfExpression = startOfOperator(endOfOperator,
941                                           &m_model->m_completionOperator,
942                                           /*want function call =*/ true);
943
944     const Core::IFile *file = m_interface->file();
945     QString fileName = file->fileName();
946
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;
951     }
952
953     // Pre-processor completion
954     if (m_model->m_completionOperator == T_POUND) {
955         completePreprocessor();
956         m_startPosition = startOfName;
957         return m_startPosition;
958     }
959
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) {
964
965         QTextCursor c(m_interface->document());
966         c.setPosition(endOfExpression);
967         if (completeInclude(c))
968             m_startPosition = startOfName;
969         return m_startPosition;
970     }
971
972     ExpressionUnderCursor expressionUnderCursor;
973     QTextCursor tc(m_interface->document());
974
975     if (m_model->m_completionOperator == T_COMMA) {
976         tc.setPosition(endOfExpression);
977         const int start = expressionUnderCursor.startOfFunctionCall(tc);
978         if (start == -1) {
979             m_model->m_completionOperator = T_EOF_SYMBOL;
980             return -1;
981         }
982
983         endOfExpression = start;
984         m_startPosition = start + 1;
985         m_model->m_completionOperator = T_LPAREN;
986     }
987
988     QString expression;
989     int startOfExpression = m_interface->position();
990     tc.setPosition(endOfExpression);
991
992     if (m_model->m_completionOperator) {
993         expression = expressionUnderCursor(tc);
994         startOfExpression = endOfExpression - expression.length();
995
996         if (m_model->m_completionOperator == T_LPAREN) {
997             if (expression.endsWith(QLatin1String("SIGNAL")))
998                 m_model->m_completionOperator = T_SIGNAL;
999
1000             else if (expression.endsWith(QLatin1String("SLOT")))
1001                 m_model->m_completionOperator = T_SLOT;
1002
1003             else if (m_interface->position() != endOfOperator) {
1004                 // We don't want a function completion when the cursor isn't at the opening brace
1005                 expression.clear();
1006                 m_model->m_completionOperator = T_EOF_SYMBOL;
1007                 m_startPosition = startOfName;
1008                 startOfExpression = m_interface->position();
1009             }
1010         }
1011     } else if (expression.isEmpty()) {
1012         while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
1013             --startOfExpression;
1014     }
1015
1016     int line = 0, column = 0;
1017     Convenience::convertPosition(m_interface->document(), startOfExpression, &line, &column);
1018     return startCompletionInternal(fileName, line, column, expression, endOfExpression);
1019 }
1020
1021 bool CppCompletionAssistProcessor::tryObjCCompletion()
1022 {
1023     int end = m_interface->position();
1024     while (m_interface->characterAt(end).isSpace())
1025         ++end;
1026     if (m_interface->characterAt(end) != QLatin1Char(']'))
1027         return false;
1028
1029     QTextCursor tc(m_interface->document());
1030     tc.setPosition(end);
1031     BackwardsScanner tokens(tc);
1032     if (tokens[tokens.startToken() - 1].isNot(T_RBRACKET))
1033         return false;
1034
1035     const int start = tokens.startOfMatchingBrace(tokens.startToken());
1036     if (start == tokens.startToken())
1037         return false;
1038
1039     const int startPos = tokens[start].begin() + tokens.startPosition();
1040     const QString expr = m_interface->textAt(startPos, m_interface->position() - startPos);
1041
1042     Document::Ptr thisDocument = m_interface->snapshot().document(m_interface->file()->fileName());
1043     if (! thisDocument)
1044         return false;
1045
1046     m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
1047
1048     int line = 0, column = 0;
1049     Convenience::convertPosition(m_interface->document(), m_interface->position(), &line, &column);
1050     Scope *scope = thisDocument->scopeAt(line, column);
1051     if (!scope)
1052         return false;
1053
1054     const QList<LookupItem> items = (*m_model->m_typeOfExpression)(expr, scope);
1055     LookupContext lookupContext(thisDocument, m_interface->snapshot());
1056
1057     foreach (const LookupItem &item, items) {
1058         FullySpecifiedType ty = item.type().simplified();
1059         if (ty->isPointerType()) {
1060             ty = ty->asPointerType()->elementType().simplified();
1061
1062             if (NamedType *namedTy = ty->asNamedType()) {
1063                 ClassOrNamespace *binding = lookupContext.lookupType(namedTy->name(), item.scope());
1064                 completeObjCMsgSend(binding, false);
1065             }
1066         } else {
1067             if (ObjCClass *clazz = ty->asObjCClassType()) {
1068                 ClassOrNamespace *binding = lookupContext.lookupType(clazz->name(), item.scope());
1069                 completeObjCMsgSend(binding, true);
1070             }
1071         }
1072     }
1073
1074     if (m_completions.isEmpty())
1075         return false;
1076
1077     m_startPosition = m_interface->position();
1078     return true;
1079 }
1080
1081 void CppCompletionAssistProcessor::addCompletionItem(const QString &text,
1082                                                      const QIcon &icon,
1083                                                      int order,
1084                                                      const QVariant &data)
1085 {
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);
1092 }
1093
1094 void CppCompletionAssistProcessor::addCompletionItem(CPlusPlus::Symbol *symbol)
1095 {
1096     ConvertToCompletionItem toCompletionItem;
1097     BasicProposalItem *item = toCompletionItem(symbol);
1098     if (item) {
1099         item->setIcon(m_icons.iconForSymbol(symbol));
1100         m_completions.append(item);
1101     }
1102 }
1103
1104 void CppCompletionAssistProcessor::completeObjCMsgSend(CPlusPlus::ClassOrNamespace *binding,
1105                                                        bool staticClassAccess)
1106 {
1107     QList<Scope*> memberScopes;
1108     foreach (Symbol *s, binding->symbols()) {
1109         if (ObjCClass *c = s->asObjCClass())
1110             memberScopes.append(c);
1111     }
1112
1113     foreach (Scope *scope, memberScopes) {
1114         for (unsigned i = 0; i < scope->memberCount(); ++i) {
1115             Symbol *symbol = scope->memberAt(i);
1116
1117             if (ObjCMethod *method = symbol->type()->asObjCMethodType()) {
1118                 if (method->isStatic() == staticClassAccess) {
1119                     Overview oo;
1120                     const SelectorNameId *selectorName =
1121                             method->name()->asSelectorNameId();
1122                     QString text;
1123                     QString data;
1124                     if (selectorName->hasArguments()) {
1125                         for (unsigned i = 0; i < selectorName->nameCount(); ++i) {
1126                             if (i > 0)
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;
1137                         }
1138                     } else {
1139                         text = selectorName->identifier()->chars();
1140                     }
1141                     data = text;
1142
1143                     if (!text.isEmpty())
1144                         addCompletionItem(text, QIcon(), 0, QVariant::fromValue(data));
1145                 }
1146             }
1147         }
1148     }
1149 }
1150
1151 bool CppCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
1152 {
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;
1162         } else {
1163             m_model->m_completionOperator = T_STRING_LITERAL;
1164         }
1165         if (startCharPos != -1)
1166             directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
1167     }
1168
1169     // Make completion for all relevant includes
1170     QStringList includePaths = m_interface->includePaths();
1171     const QString &currentFilePath = QFileInfo(m_interface->file()->fileName()).path();
1172     if (!includePaths.contains(currentFilePath))
1173         includePaths.append(currentFilePath);
1174
1175     const Core::MimeType mimeType =
1176             Core::ICore::instance()->mimeDatabase()->findByType(QLatin1String("text/x-c++hdr"));
1177     const QStringList suffixes = mimeType.suffixes();
1178
1179     foreach (const QString &includePath, includePaths) {
1180         QString realPath = includePath;
1181         if (!directoryPrefix.isEmpty()) {
1182             realPath += QLatin1Char('/');
1183             realPath += directoryPrefix;
1184         }
1185         completeInclude(realPath, suffixes);
1186     }
1187
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");
1194         }
1195         completeInclude(realPath, suffixes);
1196     }
1197
1198     return !m_completions.isEmpty();
1199 }
1200
1201 void CppCompletionAssistProcessor::completeInclude(const QString &realPath,
1202                                                    const QStringList &suffixes)
1203 {
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());
1214         }
1215     }
1216 }
1217
1218 void CppCompletionAssistProcessor::completePreprocessor()
1219 {
1220     foreach (const QString &preprocessorCompletion, preprocessorCompletions)
1221         addCompletionItem(preprocessorCompletion);
1222
1223     if (objcKeywordsWanted())
1224         addCompletionItem(QLatin1String("import"));
1225 }
1226
1227 bool CppCompletionAssistProcessor::objcKeywordsWanted() const
1228 {
1229     if (!m_objcEnabled)
1230         return false;
1231
1232     const Core::IFile *file = m_interface->file();
1233     QString fileName = file->fileName();
1234
1235     const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase();
1236     return mdb->findByFile(fileName).type() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE;
1237 }
1238
1239 int CppCompletionAssistProcessor::startCompletionInternal(const QString fileName,
1240                                                           unsigned line, unsigned column,
1241                                                           const QString &expr,
1242                                                           int endOfExpression)
1243 {
1244     QString expression = expr.trimmed();
1245
1246     Document::Ptr thisDocument = m_interface->snapshot().document(fileName);
1247     if (! thisDocument)
1248         return -1;
1249
1250     m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());
1251
1252     Scope *scope = thisDocument->scopeAt(line, column);
1253     Q_ASSERT(scope != 0);
1254
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())
1260                 return -1;
1261             return m_startPosition;
1262         }
1263
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");
1267         }
1268     }
1269
1270     QList<LookupItem> results =
1271             (*m_model->m_typeOfExpression)(expression, scope, TypeOfExpression::Preprocess);
1272
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);
1278             }
1279
1280             if (results.isEmpty())
1281                 return -1;
1282
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())
1287                 --index;
1288             index = findStartOfName(index);
1289
1290             QTextCursor tc(m_interface->document());
1291             tc.setPosition(index);
1292
1293             ExpressionUnderCursor expressionUnderCursor;
1294             const QString baseExpression = expressionUnderCursor(tc);
1295
1296             // Resolve the type of this expression
1297             const QList<LookupItem> results =
1298                     (*m_model->m_typeOfExpression)(baseExpression, scope,
1299                                      TypeOfExpression::Preprocess);
1300
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;
1306
1307                     break;
1308                 }
1309             }
1310             return -1;
1311
1312         } else {
1313             // nothing to do.
1314             return -1;
1315
1316         }
1317     }
1318
1319     switch (m_model->m_completionOperator) {
1320     case T_LPAREN:
1321         if (completeConstructorOrFunction(results, endOfExpression, false))
1322             return m_startPosition;
1323         break;
1324
1325     case T_DOT:
1326     case T_ARROW:
1327         if (completeMember(results))
1328             return m_startPosition;
1329         break;
1330
1331     case T_COLON_COLON:
1332         if (completeScope(results))
1333             return m_startPosition;
1334         break;
1335
1336     case T_SIGNAL:
1337         if (completeSignal(results))
1338             return m_startPosition;
1339         break;
1340
1341     case T_SLOT:
1342         if (completeSlot(results))
1343             return m_startPosition;
1344         break;
1345
1346     default:
1347         break;
1348     } // end of switch
1349
1350     // nothing to do.
1351     return -1;
1352 }
1353
1354 void CppCompletionAssistProcessor::globalCompletion(CPlusPlus::Scope *currentScope)
1355 {
1356     const LookupContext &context = m_model->m_typeOfExpression->context();
1357
1358     if (m_model->m_completionOperator == T_COLON_COLON) {
1359         completeNamespace(context.globalNamespace());
1360         return;
1361     }
1362
1363     QList<ClassOrNamespace *> usingBindings;
1364     ClassOrNamespace *currentBinding = 0;
1365
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())
1372                         continue;
1373                     else if (UsingNamespaceDirective *u = member->asUsingNamespaceDirective()) {
1374                         if (ClassOrNamespace *b = binding->lookupType(u->name()))
1375                             usingBindings.append(b);
1376                     }
1377                 }
1378             }
1379         } else if (scope->isFunction() || scope->isClass() || scope->isNamespace()) {
1380             currentBinding = context.lookupType(scope);
1381             break;
1382         }
1383     }
1384
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));
1389             }
1390         } else if (scope->isFunction()) {
1391             Function *fun = scope->asFunction();
1392             for (unsigned i = 0; i < fun->argumentCount(); ++i) {
1393                 addCompletionItem(fun->argumentAt(i));
1394             }
1395             break;
1396         } else {
1397             break;
1398         }
1399     }
1400
1401     for (; currentBinding; currentBinding = currentBinding->parent()) {
1402         const QList<Symbol *> symbols = currentBinding->symbols();
1403
1404         if (! symbols.isEmpty()) {
1405             if (symbols.first()->isNamespace())
1406                 completeNamespace(currentBinding);
1407             else
1408                 completeClass(currentBinding);
1409         }
1410     }
1411
1412     foreach (ClassOrNamespace *b, usingBindings)
1413         completeNamespace(b);
1414
1415     addKeywords();
1416     addMacros(QLatin1String("<configuration>"), context.snapshot());
1417     addMacros(context.thisDocument()->fileName(), context.snapshot());
1418     addSnippets();
1419 }
1420
1421 bool CppCompletionAssistProcessor::completeMember(const QList<CPlusPlus::LookupItem> &baseResults)
1422 {
1423     const LookupContext &context = m_model->m_typeOfExpression->context();
1424
1425     if (baseResults.isEmpty())
1426         return false;
1427
1428     ResolveExpression resolveExpression(context);
1429
1430     if (ClassOrNamespace *binding =
1431             resolveExpression.baseExpression(baseResults,
1432                                              m_model->m_completionOperator,
1433                                              &m_model->m_replaceDotForArrow)) {
1434         if (binding)
1435             completeClass(binding, /*static lookup = */ false);
1436
1437         return ! m_completions.isEmpty();
1438     }
1439
1440     return false;
1441 }
1442
1443 bool CppCompletionAssistProcessor::completeScope(const QList<CPlusPlus::LookupItem> &results)
1444 {
1445     const LookupContext &context = m_model->m_typeOfExpression->context();
1446     if (results.isEmpty())
1447         return false;
1448
1449     foreach (const LookupItem &result, results) {
1450         FullySpecifiedType ty = result.type();
1451         Scope *scope = result.scope();
1452
1453         if (NamedType *namedTy = ty->asNamedType()) {
1454             if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
1455                 completeClass(b);
1456                 break;
1457             }
1458
1459         } else if (Class *classTy = ty->asClassType()) {
1460             if (ClassOrNamespace *b = context.lookupType(classTy)) {
1461                 completeClass(b);
1462                 break;
1463             }
1464
1465         } else if (Namespace *nsTy = ty->asNamespaceType()) {
1466             if (ClassOrNamespace *b = context.lookupType(nsTy)) {
1467                 completeNamespace(b);
1468                 break;
1469             }
1470
1471         }
1472     }
1473
1474     return ! m_completions.isEmpty();
1475 }
1476
1477 void CppCompletionAssistProcessor::completeNamespace(CPlusPlus::ClassOrNamespace *b)
1478 {
1479     QSet<ClassOrNamespace *> bindingsVisited;
1480     QList<ClassOrNamespace *> bindingsToVisit;
1481     bindingsToVisit.append(b);
1482
1483     while (! bindingsToVisit.isEmpty()) {
1484         ClassOrNamespace *binding = bindingsToVisit.takeFirst();
1485         if (! binding || bindingsVisited.contains(binding))
1486             continue;
1487
1488         bindingsVisited.insert(binding);
1489         bindingsToVisit += binding->usings();
1490
1491         QList<Scope *> scopesToVisit;
1492         QSet<Scope *> scopesVisited;
1493
1494         foreach (Symbol *bb, binding->symbols()) {
1495             if (Namespace *ns = bb->asNamespace())
1496                 scopesToVisit.append(ns);
1497         }
1498
1499         foreach (Enum *e, binding->enums()) {
1500             scopesToVisit.append(e);
1501         }
1502
1503         while (! scopesToVisit.isEmpty()) {
1504             Scope *scope = scopesToVisit.takeFirst();
1505             if (! scope || scopesVisited.contains(scope))
1506                 continue;
1507
1508             scopesVisited.insert(scope);
1509
1510             for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
1511                 Symbol *member = *it;
1512                 addCompletionItem(member);
1513             }
1514         }
1515     }
1516 }
1517
1518 void CppCompletionAssistProcessor::completeClass(CPlusPlus::ClassOrNamespace *b, bool staticLookup)
1519 {
1520     QSet<ClassOrNamespace *> bindingsVisited;
1521     QList<ClassOrNamespace *> bindingsToVisit;
1522     bindingsToVisit.append(b);
1523
1524     while (! bindingsToVisit.isEmpty()) {
1525         ClassOrNamespace *binding = bindingsToVisit.takeFirst();
1526         if (! binding || bindingsVisited.contains(binding))
1527             continue;
1528
1529         bindingsVisited.insert(binding);
1530         bindingsToVisit += binding->usings();
1531
1532         QList<Scope *> scopesToVisit;
1533         QSet<Scope *> scopesVisited;
1534
1535         foreach (Symbol *bb, binding->symbols()) {
1536             if (Class *k = bb->asClass())
1537                 scopesToVisit.append(k);
1538         }
1539
1540         foreach (Enum *e, binding->enums())
1541             scopesToVisit.append(e);
1542
1543         while (! scopesToVisit.isEmpty()) {
1544             Scope *scope = scopesToVisit.takeFirst();
1545             if (! scope || scopesVisited.contains(scope))
1546                 continue;
1547
1548             scopesVisited.insert(scope);
1549
1550             addCompletionItem(scope); // add a completion item for the injected class name.
1551
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()) {
1557                     continue;
1558                 } else if (! staticLookup && (member->isTypedef() ||
1559                                             member->isEnum()    ||
1560                                             member->isClass())) {
1561                     continue;
1562                 }
1563
1564                 addCompletionItem(member);
1565             }
1566         }
1567     }
1568 }
1569
1570 bool CppCompletionAssistProcessor::completeQtMethod(const QList<CPlusPlus::LookupItem> &results, bool wantSignals)
1571 {
1572     if (results.isEmpty())
1573         return false;
1574
1575     const LookupContext &context = m_model->m_typeOfExpression->context();
1576
1577     ConvertToCompletionItem toCompletionItem;
1578     Overview o;
1579     o.setShowReturnTypes(false);
1580     o.setShowArgumentNames(false);
1581     o.setShowFunctionSignatures(true);
1582
1583     QSet<QString> signatures;
1584     foreach (const LookupItem &p, results) {
1585         FullySpecifiedType ty = p.type().simplified();
1586
1587         if (PointerType *ptrTy = ty->asPointerType())
1588             ty = ptrTy->elementType().simplified();
1589         else
1590             continue; // not a pointer or a reference to a pointer.
1591
1592         NamedType *namedTy = ty->asNamedType();
1593         if (! namedTy) // not a class name.
1594             continue;
1595
1596         ClassOrNamespace *b = context.lookupType(namedTy->name(), p.scope());
1597         if (! b)
1598             continue;
1599
1600         QList<ClassOrNamespace *>todo;
1601         QSet<ClassOrNamespace *> processed;
1602         QList<Scope *> scopes;
1603         todo.append(b);
1604         while (!todo.isEmpty()) {
1605             ClassOrNamespace *binding = todo.takeLast();
1606             if (!processed.contains(binding)) {
1607                 processed.insert(binding);
1608
1609                 foreach (Symbol *s, binding->symbols())
1610                     if (Class *clazz = s->asClass())
1611                         scopes.append(clazz);
1612
1613                 todo.append(binding->usings());
1614             }
1615         }
1616
1617         foreach (Scope *scope, scopes) {
1618             if (! scope->isClass())
1619                 continue;
1620
1621             for (unsigned i = 0; i < scope->memberCount(); ++i) {
1622                 Symbol *member = scope->memberAt(i);
1623                 Function *fun = member->type()->asFunctionType();
1624                 if (! fun)
1625                     continue;
1626                 if (wantSignals && ! fun->isSignal())
1627                     continue;
1628                 else if (! wantSignals && ! fun->isSlot())
1629                     continue;
1630
1631                 unsigned count = fun->argumentCount();
1632                 while (true) {
1633                     QString signature;
1634                     signature += Overview().prettyName(fun->name());
1635                     signature += QLatin1Char('(');
1636                     for (unsigned i = 0; i < count; ++i) {
1637                         Symbol *arg = fun->argumentAt(i);
1638                         if (i != 0)
1639                             signature += QLatin1Char(',');
1640                         signature += o.prettyType(arg->type());
1641                     }
1642                     signature += QLatin1Char(')');
1643
1644                     const QByteArray normalized =
1645                             QMetaObject::normalizedSignature(signature.toLatin1());
1646
1647                     signature = QString::fromLatin1(normalized, normalized.size());
1648
1649                     if (! signatures.contains(signature)) {
1650                         BasicProposalItem *ci = toCompletionItem(fun);
1651                         if (!ci)
1652                             break;
1653                         signatures.insert(signature);
1654                         ci->setText(signature); // fix the completion item.
1655                         m_completions.append(ci);
1656                     }
1657
1658                     if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer())
1659                         --count;
1660                     else
1661                         break;
1662                 }
1663             }
1664         }
1665     }
1666
1667     return ! m_completions.isEmpty();
1668 }
1669
1670 void CppCompletionAssistProcessor::addSnippets()
1671 {
1672     m_completions.append(m_snippetCollector.collect());
1673 }
1674
1675 void CppCompletionAssistProcessor::addKeywords()
1676 {
1677     int keywordLimit = T_FIRST_OBJC_AT_KEYWORD;
1678     if (objcKeywordsWanted())
1679         keywordLimit = T_LAST_OBJC_AT_KEYWORD + 1;
1680
1681     // keyword completion items.
1682     for (int i = T_FIRST_KEYWORD; i < keywordLimit; ++i)
1683         addCompletionItem(QLatin1String(Token::name(i)), m_icons.keywordIcon());
1684 }
1685
1686 void CppCompletionAssistProcessor::addMacros(const QString &fileName, const CPlusPlus::Snapshot &snapshot)
1687 {
1688     QSet<QString> processed;
1689     QSet<QString> definedMacros;
1690
1691     addMacros_helper(snapshot, fileName, &processed, &definedMacros);
1692
1693     foreach (const QString &macroName, definedMacros)
1694         addCompletionItem(macroName, m_icons.macroIcon());
1695 }
1696
1697 void CppCompletionAssistProcessor::addMacros_helper(const CPlusPlus::Snapshot &snapshot,
1698                                                     const QString &fileName,
1699                                                     QSet<QString> *processed,
1700                                                     QSet<QString> *definedMacros)
1701 {
1702     Document::Ptr doc = snapshot.document(fileName);
1703
1704     if (! doc || processed->contains(doc->fileName()))
1705         return;
1706
1707     processed->insert(doc->fileName());
1708
1709     foreach (const Document::Include &i, doc->includes()) {
1710         addMacros_helper(snapshot, i.fileName(), processed, definedMacros);
1711     }
1712
1713     foreach (const Macro &macro, doc->definedMacros()) {
1714         const QString macroName = QString::fromUtf8(macro.name().constData(), macro.name().length());
1715         if (! macro.isHidden())
1716             definedMacros->insert(macroName);
1717         else
1718             definedMacros->remove(macroName);
1719     }
1720 }
1721
1722 bool CppCompletionAssistProcessor::completeConstructorOrFunction(const QList<CPlusPlus::LookupItem> &results,
1723                                                                  int endOfExpression,
1724                                                                  bool toolTipOnly)
1725 {
1726     const LookupContext &context = m_model->m_typeOfExpression->context();
1727     QList<Function *> functions;
1728
1729     foreach (const LookupItem &result, results) {
1730         FullySpecifiedType exprTy = result.type().simplified();
1731
1732         if (Class *klass = asClassOrTemplateClassType(exprTy)) {
1733             const Name *className = klass->name();
1734             if (! className)
1735                 continue; // nothing to do for anonymous classes.
1736
1737             for (unsigned i = 0; i < klass->memberCount(); ++i) {
1738                 Symbol *member = klass->memberAt(i);
1739                 const Name *memberName = member->name();
1740
1741                 if (! memberName)
1742                     continue; // skip anonymous member.
1743
1744                 else if (memberName->isQualifiedNameId())
1745                     continue; // skip
1746
1747                 if (Function *funTy = member->type()->asFunctionType()) {
1748                     if (memberName->isEqualTo(className)) {
1749                         // it's a ctor.
1750                         functions.append(funTy);
1751                     }
1752                 }
1753             }
1754
1755             break;
1756         }
1757     }
1758
1759     if (functions.isEmpty()) {
1760         foreach (const LookupItem &result, results) {
1761             FullySpecifiedType ty = result.type().simplified();
1762
1763             if (Function *fun = asFunctionOrTemplateFunctionType(ty)) {
1764
1765                 if (! fun->name())
1766                     continue;
1767                 else if (! functions.isEmpty() && enclosingNonTemplateScope(functions.first()) != enclosingNonTemplateScope(fun))
1768                     continue; // skip fun, it's an hidden declaration.
1769
1770                 bool newOverload = true;
1771
1772                 foreach (Function *f, functions) {
1773                     if (fun->isEqualTo(f)) {
1774                         newOverload = false;
1775                         break;
1776                     }
1777                 }
1778
1779                 if (newOverload)
1780                     functions.append(fun);
1781             }
1782         }
1783     }
1784
1785     if (functions.isEmpty()) {
1786         const Name *functionCallOp = context.control()->operatorNameId(OperatorNameId::FunctionCallOp);
1787
1788         foreach (const LookupItem &result, results) {
1789             FullySpecifiedType ty = result.type().simplified();
1790             Scope *scope = result.scope();
1791
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();
1797
1798                         if (Function *funTy = overloadTy->asFunctionType()) {
1799                             functions.append(funTy);
1800                         }
1801                     }
1802                 }
1803             }
1804         }
1805     }
1806
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.
1812
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) {
1817
1818         // function definitions will only happen in class or namespace scope,
1819         // so get the current location's enclosing scope.
1820
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;
1825
1826         // find a scope that encloses the current location, starting from the lastVisibileSymbol
1827         // and moving outwards
1828
1829         Scope *sc = context.thisDocument()->scopeAt(line, column);
1830
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;
1835
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("();");
1844
1845             Document::Ptr doc = Document::create(QLatin1String("<completion>"));
1846             doc->setSource(possibleDecl.toLatin1());
1847             if (doc->parse(Document::ParseDeclaration)) {
1848                 doc->check();
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()) {
1853
1854                         autocompleteSignature = true;
1855
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;
1864                             }
1865                         }
1866                     }
1867                 }
1868             }
1869
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);
1878                 if (!targetCoN)
1879                     targetCoN = context.globalNamespace();
1880                 UseMinimalNames q(targetCoN);
1881                 env.enter(&q);
1882                 Control *control = context.control().data();
1883
1884                 // set up signature autocompletion
1885                 foreach (Function *f, functions) {
1886                     Overview overview;
1887                     overview.setShowArgumentNames(true);
1888                     overview.setShowDefaultArguments(false);
1889
1890                     const FullySpecifiedType localTy = rewriteType(f->type(), &env, control);
1891
1892                     // gets: "parameter list) cv-spec",
1893                     QString completion = overview(localTy).mid(1);
1894
1895                     addCompletionItem(completion, QIcon(), 0,
1896                                       QVariant::fromValue(CompleteFunctionDeclaration(f)));
1897                 }
1898                 return true;
1899             }
1900         }
1901     }
1902
1903     if (! functions.empty() && !isDestructor) {
1904         m_hintProposal = createHintProposal(functions);
1905         return true;
1906     }
1907
1908     return false;
1909 }