cppcompletionassist.cpp 79.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

Leandro Melo's avatar
Leandro Melo committed
31
#include "cppcompletionassist.h"
32

33
#include "builtineditordocumentparser.h"
34
#include "cppdoxygen.h"
Leandro Melo's avatar
Leandro Melo committed
35
#include "cppmodelmanager.h"
36
#include "cpptoolsconstants.h"
37
#include "cpptoolsreuse.h"
38
#include "editordocumenthandle.h"
con's avatar
con committed
39 40

#include <coreplugin/icore.h>
Leandro Melo's avatar
Leandro Melo committed
41
#include <cppeditor/cppeditorconstants.h>
42
#include <texteditor/codeassist/assistproposalitem.h>
Leandro Melo's avatar
Leandro Melo committed
43 44 45 46
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/convenience.h>
47
#include <texteditor/snippets/snippet.h>
Leandro Melo's avatar
Leandro Melo committed
48 49
#include <texteditor/texteditorsettings.h>
#include <texteditor/completionsettings.h>
50

Eike Ziller's avatar
Eike Ziller committed
51
#include <utils/mimetypes/mimedatabase.h>
52
#include <utils/qtcassert.h>
con's avatar
con committed
53

54 55 56 57 58 59 60
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/CppRewriter.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/MatchingText.h>
#include <cplusplus/Overview.h>
#include <cplusplus/ResolveExpression.h>

61
#include <QDirIterator>
62 63 64 65
#include <QLatin1String>
#include <QTextCursor>
#include <QTextDocument>
#include <QIcon>
66

con's avatar
con committed
67
using namespace CPlusPlus;
Leandro Melo's avatar
Leandro Melo committed
68 69
using namespace CppEditor;
using namespace CppTools;
70
using namespace CppTools::Internal;
Leandro Melo's avatar
Leandro Melo committed
71
using namespace TextEditor;
con's avatar
con committed
72

Leandro Melo's avatar
Leandro Melo committed
73 74
namespace CppTools {
namespace Internal {
75

Leandro Melo's avatar
Leandro Melo committed
76 77 78 79 80
struct CompleteFunctionDeclaration
{
    explicit CompleteFunctionDeclaration(Function *f = 0)
        : function(f)
    {}
81

Leandro Melo's avatar
Leandro Melo committed
82 83
    Function *function;
};
84

Leandro Melo's avatar
Leandro Melo committed
85 86 87
// ---------------------
// CppAssistProposalItem
// ---------------------
88
class CppAssistProposalItem : public AssistProposalItem
89
{
Leandro Melo's avatar
Leandro Melo committed
90
public:
91
    CppAssistProposalItem() :
92
        m_isOverloaded(false) {}
93

94 95
    bool prematurelyApplies(const QChar &c) const override;
    void applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const override;
96

Leandro Melo's avatar
Leandro Melo committed
97 98 99
    bool isOverloaded() const { return m_isOverloaded; }
    void markAsOverloaded() { m_isOverloaded = true; }
    void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; }
100 101
    void keepTypeOfExpression(const QSharedPointer<TypeOfExpression> &typeOfExp)
    { m_typeOfExpression = typeOfExp; }
102

Leandro Melo's avatar
Leandro Melo committed
103 104 105
private:
    bool m_isOverloaded;
    mutable QChar m_typedChar;
106
    unsigned m_completionOperator;
107
    QSharedPointer<TypeOfExpression> m_typeOfExpression;
Leandro Melo's avatar
Leandro Melo committed
108
};
109

Leandro Melo's avatar
Leandro Melo committed
110 111
} // Internal
} // CppTools
112

Leandro Melo's avatar
Leandro Melo committed
113
Q_DECLARE_METATYPE(CppTools::Internal::CompleteFunctionDeclaration)
114

115 116 117 118 119 120 121 122
bool CppAssistProposalModel::isSortable(const QString &prefix) const
{
    if (m_completionOperator != T_EOF_SYMBOL)
        return true;

    return !prefix.isEmpty();
}

123
AssistProposalItem *CppAssistProposalModel::proposalItem(int index) const
Leandro Melo's avatar
Leandro Melo committed
124
{
125
    auto item = static_cast<AssistProposalItem *>(GenericProposalModel::proposalItem(index));
Leandro Melo's avatar
Leandro Melo committed
126 127 128
    if (!item->data().canConvert<QString>()) {
        CppAssistProposalItem *cppItem = static_cast<CppAssistProposalItem *>(item);
        cppItem->keepCompletionOperator(m_completionOperator);
129
        cppItem->keepTypeOfExpression(m_typeOfExpression);
Leandro Melo's avatar
Leandro Melo committed
130 131 132
    }
    return item;
}
133

Leandro Melo's avatar
Leandro Melo committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
bool CppAssistProposalItem::prematurelyApplies(const QChar &typedChar) const
{
    if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
        if (typedChar == QLatin1Char('(') || typedChar == QLatin1Char(',')) {
            m_typedChar = typedChar;
            return true;
        }
    } else if (m_completionOperator == T_STRING_LITERAL
               || m_completionOperator == T_ANGLE_STRING_LITERAL) {
        if (typedChar == QLatin1Char('/') && text().endsWith(QLatin1Char('/'))) {
            m_typedChar = typedChar;
            return true;
        }
    } else if (data().value<Symbol *>()) {
        if (typedChar == QLatin1Char(':')
                || typedChar == QLatin1Char(';')
                || typedChar == QLatin1Char('.')
                || typedChar == QLatin1Char(',')
                || typedChar == QLatin1Char('(')) {
            m_typedChar = typedChar;
            return true;
        }
    } else if (data().canConvert<CompleteFunctionDeclaration>()) {
        if (typedChar == QLatin1Char('(')) {
            m_typedChar = typedChar;
            return true;
        }
    }

    return false;
}

166
static bool isDereferenced(TextEditorWidget *editorWidget, int basePosition)
167
{
168
    QTextCursor cursor = editorWidget->textCursor();
169 170
    cursor.setPosition(basePosition);

171
    BackwardsScanner scanner(cursor, LanguageFeatures());
172 173 174 175 176 177 178 179 180 181 182 183 184 185
    for (int pos = scanner.startToken()-1; pos >= 0; pos--) {
        switch (scanner[pos].kind()) {
        case T_COLON_COLON:
        case T_IDENTIFIER:
            //Ignore scope specifiers
            break;

        case T_AMPER: return true;
        default:      return false;
        }
    }
    return false;
}

186
void CppAssistProposalItem::applyContextualContent(TextEditorWidget *editorWidget, int basePosition) const
Leandro Melo's avatar
Leandro Melo committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
{
    Symbol *symbol = 0;

    if (data().isValid())
        symbol = data().value<Symbol *>();

    QString toInsert;
    QString extraChars;
    int extraLength = 0;
    int cursorOffset = 0;

    bool autoParenthesesEnabled = true;

    if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
        toInsert = text();
        extraChars += QLatin1Char(')');

        if (m_typedChar == QLatin1Char('(')) // Eat the opening parenthesis
            m_typedChar = QChar();
    } else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
        toInsert = text();
        if (!toInsert.endsWith(QLatin1Char('/'))) {
            extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
        } else {
            if (m_typedChar == QLatin1Char('/')) // Eat the slash
                m_typedChar = QChar();
        }
    } else {
        toInsert = text();

217
        const CompletionSettings &completionSettings = TextEditorSettings::completionSettings();
Leandro Melo's avatar
Leandro Melo committed
218 219 220 221 222 223
        const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;

        if (autoInsertBrackets && symbol && symbol->type()) {
            if (Function *function = symbol->type()->asFunctionType()) {
                // If the member is a function, automatically place the opening parenthesis,
                // except when it might take template parameters.
224 225 226
                if (!function->hasReturnType()
                    && (function->unqualifiedName()
                    && !function->unqualifiedName()->isDestructorNameId())) {
Leandro Melo's avatar
Leandro Melo committed
227 228 229 230 231 232
                    // Don't insert any magic, since the user might have just wanted to select the class

                    /// ### port me
#if 0
                } else if (function->templateParameterCount() != 0 && typedChar != QLatin1Char('(')) {
                    // If there are no arguments, then we need the template specification
233
                    if (function->argumentCount() == 0)
Leandro Melo's avatar
Leandro Melo committed
234 235
                        extraChars += QLatin1Char('<');
#endif
236
                } else if (!isDereferenced(editorWidget, basePosition) && !function->isAmbiguous()) {
Leandro Melo's avatar
Leandro Melo committed
237 238 239 240 241 242 243 244 245 246 247 248 249
                    // When the user typed the opening parenthesis, he'll likely also type the closing one,
                    // in which case it would be annoying if we put the cursor after the already automatically
                    // inserted closing parenthesis.
                    const bool skipClosingParenthesis = m_typedChar != QLatin1Char('(');

                    if (completionSettings.m_spaceAfterFunctionName)
                        extraChars += QLatin1Char(' ');
                    extraChars += QLatin1Char('(');
                    if (m_typedChar == QLatin1Char('('))
                        m_typedChar = QChar();

                    // If the function doesn't return anything, automatically place the semicolon,
                    // unless we're doing a scope completion (then it might be function definition).
250
                    const QChar characterAtCursor = editorWidget->characterAt(editorWidget->position());
Leandro Melo's avatar
Leandro Melo committed
251 252 253 254 255 256 257 258 259 260
                    bool endWithSemicolon = m_typedChar == QLatin1Char(';')
                            || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON);
                    const QChar semicolon = m_typedChar.isNull() ? QLatin1Char(';') : m_typedChar;

                    if (endWithSemicolon && characterAtCursor == semicolon) {
                        endWithSemicolon = false;
                        m_typedChar = QChar();
                    }

                    // If the function takes no arguments, automatically place the closing parenthesis
261
                    if (!isOverloaded() && !function->hasArguments() && skipClosingParenthesis) {
Leandro Melo's avatar
Leandro Melo committed
262 263 264 265 266 267
                        extraChars += QLatin1Char(')');
                        if (endWithSemicolon) {
                            extraChars += semicolon;
                            m_typedChar = QChar();
                        }
                    } else if (autoParenthesesEnabled) {
268
                        const QChar lookAhead = editorWidget->characterAt(editorWidget->position() + 1);
Leandro Melo's avatar
Leandro Melo committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
                        if (MatchingText::shouldInsertMatchingText(lookAhead)) {
                            extraChars += QLatin1Char(')');
                            --cursorOffset;
                            if (endWithSemicolon) {
                                extraChars += semicolon;
                                --cursorOffset;
                                m_typedChar = QChar();
                            }
                        }
                        // TODO: When an opening parenthesis exists, the "semicolon" should really be
                        // inserted after the matching closing parenthesis.
                    }
                }
            }
        }

        if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) {
            if (m_typedChar == QLatin1Char('('))
                m_typedChar = QChar();

            // everything from the closing parenthesis on are extra chars, to
            // make sure an auto-inserted ")" gets replaced by ") const" if necessary
            int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
            extraChars = toInsert.mid(closingParen);
            toInsert.truncate(closingParen);
        }
    }

    // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
    if (!m_typedChar.isNull()) {
        extraChars += m_typedChar;
        if (cursorOffset != 0)
            --cursorOffset;
    }

304 305
    // Determine the length of characters that should just be kept on the editor, but do
    // not consider content that ends as an identifier (which could be undesired).
306
    const int lineEnd = editorWidget->position(EndOfLinePosition);
307
    const QString inEditor = editorWidget->textAt(editorWidget->position(), lineEnd - editorWidget->position());
308
    int preserveLength = 0;
309
    if (!inEditor.isEmpty()) {
310
        preserveLength = toInsert.length() - (editorWidget->position() - basePosition);
311
        const int inEditorLength = inEditor.length();
312
        while (preserveLength > 0) {
313 314
            if (inEditor.startsWith(toInsert.right(preserveLength))
                    && (inEditorLength == preserveLength
315
                        || !CppTools::isValidIdentifierChar(inEditor.at(preserveLength)))) {
Yuchen Deng's avatar
Yuchen Deng committed
316
                break;
317 318
            }
            --preserveLength;
Yuchen Deng's avatar
Yuchen Deng committed
319 320
        }
    }
321

Leandro Melo's avatar
Leandro Melo committed
322 323
    for (int i = 0; i < extraChars.length(); ++i) {
        const QChar a = extraChars.at(i);
324
        const QChar b = editorWidget->characterAt(editorWidget->position() + i + preserveLength);
Leandro Melo's avatar
Leandro Melo committed
325 326 327 328 329 330 331 332 333
        if (a == b)
            ++extraLength;
        else
            break;
    }

    toInsert += extraChars;

    // Insert the remainder of the name
334 335 336
    const int length = editorWidget->position() - basePosition + preserveLength + extraLength;
    editorWidget->setCursorPosition(basePosition);
    editorWidget->replace(length, toInsert);
Leandro Melo's avatar
Leandro Melo committed
337
    if (cursorOffset)
338
        editorWidget->setCursorPosition(editorWidget->position() + cursorOffset);
Leandro Melo's avatar
Leandro Melo committed
339 340 341 342 343
}

// --------------------
// CppFunctionHintModel
// --------------------
344
class CppFunctionHintModel : public IFunctionHintProposalModel
Leandro Melo's avatar
Leandro Melo committed
345 346
{
public:
347 348
    CppFunctionHintModel(QList<Function *> functionSymbols,
                         const QSharedPointer<TypeOfExpression> &typeOfExp)
Leandro Melo's avatar
Leandro Melo committed
349 350
        : m_functionSymbols(functionSymbols)
        , m_currentArg(-1)
351
        , m_typeOfExpression(typeOfExp)
Leandro Melo's avatar
Leandro Melo committed
352 353
    {}

354 355 356 357
    void reset() override {}
    int size() const override { return m_functionSymbols.size(); }
    QString text(int index) const override;
    int activeArgument(const QString &prefix) const override;
Leandro Melo's avatar
Leandro Melo committed
358 359 360 361

private:
    QList<Function *> m_functionSymbols;
    mutable int m_currentArg;
362
    QSharedPointer<TypeOfExpression> m_typeOfExpression;
Leandro Melo's avatar
Leandro Melo committed
363 364 365 366 367
};

QString CppFunctionHintModel::text(int index) const
{
    Overview overview;
368 369 370
    overview.showReturnTypes = true;
    overview.showArgumentNames = true;
    overview.markedArgument = m_currentArg + 1;
Leandro Melo's avatar
Leandro Melo committed
371 372
    Function *f = m_functionSymbols.at(index);

373 374 375
    const QString prettyMethod = overview.prettyType(f->type(), f->name());
    const int begin = overview.markedArgumentBegin;
    const int end = overview.markedArgumentEnd;
Leandro Melo's avatar
Leandro Melo committed
376 377

    QString hintText;
378
    hintText += prettyMethod.left(begin).toHtmlEscaped();
379
    hintText += QLatin1String("<b>");
380
    hintText += prettyMethod.mid(begin, end - begin).toHtmlEscaped();
381
    hintText += QLatin1String("</b>");
382
    hintText += prettyMethod.mid(end).toHtmlEscaped();
Leandro Melo's avatar
Leandro Melo committed
383 384 385 386 387 388 389 390
    return hintText;
}

int CppFunctionHintModel::activeArgument(const QString &prefix) const
{
    int argnr = 0;
    int parcount = 0;
    SimpleLexer tokenize;
391
    Tokens tokens = tokenize(prefix);
Leandro Melo's avatar
Leandro Melo committed
392 393 394 395 396 397
    for (int i = 0; i < tokens.count(); ++i) {
        const Token &tk = tokens.at(i);
        if (tk.is(T_LPAREN))
            ++parcount;
        else if (tk.is(T_RPAREN))
            --parcount;
398
        else if (!parcount && tk.is(T_COMMA))
Leandro Melo's avatar
Leandro Melo committed
399 400 401 402 403 404 405 406 407 408 409 410 411
            ++argnr;
    }

    if (parcount < 0)
        return -1;

    if (argnr != m_currentArg)
        m_currentArg = argnr;

    return argnr;
}

// ---------------------------
412
// InternalCompletionAssistProvider
Leandro Melo's avatar
Leandro Melo committed
413 414
// ---------------------------

415
IAssistProcessor *InternalCompletionAssistProvider::createProcessor() const
Leandro Melo's avatar
Leandro Melo committed
416
{
417
    return new InternalCppCompletionAssistProcessor;
Leandro Melo's avatar
Leandro Melo committed
418 419
}

420
AssistInterface *InternalCompletionAssistProvider::createAssistInterface(
421 422 423 424 425
        const QString &filePath,
        const TextEditorWidget *textEditorWidget,
        const LanguageFeatures &languageFeatures,
        int position,
        AssistReason reason) const
Leandro Melo's avatar
Leandro Melo committed
426
{
427
    QTC_ASSERT(textEditorWidget, return 0);
428

429 430
    return new CppCompletionAssistInterface(filePath,
                                            textEditorWidget,
431
                                            BuiltinEditorDocumentParser::get(filePath),
432 433 434
                                            languageFeatures,
                                            position,
                                            reason,
435
                                            CppModelManager::instance()->workingCopy());
Leandro Melo's avatar
Leandro Melo committed
436 437 438 439 440
}

// -----------------
// CppAssistProposal
// -----------------
441
class CppAssistProposal : public GenericProposal
Leandro Melo's avatar
Leandro Melo committed
442 443
{
public:
444
    CppAssistProposal(int cursorPos, GenericProposalModel *model)
445
        : GenericProposal(cursorPos, model)
Leandro Melo's avatar
Leandro Melo committed
446 447 448
        , m_replaceDotForArrow(static_cast<CppAssistProposalModel *>(model)->m_replaceDotForArrow)
    {}

449 450
    bool isCorrective() const override { return m_replaceDotForArrow; }
    void makeCorrection(TextEditorWidget *editorWidget) override;
Leandro Melo's avatar
Leandro Melo committed
451 452 453 454 455

private:
    bool m_replaceDotForArrow;
};

456
void CppAssistProposal::makeCorrection(TextEditorWidget *editorWidget)
Leandro Melo's avatar
Leandro Melo committed
457
{
458 459 460 461
    const int oldPosition = editorWidget->position();
    editorWidget->setCursorPosition(basePosition() - 1);
    editorWidget->replace(1, QLatin1String("->"));
    editorWidget->setCursorPosition(oldPosition + 1);
Leandro Melo's avatar
Leandro Melo committed
462 463 464 465 466 467 468 469
    moveBasePosition(1);
}

namespace {

class ConvertToCompletionItem: protected NameVisitor
{
    // The completion item.
470
    AssistProposalItem *_item;
Leandro Melo's avatar
Leandro Melo committed
471 472 473 474 475 476 477 478 479 480 481

    // The current symbol.
    Symbol *_symbol;

    // The pretty printer.
    Overview overview;

public:
    ConvertToCompletionItem()
        : _item(0)
        , _symbol(0)
482
    {
483 484
        overview.showReturnTypes = true;
        overview.showArgumentNames = true;
485
    }
Leandro Melo's avatar
Leandro Melo committed
486

487
    AssistProposalItem *operator()(Symbol *symbol)
Leandro Melo's avatar
Leandro Melo committed
488
    {
489
        //using declaration can be qualified
490 491
        if (!symbol || !symbol->name() || (symbol->name()->isQualifiedNameId()
                                           && !symbol->asUsingDeclaration()))
Leandro Melo's avatar
Leandro Melo committed
492 493
            return 0;

494
        AssistProposalItem *previousItem = switchCompletionItem(0);
Leandro Melo's avatar
Leandro Melo committed
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
        Symbol *previousSymbol = switchSymbol(symbol);
        accept(symbol->unqualifiedName());
        if (_item)
            _item->setData(QVariant::fromValue(symbol));
        (void) switchSymbol(previousSymbol);
        return switchCompletionItem(previousItem);
    }

protected:
    Symbol *switchSymbol(Symbol *symbol)
    {
        Symbol *previousSymbol = _symbol;
        _symbol = symbol;
        return previousSymbol;
    }

511
    AssistProposalItem *switchCompletionItem(AssistProposalItem *item)
Leandro Melo's avatar
Leandro Melo committed
512
    {
513
        AssistProposalItem *previousItem = _item;
Leandro Melo's avatar
Leandro Melo committed
514 515 516 517
        _item = item;
        return previousItem;
    }

518
    AssistProposalItem *newCompletionItem(const Name *name)
Leandro Melo's avatar
Leandro Melo committed
519
    {
520
        AssistProposalItem *item = new CppAssistProposalItem;
Leandro Melo's avatar
Leandro Melo committed
521 522 523 524
        item->setText(overview.prettyName(name));
        return item;
    }

525
    void visit(const Identifier *name)
526 527
    {
        _item = newCompletionItem(name);
528
        if (!_symbol->isScope() || _symbol->isFunction())
529 530
            _item->setDetail(overview.prettyType(_symbol->type(), name));
    }
Leandro Melo's avatar
Leandro Melo committed
531

532
    void visit(const TemplateNameId *name)
Leandro Melo's avatar
Leandro Melo committed
533 534
    {
        _item = newCompletionItem(name);
535
        _item->setText(QString::fromUtf8(name->identifier()->chars(), name->identifier()->size()));
Leandro Melo's avatar
Leandro Melo committed
536 537
    }

538
    void visit(const DestructorNameId *name)
Leandro Melo's avatar
Leandro Melo committed
539 540
    { _item = newCompletionItem(name); }

541
    void visit(const OperatorNameId *name)
542 543 544 545
    {
        _item = newCompletionItem(name);
        _item->setDetail(overview.prettyType(_symbol->type(), name));
    }
Leandro Melo's avatar
Leandro Melo committed
546

547
    void visit(const ConversionNameId *name)
Leandro Melo's avatar
Leandro Melo committed
548 549
    { _item = newCompletionItem(name); }

550
    void visit(const QualifiedNameId *name)
Leandro Melo's avatar
Leandro Melo committed
551 552 553 554 555 556 557
    { _item = newCompletionItem(name->name()); }
};

Class *asClassOrTemplateClassType(FullySpecifiedType ty)
{
    if (Class *classTy = ty->asClassType())
        return classTy;
558
    if (Template *templ = ty->asTemplateType()) {
Leandro Melo's avatar
Leandro Melo committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
        if (Symbol *decl = templ->declaration())
            return decl->asClass();
    }
    return 0;
}

Scope *enclosingNonTemplateScope(Symbol *symbol)
{
    if (symbol) {
        if (Scope *scope = symbol->enclosingScope()) {
            if (Template *templ = scope->asTemplate())
                return templ->enclosingScope();
            return scope;
        }
    }
    return 0;
}

Function *asFunctionOrTemplateFunctionType(FullySpecifiedType ty)
{
    if (Function *funTy = ty->asFunctionType())
        return funTy;
581
    if (Template *templ = ty->asTemplateType()) {
Leandro Melo's avatar
Leandro Melo committed
582 583 584 585 586 587
        if (Symbol *decl = templ->declaration())
            return decl->asFunction();
    }
    return 0;
}

588 589 590 591 592 593 594 595 596 597
bool isQPrivateSignal(const Symbol *symbol)
{
    if (!symbol)
        return false;

    static Identifier qPrivateSignalIdentifier("QPrivateSignal", 14);

    if (FullySpecifiedType type = symbol->type()) {
        if (NamedType *namedType = type->asNamedType()) {
            if (const Name *name = namedType->name()) {
598
                if (name->match(&qPrivateSignalIdentifier))
599 600 601 602 603 604 605
                    return true;
            }
        }
    }
    return false;
}

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
QString createQt4SignalOrSlot(CPlusPlus::Function *function, const Overview &overview)
{
    QString signature;
    signature += Overview().prettyName(function->name());
    signature += QLatin1Char('(');
    for (unsigned i = 0, to = function->argumentCount(); i < to; ++i) {
        Symbol *arg = function->argumentAt(i);
        if (isQPrivateSignal(arg))
            continue;
        if (i != 0)
            signature += QLatin1Char(',');
        signature += overview.prettyType(arg->type());
    }
    signature += QLatin1Char(')');

    const QByteArray normalized = QMetaObject::normalizedSignature(signature.toUtf8());
    return QString::fromUtf8(normalized, normalized.size());
}

625
QString createQt5SignalOrSlot(CPlusPlus::Function *function, const Overview &overview)
626 627
{
    QString text;
628
    text += overview.prettyName(function->name());
629 630 631
    return text;
}

632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
/*!
    \class BackwardsEater
    \brief Checks strings and expressions before given position.

    Similar to BackwardsScanner, but also can handle expressions. Ignores whitespace.
*/
class BackwardsEater
{
public:
    explicit BackwardsEater(const CppCompletionAssistInterface *assistInterface, int position)
        : m_position(position)
        , m_assistInterface(assistInterface)
    {
    }

    bool isPositionValid() const
    {
        return m_position >= 0;
    }

    bool eatConnectOpenParenthesis()
    {
        return eatString(QLatin1String("(")) && eatString(QLatin1String("connect"));
    }

    bool eatExpressionCommaAmpersand()
    {
        return eatString(QLatin1String("&")) && eatString(QLatin1String(",")) && eatExpression();
    }

    bool eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma()
    {
        return eatString(QLatin1String(","))
            && eatExpression()
            && eatExpressionCommaAmpersand()
            && eatConnectOpenParenthesis();
    }

private:
    bool eatExpression()
    {
        if (!isPositionValid())
            return false;

        maybeEatWhitespace();

        QTextCursor cursor(m_assistInterface->textDocument());
        cursor.setPosition(m_position + 1);
680
        ExpressionUnderCursor expressionUnderCursor(m_assistInterface->languageFeatures());
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
        const QString expression = expressionUnderCursor(cursor);
        if (expression.isEmpty())
            return false;
        m_position = m_position - expression.length();
        return true;
    }

    bool eatString(const QString &string)
    {
        if (!isPositionValid())
            return false;

        if (string.isEmpty())
            return true;

        maybeEatWhitespace();

        const int stringLength = string.length();
        const int stringStart = m_position - (stringLength - 1);

        if (stringStart < 0)
            return false;

        if (m_assistInterface->textAt(stringStart, stringLength) == string) {
            m_position = stringStart - 1;
            return true;
        }

        return false;
    }

    void maybeEatWhitespace()
    {
        while (isPositionValid() && m_assistInterface->characterAt(m_position).isSpace())
            --m_position;
    }

private:
    int m_position;
    const CppCompletionAssistInterface * const m_assistInterface;
};

bool canCompleteConnectSignalAt2ndArgument(const CppCompletionAssistInterface *assistInterface,
                                           int startOfExpression)
{
    BackwardsEater eater(assistInterface, startOfExpression);

    return eater.isPositionValid()
        && eater.eatExpressionCommaAmpersand()
        && eater.eatConnectOpenParenthesis();
}

bool canCompleteConnectSignalAt4thArgument(const CppCompletionAssistInterface *assistInterface,
                                           int startPosition)
{
    BackwardsEater eater(assistInterface, startPosition);

    return eater.isPositionValid()
        && eater.eatExpressionCommaAmpersand()
        && eater.eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma();
}

bool canCompleteClassNameAt2ndOr4thConnectArgument(
        const CppCompletionAssistInterface *assistInterface,
        int startPosition)
{
    BackwardsEater eater(assistInterface, startPosition);

    if (!eater.isPositionValid())
        return false;

    return eater.eatConnectOpenParenthesis()
        || eater.eatConnectOpenParenthesisExpressionCommaAmpersandExpressionComma();
}

756 757
LookupScope *lookupScopeFromLookupItem(const LookupItem &lookupItem,
                                       const LookupContext &context)
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
{
    const Name *name = 0;

    if (Symbol *d = lookupItem.declaration()) {
        if (Class *k = d->asClass())
            name = k->name();
    }

    if (!name) {
        FullySpecifiedType type = lookupItem.type().simplified();

        if (PointerType *pointerType = type->asPointerType())
            type = pointerType->elementType().simplified();
        else
            return 0; // not a pointer or a reference to a pointer.

        NamedType *namedType = type->asNamedType();
        if (!namedType) // not a class name.
            return 0;

        name = namedType->name();
    }

    return name ? context.lookupType(name, lookupItem.scope()) : 0;
}

Class *classFromLookupItem(const LookupItem &lookupItem, const LookupContext &context)
{
786
    LookupScope *b = lookupScopeFromLookupItem(lookupItem, context);
787 788 789 790 791 792 793 794 795 796 797 798
    if (!b)
        return 0;

    foreach (Symbol *s, b->symbols()) {
        if (Class *klass = s->asClass())
            return klass;
    }
    return 0;
}

const Name *minimalName(Symbol *symbol, Scope *targetScope, const LookupContext &context)
{
799
    LookupScope *target = context.lookupType(targetScope);
800 801 802 803 804
    if (!target)
        target = context.globalNamespace();
    return context.minimalName(symbol, target, context.bindings()->control().data());
}

Leandro Melo's avatar
Leandro Melo committed
805 806
} // Anonymous

807 808 809 810
// ------------------------------------
// InternalCppCompletionAssistProcessor
// ------------------------------------
InternalCppCompletionAssistProcessor::InternalCppCompletionAssistProcessor()
811
    : m_model(new CppAssistProposalModel)
812 813
{
}
Leandro Melo's avatar
Leandro Melo committed
814

815
InternalCppCompletionAssistProcessor::~InternalCppCompletionAssistProcessor()
Leandro Melo's avatar
Leandro Melo committed
816 817
{}

818
IAssistProposal * InternalCppCompletionAssistProcessor::perform(const AssistInterface *interface)
Leandro Melo's avatar
Leandro Melo committed
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
{
    m_interface.reset(static_cast<const CppCompletionAssistInterface *>(interface));

    if (interface->reason() != ExplicitlyInvoked && !accepts())
        return 0;

    int index = startCompletionHelper();
    if (index != -1) {
        if (m_hintProposal)
            return m_hintProposal;

        return createContentProposal();
    }

    return 0;
}

836
bool InternalCppCompletionAssistProcessor::accepts() const
Leandro Melo's avatar
Leandro Melo committed
837 838 839 840 841 842 843
{
    const int pos = m_interface->position();
    unsigned token = T_EOF_SYMBOL;

    const int start = startOfOperator(pos, &token, /*want function call=*/ true);
    if (start != pos) {
        if (token == T_POUND) {
844
            const int column = pos - m_interface->textDocument()->findBlock(start).position();
Leandro Melo's avatar
Leandro Melo committed
845 846 847 848 849 850 851 852
            if (column != 1)
                return false;
        }

        return true;
    } else {
        // Trigger completion after three characters of a name have been typed, when not editing an existing name
        QChar characterUnderCursor = m_interface->characterAt(pos);
853 854

        if (!isValidIdentifierChar(characterUnderCursor)) {
855
            const int startOfName = findStartOfName(pos);
856
            if (pos - startOfName >= 3) {
Leandro Melo's avatar
Leandro Melo committed
857
                const QChar firstCharacter = m_interface->characterAt(startOfName);
858
                if (isValidFirstIdentifierChar(firstCharacter)) {
859
                    // Finally check that we're not inside a comment or string (code copied from startOfOperator)
860
                    QTextCursor tc(m_interface->textDocument());
861 862 863
                    tc.setPosition(pos);

                    SimpleLexer tokenize;
864
                    tokenize.setLanguageFeatures(m_interface->languageFeatures());
865
                    tokenize.setSkipComments(false);
866

867
                    const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
868 869 870
                    const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
                    const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);

871
                    if (!tk.isComment() && !tk.isLiteral()) {
872
                        return true;
873 874 875 876 877 878 879
                    } else if (tk.isLiteral()
                               && tokens.size() == 3
                               && tokens.at(0).kind() == T_POUND
                               && tokens.at(1).kind() == T_IDENTIFIER) {
                        const QString &line = tc.block().text();
                        const Token &idToken = tokens.at(1);
                        const QStringRef &identifier =
880 881
                                line.midRef(idToken.utf16charsBegin(),
                                            idToken.utf16charsEnd() - idToken.utf16charsBegin());
882 883
                        if (identifier == QLatin1String("include")
                                || identifier == QLatin1String("include_next")
884
                                || (m_interface->languageFeatures().objCEnabled && identifier == QLatin1String("import"))) {
885 886 887
                            return true;
                        }
                    }
888 889
                }
            }
890
        }
891
    }
con's avatar
con committed
892 893 894 895

    return false;
}

896
IAssistProposal *InternalCppCompletionAssistProcessor::createContentProposal()
con's avatar
con committed
897
{
Leandro Melo's avatar
Leandro Melo committed
898 899
    // Duplicates are kept only if they are snippets.
    QSet<QString> processed;
900
    QList<AssistProposalItem *>::iterator it = m_completions.begin();
Leandro Melo's avatar
Leandro Melo committed
901 902 903 904 905 906 907 908 909 910 911
    while (it != m_completions.end()) {
        CppAssistProposalItem *item = static_cast<CppAssistProposalItem *>(*it);
        if (!processed.contains(item->text()) || item->data().canConvert<QString>()) {
            ++it;
            if (!item->data().canConvert<QString>()) {
                processed.insert(item->text());
                if (!item->isOverloaded()) {
                    if (Symbol *symbol = qvariant_cast<Symbol *>(item->data())) {
                        if (Function *funTy = symbol->type()->asFunctionType()) {
                            if (funTy->hasArguments())
                                item->markAsOverloaded();
912
                        }
Leandro Melo's avatar
Leandro Melo committed
913
                    }
914 915
                }
            }
Leandro Melo's avatar
Leandro Melo committed
916
        } else {
917
            delete *it;
Leandro Melo's avatar
Leandro Melo committed
918
            it = m_completions.erase(it);
919
        }
Roberto Raggi's avatar
Roberto Raggi committed
920
    }
921

Leandro Melo's avatar
Leandro Melo committed
922
    m_model->loadContent(m_completions);
923
    return new CppAssistProposal(m_positionForProposal, m_model.take());
Leandro Melo's avatar
Leandro Melo committed
924
}
925

926
IAssistProposal *InternalCppCompletionAssistProcessor::createHintProposal(
927
    QList<Function *> functionSymbols) const
Leandro Melo's avatar
Leandro Melo committed
928
{
929 930
    IFunctionHintProposalModel *model =
            new CppFunctionHintModel(functionSymbols, m_model->m_typeOfExpression);
931
    IAssistProposal *proposal = new FunctionHintProposal(m_positionForProposal, model);
Leandro Melo's avatar
Leandro Melo committed
932
    return proposal;
933 934
}

935 936 937
int InternalCppCompletionAssistProcessor::startOfOperator(int pos,
                                                          unsigned *kind,
                                                          bool wantFunctionCall) const
938
{
Leandro Melo's avatar
Leandro Melo committed
939 940 941 942
    const QChar ch  = pos > -1 ? m_interface->characterAt(pos - 1) : QChar();
    const QChar ch2 = pos >  0 ? m_interface->characterAt(pos - 2) : QChar();
    const QChar ch3 = pos >  1 ? m_interface->characterAt(pos - 3) : QChar();

943 944
    int start = pos - CppCompletionAssistProvider::activationSequenceChar(ch, ch2, ch3, kind,
        wantFunctionCall, /*wantQt5SignalSlots*/ true);
Leandro Melo's avatar
Leandro Melo committed
945
    if (start != pos) {
946
        QTextCursor tc(m_interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
947 948 949 950 951 952 953 954 955 956 957 958
        tc.setPosition(pos);

        // Include completion: make sure the quote character is the first one on the line
        if (*kind == T_STRING_LITERAL) {
            QTextCursor s = tc;
            s.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
            QString sel = s.selectedText();
            if (sel.indexOf(QLatin1Char('"')) < sel.length() - 1) {
                *kind = T_EOF_SYMBOL;
                start = pos;
            }
        }
959

Leandro Melo's avatar
Leandro Melo committed
960
        if (*kind == T_COMMA) {
961
            ExpressionUnderCursor expressionUnderCursor(m_interface->languageFeatures());
Leandro Melo's avatar
Leandro Melo committed
962 963 964 965 966
            if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
                *kind = T_EOF_SYMBOL;
                start = pos;
            }
        }
967

Leandro Melo's avatar
Leandro Melo committed
968
        SimpleLexer tokenize;
969
        tokenize.setLanguageFeatures(m_interface->languageFeatures());
Leandro Melo's avatar
Leandro Melo committed
970
        tokenize.setSkipComments(false);
971
        const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
Leandro Melo's avatar
Leandro Melo committed
972 973
        const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1)); // get the token at the left of the cursor
        const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
974

975 976
        if (*kind == T_AMPER && tokenIdx > 0) {
            const Token &previousToken = tokens.at(tokenIdx - 1);
977
            if (previousToken.kind() == T_COMMA)
978 979
                start = pos - (tk.utf16charOffset - previousToken.utf16charOffset) - 1;
        } else if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
Leandro Melo's avatar
Leandro Melo committed
980 981 982 983
            *kind = T_EOF_SYMBOL;
            start = pos;
        }
        // Don't complete in comments or strings, but still check for include completion
984 985 986
        else if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT)
                 || tk.is(T_CPP_DOXY_COMMENT) || tk.is(T_DOXY_COMMENT)
                 || (tk.isLiteral() && (*kind != T_STRING_LITERAL
Leandro Melo's avatar
Leandro Melo committed
987
                                     && *kind != T_ANGLE_STRING_LITERAL
988 989
                                     && *kind != T_SLASH
                                     && *kind != T_DOT))) {
Leandro Melo's avatar
Leandro Melo committed
990 991 992
            *kind = T_EOF_SYMBOL;
            start = pos;
        // Include completion: can be triggered by slash, but only in a string
993
        } else if (*kind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) {
Leandro Melo's avatar
Leandro Melo committed
994 995
            *kind = T_EOF_SYMBOL;
            start = pos;
996
        } else if (*kind == T_LPAREN) {
Leandro Melo's avatar
Leandro Melo committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
            if (tokenIdx > 0) {
                const Token &previousToken = tokens.at(tokenIdx - 1); // look at the token at the left of T_LPAREN
                switch (previousToken.kind()) {
                case T_IDENTIFIER:
                case T_GREATER:
                case T_SIGNAL:
                case T_SLOT:
                    break; // good

                default:
                    // that's a bad token :)
                    *kind = T_EOF_SYMBOL;
                    start = pos;
                }
            }
        }
        // Check for include preprocessor directive
1014 1015
        else if (*kind == T_STRING_LITERAL || *kind == T_ANGLE_STRING_LITERAL|| *kind == T_SLASH
                 || (*kind == T_DOT && (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)))) {
Leandro Melo's avatar
Leandro Melo committed
1016 1017 1018 1019 1020
            bool include = false;
            if (tokens.size() >= 3) {
                if (tokens.at(0).is(T_POUND) && tokens.at(1).is(T_IDENTIFIER) && (tokens.at(2).is(T_STRING_LITERAL) ||
                                                                                  tokens.at(2).is(T_ANGLE_STRING_LITERAL))) {
                    const Token &directiveToken = tokens.at(1);
1021 1022
                    QString directive = tc.block().text().mid(directiveToken.utf16charsBegin(),
                                                              directiveToken.utf16chars());
Leandro Melo's avatar
Leandro Melo committed
1023 1024 1025 1026
                    if (directive == QLatin1String("include") ||
                            directive == QLatin1String("include_next") ||
                            directive == QLatin1String("import")) {
                        include = true;
1027 1028 1029
                    }
                }
            }
Leandro Melo's avatar
Leandro Melo committed
1030 1031 1032 1033

            if (!include) {
                *kind = T_EOF_SYMBOL;
                start = pos;
1034 1035 1036 1037 1038 1039
            } else {
                if (*kind == T_DOT) {
                    start = findStartOfName(start);
                    const QChar ch4  = start > -1 ? m_interface->characterAt(start - 1) : QChar();
                    const QChar ch5 = start >  0 ? m_interface->characterAt(start - 2) : QChar();
                    const QChar ch6 = start >  1 ? m_interface->characterAt(start - 3) : QChar();
1040 1041
                    start = start - CppCompletionAssistProvider::activationSequenceChar(
                                        ch4, ch5, ch6, kind, wantFunctionCall, false);
1042
                }
Leandro Melo's avatar
Leandro Melo committed
1043
            }
1044 1045
        }
    }
Leandro Melo's avatar
Leandro Melo committed
1046 1047 1048 1049

    return start;
}

1050
int InternalCppCompletionAssistProcessor::findStartOfName(int pos) const
Leandro Melo's avatar
Leandro Melo committed
1051 1052 1053 1054 1055 1056 1057 1058
{
    if (pos == -1)
        pos = m_interface->position();
    QChar chr;

    // Skip to the start of a name
    do {
        chr = m_interface->characterAt(--pos);
1059
    } while (CppTools::isValidIdentifierChar(chr));
Leandro Melo's avatar
Leandro Melo committed
1060 1061

    return pos + 1;
1062 1063
}

1064
int InternalCppCompletionAssistProcessor::startCompletionHelper()
1065
{
1066
    if (m_interface->languageFeatures().objCEnabled) {
Leandro Melo's avatar
Leandro Melo committed
1067
        if (tryObjCCompletion())
1068
            return m_positionForProposal;
Leandro Melo's avatar
Leandro Melo committed
1069 1070 1071
    }

    const int startOfName = findStartOfName();
1072
    m_positionForProposal = startOfName;
Leandro Melo's avatar
Leandro Melo committed
1073 1074
    m_model->m_completionOperator = T_EOF_SYMBOL;

1075
    int endOfOperator = m_positionForProposal;
Leandro Melo's avatar
Leandro Melo committed
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087

    // Skip whitespace preceding this position
    while (m_interface->characterAt(endOfOperator - 1).isSpace())
        --endOfOperator;

    int endOfExpression = startOfOperator(endOfOperator,
                                          &m_model->m_completionOperator,
                                          /*want function call =*/ true);

    if (m_model->m_completionOperator == T_DOXY_COMMENT) {
        for (int i = 1; i < T_DOXY_LAST_TAG; ++i)
            addCompletionItem(QString::fromLatin1(doxygenTagSpell(i)), m_icons.keywordIcon());
1088
        return m_positionForProposal;
Leandro Melo's avatar
Leandro Melo committed
1089 1090 1091 1092 1093
    }

    // Pre-processor completion
    if (m_model->m_completionOperator == T_POUND) {
        completePreprocessor();
1094 1095
        m_positionForProposal = startOfName;
        return m_positionForProposal;
Leandro Melo's avatar
Leandro Melo committed
1096 1097 1098 1099 1100 1101 1102
    }

    // Include completion
    if (m_model->m_completionOperator == T_STRING_LITERAL
        || m_model->m_completionOperator == T_ANGLE_STRING_LITERAL
        || m_model->m_completionOperator == T_SLASH) {

1103
        QTextCursor c(m_interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
1104 1105
        c.setPosition(endOfExpression);
        if (completeInclude(c))
1106 1107
            m_positionForProposal = endOfExpression + 1;
        return m_positionForProposal;
Leandro Melo's avatar
Leandro Melo committed
1108 1109
    }

1110
    ExpressionUnderCursor expressionUnderCursor(m_interface->languageFeatures());
1111
    QTextCursor tc(m_interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121

    if (m_model->m_completionOperator == T_COMMA) {
        tc.setPosition(endOfExpression);
        const int start = expressionUnderCursor.startOfFunctionCall(tc);
        if (start == -1) {
            m_model->m_completionOperator = T_EOF_SYMBOL;
            return -1;
        }

        endOfExpression = start;
1122
        m_positionForProposal = start + 1;
Leandro Melo's avatar
Leandro Melo committed
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
        m_model->m_completionOperator = T_LPAREN;
    }

    QString expression;
    int startOfExpression = m_interface->position();
    tc.setPosition(endOfExpression);

    if (m_model->m_completionOperator) {
        expression = expressionUnderCursor(tc);
        startOfExpression = endOfExpression - expression.length();

1134
        if (m_model->m_completionOperator == T_AMPER) {
1135 1136 1137 1138
            // We expect 'expression' to be either "sender" or "receiver" in
            //  "connect(sender, &" or
            //  "connect(otherSender, &Foo::signal1, receiver, &"
            const int beforeExpression = startOfExpression - 1;
1139 1140
            if (canCompleteClassNameAt2ndOr4thConnectArgument(m_interface.data(),
                                                              beforeExpression)) {
1141
                m_model->m_completionOperator = CompleteQt5SignalOrSlotClassNameTrigger;
1142
            } else { // Ensure global completion
1143
                startOfExpression = endOfExpression = m_positionForProposal;
1144 1145 1146
                expression.clear();
                m_model->m_completionOperator = T_EOF_SYMBOL;
            }
1147 1148 1149 1150 1151 1152 1153 1154 1155
        } else if (m_model->m_completionOperator == T_COLON_COLON) {
            // We expect 'expression' to be "Foo" in
            //  "connect(sender, &Foo::" or
            //  "connect(sender, &Bar::signal1, receiver, &Foo::"
            const int beforeExpression = startOfExpression - 1;
            if (canCompleteConnectSignalAt2ndArgument(m_interface.data(), beforeExpression))
                m_model->m_completionOperator = CompleteQt5SignalTrigger;
            else if (canCompleteConnectSignalAt4thArgument(m_interface.data(), beforeExpression))
                m_model->m_completionOperator = CompleteQt5SlotTrigger;
1156
        } else if (m_model->m_completionOperator == T_LPAREN) {
1157
            if (expression.endsWith(QLatin1String("SIGNAL"))) {
Leandro Melo's avatar
Leandro Melo committed
1158
                m_model->m_completionOperator = T_SIGNAL;
1159
            } else if (expression.endsWith(QLatin1String("SLOT"))) {
Leandro Melo's avatar
Leandro Melo committed
1160
                m_model->m_completionOperator = T_SLOT;
1161
            } else if (m_interface->position() != endOfOperator) {
Leandro Melo's avatar
Leandro Melo committed
1162 1163 1164
                // We don't want a function completion when the cursor isn't at the opening brace
                expression.clear();
                m_model->m_completionOperator = T_EOF_SYMBOL;
1165
                m_positionForProposal = startOfName;
Leandro Melo's avatar
Leandro Melo committed
1166 1167 1168 1169 1170 1171 1172 1173 1174
                startOfExpression = m_interface->position();
            }
        }
    } else if (expression.isEmpty()) {
        while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
            --startOfExpression;
    }

    int line = 0, column = 0;
1175
    Convenience::convertPosition(m_interface->textDocument(), startOfExpression, &line, &column);
1176
    const QString fileName = m_interface->fileName();
Leandro Melo's avatar
Leandro Melo committed
1177 1178
    return startCompletionInternal(fileName, line, column, expression, endOfExpression);
}
1179

1180
bool InternalCppCompletionAssistProcessor::tryObjCCompletion()
Leandro Melo's avatar
Leandro Melo committed
1181 1182 1183
{
    int end = m_interface->position();
    while (m_interface->characterAt(end).isSpace())
1184
        ++end;
Leandro Melo's avatar
Leandro Melo committed
1185
    if (m_interface->characterAt(end) != QLatin1Char(']'))
1186 1187
        return false;

1188
    QTextCursor tc(m_interface->textDocument());
1189
    tc.setPosition(end);
1190
    BackwardsScanner tokens(tc, m_interface->languageFeatures());
1191 1192 1193 1194 1195 1196 1197
    if (tokens[tokens.startToken() - 1].isNot(T_RBRACKET))
        return false;

    const int start = tokens.startOfMatchingBrace(tokens.startToken());
    if (start == tokens.startToken())
        return false;

Nikolai Kosjar's avatar
Nikolai Kosjar committed
1198
    const int startPos = tokens[start].bytesBegin() + tokens.startPosition();
Leandro Melo's avatar
Leandro Melo committed
1199
    const QString expr = m_interface->textAt(startPos, m_interface->position() - startPos);
1200

1201
    Document::Ptr thisDocument = m_interface->snapshot().document(m_interface->fileName());
1202
    if (!thisDocument)
1203 1204
        return false;

1205 1206
    m_model->m_typeOfExpression->init(thisDocument, m_interface->snapshot());