forked from qt-creator/qt-creator
		
	With the completion now in a separate thread this should no longer be necessary. Reviewed-by: Roberto Raggi
		
			
				
	
	
		
			1885 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1885 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**************************************************************************
 | 
						|
**
 | 
						|
** This file is part of Qt Creator
 | 
						|
**
 | 
						|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
 | 
						|
**
 | 
						|
** Contact: Nokia Corporation (info@qt.nokia.com)
 | 
						|
**
 | 
						|
**
 | 
						|
** GNU Lesser General Public License Usage
 | 
						|
**
 | 
						|
** This file may be used under the terms of the GNU Lesser General Public
 | 
						|
** License version 2.1 as published by the Free Software Foundation and
 | 
						|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
 | 
						|
** Please review the following information to ensure the GNU Lesser General
 | 
						|
** Public License version 2.1 requirements will be met:
 | 
						|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 | 
						|
**
 | 
						|
** In addition, as a special exception, Nokia gives you certain additional
 | 
						|
** rights. These rights are described in the Nokia Qt LGPL Exception
 | 
						|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 | 
						|
**
 | 
						|
** Other Usage
 | 
						|
**
 | 
						|
** Alternatively, this file may be used in accordance with the terms and
 | 
						|
** conditions contained in a signed written agreement between you and Nokia.
 | 
						|
**
 | 
						|
** If you have questions regarding the use of this file, please contact
 | 
						|
** Nokia at info@qt.nokia.com.
 | 
						|
**
 | 
						|
**************************************************************************/
 | 
						|
 | 
						|
#include "cppmodelmanager.h"
 | 
						|
#include "cppcompletionassist.h"
 | 
						|
#include "cppdoxygen.h"
 | 
						|
#include "cppmodelmanager.h"
 | 
						|
#include "cpptoolsconstants.h"
 | 
						|
 | 
						|
#include <Control.h>
 | 
						|
#include <AST.h>
 | 
						|
#include <ASTVisitor.h>
 | 
						|
#include <CoreTypes.h>
 | 
						|
#include <Literals.h>
 | 
						|
#include <Names.h>
 | 
						|
#include <NameVisitor.h>
 | 
						|
#include <Symbols.h>
 | 
						|
#include <SymbolVisitor.h>
 | 
						|
#include <Scope.h>
 | 
						|
#include <TranslationUnit.h>
 | 
						|
 | 
						|
#include <cplusplus/ResolveExpression.h>
 | 
						|
#include <cplusplus/MatchingText.h>
 | 
						|
#include <cplusplus/Overview.h>
 | 
						|
#include <cplusplus/ExpressionUnderCursor.h>
 | 
						|
#include <cplusplus/BackwardsScanner.h>
 | 
						|
#include <cplusplus/LookupContext.h>
 | 
						|
 | 
						|
#include <coreplugin/ifile.h>
 | 
						|
#include <coreplugin/icore.h>
 | 
						|
#include <coreplugin/mimedatabase.h>
 | 
						|
#include <cppeditor/cppeditorconstants.h>
 | 
						|
#include <texteditor/codeassist/basicproposalitem.h>
 | 
						|
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
 | 
						|
#include <texteditor/codeassist/genericproposal.h>
 | 
						|
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
 | 
						|
#include <texteditor/codeassist/functionhintproposal.h>
 | 
						|
#include <texteditor/convenience.h>
 | 
						|
#include <texteditor/snippets/snippet.h>
 | 
						|
#include <texteditor/texteditorsettings.h>
 | 
						|
#include <texteditor/completionsettings.h>
 | 
						|
 | 
						|
#include <QtCore/QLatin1String>
 | 
						|
#include <QtGui/QTextCursor>
 | 
						|
#include <QtGui/QTextDocument>
 | 
						|
#include <QtGui/QIcon>
 | 
						|
 | 
						|
using namespace CPlusPlus;
 | 
						|
using namespace CppEditor;
 | 
						|
using namespace CppTools;
 | 
						|
using namespace Internal;
 | 
						|
using namespace TextEditor;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
int activationSequenceChar(const QChar &ch,
 | 
						|
                           const QChar &ch2,
 | 
						|
                           const QChar &ch3,
 | 
						|
                           unsigned *kind,
 | 
						|
                           bool wantFunctionCall)
 | 
						|
{
 | 
						|
    int referencePosition = 0;
 | 
						|
    int completionKind = T_EOF_SYMBOL;
 | 
						|
    switch (ch.toLatin1()) {
 | 
						|
    case '.':
 | 
						|
        if (ch2 != QLatin1Char('.')) {
 | 
						|
            completionKind = T_DOT;
 | 
						|
            referencePosition = 1;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case ',':
 | 
						|
        completionKind = T_COMMA;
 | 
						|
        referencePosition = 1;
 | 
						|
        break;
 | 
						|
    case '(':
 | 
						|
        if (wantFunctionCall) {
 | 
						|
            completionKind = T_LPAREN;
 | 
						|
            referencePosition = 1;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case ':':
 | 
						|
        if (ch3 != QLatin1Char(':') && ch2 == QLatin1Char(':')) {
 | 
						|
            completionKind = T_COLON_COLON;
 | 
						|
            referencePosition = 2;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case '>':
 | 
						|
        if (ch2 == QLatin1Char('-')) {
 | 
						|
            completionKind = T_ARROW;
 | 
						|
            referencePosition = 2;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case '*':
 | 
						|
        if (ch2 == QLatin1Char('.')) {
 | 
						|
            completionKind = T_DOT_STAR;
 | 
						|
            referencePosition = 2;
 | 
						|
        } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>')) {
 | 
						|
            completionKind = T_ARROW_STAR;
 | 
						|
            referencePosition = 3;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case '\\':
 | 
						|
    case '@':
 | 
						|
        if (ch2.isNull() || ch2.isSpace()) {
 | 
						|
            completionKind = T_DOXY_COMMENT;
 | 
						|
            referencePosition = 1;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case '<':
 | 
						|
        completionKind = T_ANGLE_STRING_LITERAL;
 | 
						|
        referencePosition = 1;
 | 
						|
        break;
 | 
						|
    case '"':
 | 
						|
        completionKind = T_STRING_LITERAL;
 | 
						|
        referencePosition = 1;
 | 
						|
        break;
 | 
						|
    case '/':
 | 
						|
        completionKind = T_SLASH;
 | 
						|
        referencePosition = 1;
 | 
						|
        break;
 | 
						|
    case '#':
 | 
						|
        completionKind = T_POUND;
 | 
						|
        referencePosition = 1;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (kind)
 | 
						|
        *kind = completionKind;
 | 
						|
 | 
						|
    return referencePosition;
 | 
						|
}
 | 
						|
 | 
						|
} // Anonymous
 | 
						|
 | 
						|
namespace CppTools {
 | 
						|
namespace Internal {
 | 
						|
 | 
						|
struct CompleteFunctionDeclaration
 | 
						|
{
 | 
						|
    explicit CompleteFunctionDeclaration(Function *f = 0)
 | 
						|
        : function(f)
 | 
						|
    {}
 | 
						|
 | 
						|
    Function *function;
 | 
						|
};
 | 
						|
 | 
						|
// ----------------------
 | 
						|
// CppAssistProposalModel
 | 
						|
// ----------------------
 | 
						|
class CppAssistProposalModel : public TextEditor::BasicProposalItemListModel
 | 
						|
{
 | 
						|
public:
 | 
						|
    CppAssistProposalModel()
 | 
						|
        : TextEditor::BasicProposalItemListModel()
 | 
						|
        , m_sortable(false)
 | 
						|
        , m_completionOperator(T_EOF_SYMBOL)
 | 
						|
        , m_replaceDotForArrow(false)
 | 
						|
    {}
 | 
						|
 | 
						|
    virtual bool isSortable() const { return m_sortable; }
 | 
						|
    virtual IAssistProposalItem *proposalItem(int index) const;
 | 
						|
 | 
						|
    bool m_sortable;
 | 
						|
    unsigned m_completionOperator;
 | 
						|
    bool m_replaceDotForArrow;
 | 
						|
    Snapshot m_snapshot;
 | 
						|
};
 | 
						|
 | 
						|
// ---------------------
 | 
						|
// CppAssistProposalItem
 | 
						|
// ---------------------
 | 
						|
class CppAssistProposalItem : public TextEditor::BasicProposalItem
 | 
						|
{
 | 
						|
public:
 | 
						|
    CppAssistProposalItem() : m_isOverloaded(false) {}
 | 
						|
 | 
						|
    virtual bool prematurelyApplies(const QChar &c) const;
 | 
						|
    virtual void applyContextualContent(TextEditor::BaseTextEditor *editor,
 | 
						|
                                        int basePosition) const;
 | 
						|
 | 
						|
    bool isOverloaded() const { return m_isOverloaded; }
 | 
						|
    void markAsOverloaded() { m_isOverloaded = true; }
 | 
						|
    void keepCompletionOperator(unsigned compOp) { m_completionOperator = compOp; }
 | 
						|
    void keepSnapshot(const Snapshot &snapshot) { m_snapshot = snapshot; }
 | 
						|
 | 
						|
private:
 | 
						|
    bool m_isOverloaded;
 | 
						|
    unsigned m_completionOperator;
 | 
						|
    mutable QChar m_typedChar;
 | 
						|
    Snapshot m_snapshot;
 | 
						|
};
 | 
						|
 | 
						|
} // Internal
 | 
						|
} // CppTools
 | 
						|
 | 
						|
Q_DECLARE_METATYPE(CppTools::Internal::CompleteFunctionDeclaration)
 | 
						|
 | 
						|
IAssistProposalItem *CppAssistProposalModel::proposalItem(int index) const
 | 
						|
{
 | 
						|
    BasicProposalItem *item =
 | 
						|
        static_cast<BasicProposalItem *>(BasicProposalItemListModel::proposalItem(index));
 | 
						|
    if (!item->data().canConvert<QString>()) {
 | 
						|
        CppAssistProposalItem *cppItem = static_cast<CppAssistProposalItem *>(item);
 | 
						|
        cppItem->keepCompletionOperator(m_completionOperator);
 | 
						|
        cppItem->keepSnapshot(m_snapshot);
 | 
						|
    }
 | 
						|
    return item;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
void CppAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor *editor,
 | 
						|
                                                    int basePosition) const
 | 
						|
{
 | 
						|
    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();
 | 
						|
 | 
						|
        const CompletionSettings &completionSettings =
 | 
						|
                TextEditorSettings::instance()->completionSettings();
 | 
						|
        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.
 | 
						|
                if (! function->hasReturnType() && (function->unqualifiedName() && !function->unqualifiedName()->isDestructorNameId())) {
 | 
						|
                    // 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
 | 
						|
                    if (function->argumentCount() == 0) {
 | 
						|
                        extraChars += QLatin1Char('<');
 | 
						|
                    }
 | 
						|
#endif
 | 
						|
                } else if (! function->isAmbiguous()) {
 | 
						|
                    // 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).
 | 
						|
                    const QChar characterAtCursor = editor->characterAt(editor->position());
 | 
						|
                    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
 | 
						|
                    if (!isOverloaded() && ! function->hasArguments() && skipClosingParenthesis) {
 | 
						|
                        extraChars += QLatin1Char(')');
 | 
						|
                        if (endWithSemicolon) {
 | 
						|
                            extraChars += semicolon;
 | 
						|
                            m_typedChar = QChar();
 | 
						|
                        }
 | 
						|
                    } else if (autoParenthesesEnabled) {
 | 
						|
                        const QChar lookAhead = editor->characterAt(editor->position() + 1);
 | 
						|
                        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;
 | 
						|
    }
 | 
						|
 | 
						|
    // Avoid inserting characters that are already there
 | 
						|
    for (int i = 0; i < extraChars.length(); ++i) {
 | 
						|
        const QChar a = extraChars.at(i);
 | 
						|
        const QChar b = editor->characterAt(editor->position() + i);
 | 
						|
        if (a == b)
 | 
						|
            ++extraLength;
 | 
						|
        else
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    toInsert += extraChars;
 | 
						|
 | 
						|
    // Insert the remainder of the name
 | 
						|
    int length = editor->position() - basePosition + extraLength;
 | 
						|
    editor->setCursorPosition(basePosition);
 | 
						|
    editor->replace(length, toInsert);
 | 
						|
    if (cursorOffset)
 | 
						|
        editor->setCursorPosition(editor->position() + cursorOffset);
 | 
						|
}
 | 
						|
 | 
						|
// --------------------
 | 
						|
// CppFunctionHintModel
 | 
						|
// --------------------
 | 
						|
class CppFunctionHintModel : public TextEditor::IFunctionHintProposalModel
 | 
						|
{
 | 
						|
public:
 | 
						|
    CppFunctionHintModel(QList<Function *> functionSymbols)
 | 
						|
        : m_functionSymbols(functionSymbols)
 | 
						|
        , m_currentArg(-1)
 | 
						|
    {}
 | 
						|
 | 
						|
    virtual void reset() {}
 | 
						|
    virtual int size() const { return m_functionSymbols.size(); }
 | 
						|
    virtual QString text(int index) const;
 | 
						|
    virtual int activeArgument(const QString &prefix) const;
 | 
						|
 | 
						|
private:
 | 
						|
    QList<Function *> m_functionSymbols;
 | 
						|
    mutable int m_currentArg;
 | 
						|
};
 | 
						|
 | 
						|
QString CppFunctionHintModel::text(int index) const
 | 
						|
{
 | 
						|
    Overview overview;
 | 
						|
    overview.setShowReturnTypes(true);
 | 
						|
    overview.setShowArgumentNames(true);
 | 
						|
    overview.setMarkedArgument(m_currentArg + 1);
 | 
						|
    Function *f = m_functionSymbols.at(index);
 | 
						|
 | 
						|
    const QString prettyMethod = overview(f->type(), f->name());
 | 
						|
    const int begin = overview.markedArgumentBegin();
 | 
						|
    const int end = overview.markedArgumentEnd();
 | 
						|
 | 
						|
    QString hintText;
 | 
						|
    hintText += Qt::escape(prettyMethod.left(begin));
 | 
						|
    hintText += "<b>";
 | 
						|
    hintText += Qt::escape(prettyMethod.mid(begin, end - begin));
 | 
						|
    hintText += "</b>";
 | 
						|
    hintText += Qt::escape(prettyMethod.mid(end));
 | 
						|
    return hintText;
 | 
						|
}
 | 
						|
 | 
						|
int CppFunctionHintModel::activeArgument(const QString &prefix) const
 | 
						|
{
 | 
						|
    int argnr = 0;
 | 
						|
    int parcount = 0;
 | 
						|
    SimpleLexer tokenize;
 | 
						|
    QList<Token> tokens = tokenize(prefix);
 | 
						|
    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;
 | 
						|
        else if (! parcount && tk.is(T_COMMA))
 | 
						|
            ++argnr;
 | 
						|
    }
 | 
						|
 | 
						|
    if (parcount < 0)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    if (argnr != m_currentArg)
 | 
						|
        m_currentArg = argnr;
 | 
						|
 | 
						|
    return argnr;
 | 
						|
}
 | 
						|
 | 
						|
// ---------------------------
 | 
						|
// CppCompletionAssistProvider
 | 
						|
// ---------------------------
 | 
						|
bool CppCompletionAssistProvider::supportsEditor(const QString &editorId) const
 | 
						|
{
 | 
						|
    return editorId == QLatin1String(CppEditor::Constants::CPPEDITOR_ID);
 | 
						|
}
 | 
						|
 | 
						|
int CppCompletionAssistProvider::activationCharSequenceLength() const
 | 
						|
{
 | 
						|
    return 3;
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const
 | 
						|
{
 | 
						|
    const QChar &ch  = sequence.at(2);
 | 
						|
    const QChar &ch2 = sequence.at(1);
 | 
						|
    const QChar &ch3 = sequence.at(0);
 | 
						|
    if (activationSequenceChar(ch, ch2, ch3, 0, true) != 0)
 | 
						|
        return true;
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
IAssistProcessor *CppCompletionAssistProvider::createProcessor() const
 | 
						|
{
 | 
						|
    return new CppCompletionAssistProcessor;
 | 
						|
}
 | 
						|
 | 
						|
// -----------------
 | 
						|
// CppAssistProposal
 | 
						|
// -----------------
 | 
						|
class CppAssistProposal : public TextEditor::GenericProposal
 | 
						|
{
 | 
						|
public:
 | 
						|
    CppAssistProposal(int cursorPos, TextEditor::IGenericProposalModel *model)
 | 
						|
        : TextEditor::GenericProposal(cursorPos, model)
 | 
						|
        , m_replaceDotForArrow(static_cast<CppAssistProposalModel *>(model)->m_replaceDotForArrow)
 | 
						|
    {}
 | 
						|
 | 
						|
    virtual bool isCorrective() const { return m_replaceDotForArrow; }
 | 
						|
    virtual void makeCorrection(BaseTextEditor *editor);
 | 
						|
 | 
						|
private:
 | 
						|
    bool m_replaceDotForArrow;
 | 
						|
};
 | 
						|
 | 
						|
void CppAssistProposal::makeCorrection(BaseTextEditor *editor)
 | 
						|
{
 | 
						|
    editor->setCursorPosition(basePosition() - 1);
 | 
						|
    editor->replace(1, QLatin1String("->"));
 | 
						|
    moveBasePosition(1);
 | 
						|
}
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class ConvertToCompletionItem: protected NameVisitor
 | 
						|
{
 | 
						|
    // The completion item.
 | 
						|
    BasicProposalItem *_item;
 | 
						|
 | 
						|
    // The current symbol.
 | 
						|
    Symbol *_symbol;
 | 
						|
 | 
						|
    // The pretty printer.
 | 
						|
    Overview overview;
 | 
						|
 | 
						|
public:
 | 
						|
    ConvertToCompletionItem()
 | 
						|
        : _item(0)
 | 
						|
        , _symbol(0)
 | 
						|
    { }
 | 
						|
 | 
						|
    BasicProposalItem *operator()(Symbol *symbol)
 | 
						|
    {
 | 
						|
        if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId())
 | 
						|
            return 0;
 | 
						|
 | 
						|
        BasicProposalItem *previousItem = switchCompletionItem(0);
 | 
						|
        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;
 | 
						|
    }
 | 
						|
 | 
						|
    BasicProposalItem *switchCompletionItem(BasicProposalItem *item)
 | 
						|
    {
 | 
						|
        BasicProposalItem *previousItem = _item;
 | 
						|
        _item = item;
 | 
						|
        return previousItem;
 | 
						|
    }
 | 
						|
 | 
						|
    BasicProposalItem *newCompletionItem(const Name *name)
 | 
						|
    {
 | 
						|
        BasicProposalItem *item = new CppAssistProposalItem;
 | 
						|
        item->setText(overview.prettyName(name));
 | 
						|
        return item;
 | 
						|
    }
 | 
						|
 | 
						|
    virtual void visit(const Identifier *name)
 | 
						|
    { _item = newCompletionItem(name); }
 | 
						|
 | 
						|
    virtual void visit(const TemplateNameId *name)
 | 
						|
    {
 | 
						|
        _item = newCompletionItem(name);
 | 
						|
        _item->setText(QLatin1String(name->identifier()->chars()));
 | 
						|
    }
 | 
						|
 | 
						|
    virtual void visit(const DestructorNameId *name)
 | 
						|
    { _item = newCompletionItem(name); }
 | 
						|
 | 
						|
    virtual void visit(const OperatorNameId *name)
 | 
						|
    { _item = newCompletionItem(name); }
 | 
						|
 | 
						|
    virtual void visit(const ConversionNameId *name)
 | 
						|
    { _item = newCompletionItem(name); }
 | 
						|
 | 
						|
    virtual void visit(const QualifiedNameId *name)
 | 
						|
    { _item = newCompletionItem(name->name()); }
 | 
						|
};
 | 
						|
 | 
						|
Class *asClassOrTemplateClassType(FullySpecifiedType ty)
 | 
						|
{
 | 
						|
    if (Class *classTy = ty->asClassType())
 | 
						|
        return classTy;
 | 
						|
    else if (Template *templ = ty->asTemplateType()) {
 | 
						|
        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;
 | 
						|
    else if (Template *templ = ty->asTemplateType()) {
 | 
						|
        if (Symbol *decl = templ->declaration())
 | 
						|
            return decl->asFunction();
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
} // Anonymous
 | 
						|
 | 
						|
// ----------------------------
 | 
						|
// CppCompletionAssistProcessor
 | 
						|
// ----------------------------
 | 
						|
CppCompletionAssistProcessor::CppCompletionAssistProcessor()
 | 
						|
    : m_startPosition(-1)
 | 
						|
    , m_objcEnabled(true)
 | 
						|
    , m_snippetCollector(CppEditor::Constants::CPP_SNIPPETS_GROUP_ID,
 | 
						|
                         QIcon(QLatin1String(":/texteditor/images/snippet.png")))
 | 
						|
    , preprocessorCompletions(QStringList()
 | 
						|
          << QLatin1String("define")
 | 
						|
          << QLatin1String("error")
 | 
						|
          << QLatin1String("include")
 | 
						|
          << QLatin1String("line")
 | 
						|
          << QLatin1String("pragma")
 | 
						|
          << QLatin1String("undef")
 | 
						|
          << QLatin1String("if")
 | 
						|
          << QLatin1String("ifdef")
 | 
						|
          << QLatin1String("ifndef")
 | 
						|
          << QLatin1String("elif")
 | 
						|
          << QLatin1String("else")
 | 
						|
          << QLatin1String("endif"))
 | 
						|
    , m_model(new CppAssistProposalModel)
 | 
						|
    , m_hintProposal(0)
 | 
						|
{}
 | 
						|
 | 
						|
CppCompletionAssistProcessor::~CppCompletionAssistProcessor()
 | 
						|
{}
 | 
						|
 | 
						|
IAssistProposal * CppCompletionAssistProcessor::perform(const IAssistInterface *interface)
 | 
						|
{
 | 
						|
    m_interface.reset(static_cast<const CppCompletionAssistInterface *>(interface));
 | 
						|
 | 
						|
    if (interface->reason() != ExplicitlyInvoked && !accepts())
 | 
						|
        return 0;
 | 
						|
 | 
						|
    m_model->m_snapshot = m_interface->snapshot();
 | 
						|
 | 
						|
    int index = startCompletionHelper();
 | 
						|
    if (index != -1) {
 | 
						|
        if (m_hintProposal)
 | 
						|
            return m_hintProposal;
 | 
						|
 | 
						|
        if (interface->reason() == IdleEditor) {
 | 
						|
            const int pos = m_interface->position();
 | 
						|
            const QChar ch = m_interface->characterAt(pos);
 | 
						|
            if (! (ch.isLetterOrNumber() || ch == QLatin1Char('_'))) {
 | 
						|
                for (int i = pos - 1;; --i) {
 | 
						|
                    const QChar ch = m_interface->characterAt(i);
 | 
						|
                    if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) {
 | 
						|
                        const QString wordUnderCursor = m_interface->textAt(i, pos - i);
 | 
						|
                        if (wordUnderCursor.at(0).isLetter() || wordUnderCursor.at(0) == QLatin1Char('_')) {
 | 
						|
                            foreach (const BasicProposalItem *item, m_completions) {
 | 
						|
                                if (item->text() == wordUnderCursor)
 | 
						|
                                    return 0;
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            return 0;
 | 
						|
                        }
 | 
						|
                    } else
 | 
						|
                        break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_model->m_completionOperator != T_EOF_SYMBOL)
 | 
						|
            m_model->m_sortable = true;
 | 
						|
        else
 | 
						|
            m_model->m_sortable = false;
 | 
						|
        return createContentProposal();
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::accepts() const
 | 
						|
{
 | 
						|
    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) {
 | 
						|
            const int column = pos - m_interface->document()->findBlock(start).position();
 | 
						|
            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);
 | 
						|
        if (!characterUnderCursor.isLetterOrNumber()) {
 | 
						|
            const int startOfName = findStartOfName(pos);
 | 
						|
            if (pos - startOfName >= 3) {
 | 
						|
                const QChar firstCharacter = m_interface->characterAt(startOfName);
 | 
						|
                if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
 | 
						|
                    // Finally check that we're not inside a comment or string (code copied from startOfOperator)
 | 
						|
                    QTextCursor tc(m_interface->document());
 | 
						|
                    tc.setPosition(pos);
 | 
						|
 | 
						|
                    SimpleLexer tokenize;
 | 
						|
                    tokenize.setQtMocRunEnabled(true);
 | 
						|
                    tokenize.setObjCEnabled(true);
 | 
						|
                    tokenize.setSkipComments(false);
 | 
						|
                    const QList<Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
 | 
						|
                    const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
 | 
						|
                    const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
 | 
						|
 | 
						|
                    if (!tk.isComment() && !tk.isLiteral())
 | 
						|
                        return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
IAssistProposal *CppCompletionAssistProcessor::createContentProposal()
 | 
						|
{
 | 
						|
    // Duplicates are kept only if they are snippets.
 | 
						|
    QSet<QString> processed;
 | 
						|
    QList<BasicProposalItem *>::iterator it = m_completions.begin();
 | 
						|
    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();
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            it = m_completions.erase(it);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    m_model->loadContent(m_completions);
 | 
						|
    return new CppAssistProposal(m_startPosition, m_model.take());
 | 
						|
}
 | 
						|
 | 
						|
IAssistProposal *CppCompletionAssistProcessor::createHintProposal(
 | 
						|
    QList<CPlusPlus::Function *> functionSymbols) const
 | 
						|
{
 | 
						|
    IFunctionHintProposalModel *model = new CppFunctionHintModel(functionSymbols);
 | 
						|
    IAssistProposal *proposal = new FunctionHintProposal(m_startPosition, model);
 | 
						|
    return proposal;
 | 
						|
}
 | 
						|
 | 
						|
int CppCompletionAssistProcessor::startOfOperator(int pos,
 | 
						|
                                                  unsigned *kind,
 | 
						|
                                                  bool wantFunctionCall) const
 | 
						|
{
 | 
						|
    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();
 | 
						|
 | 
						|
    int start = pos - activationSequenceChar(ch, ch2, ch3, kind, wantFunctionCall);
 | 
						|
    if (start != pos) {
 | 
						|
        QTextCursor tc(m_interface->document());
 | 
						|
        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;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (*kind == T_COMMA) {
 | 
						|
            ExpressionUnderCursor expressionUnderCursor;
 | 
						|
            if (expressionUnderCursor.startOfFunctionCall(tc) == -1) {
 | 
						|
                *kind = T_EOF_SYMBOL;
 | 
						|
                start = pos;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        SimpleLexer tokenize;
 | 
						|
        tokenize.setQtMocRunEnabled(true);
 | 
						|
        tokenize.setObjCEnabled(true);
 | 
						|
        tokenize.setSkipComments(false);
 | 
						|
        const QList<Token> &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
 | 
						|
        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);
 | 
						|
 | 
						|
        if (*kind == T_DOXY_COMMENT && !(tk.is(T_DOXY_COMMENT) || tk.is(T_CPP_DOXY_COMMENT))) {
 | 
						|
            *kind = T_EOF_SYMBOL;
 | 
						|
            start = pos;
 | 
						|
        }
 | 
						|
        // Don't complete in comments or strings, but still check for include completion
 | 
						|
        else if (tk.is(T_COMMENT) || tk.is(T_CPP_COMMENT) ||
 | 
						|
                 (tk.isLiteral() && (*kind != T_STRING_LITERAL
 | 
						|
                                     && *kind != T_ANGLE_STRING_LITERAL
 | 
						|
                                     && *kind != T_SLASH))) {
 | 
						|
            *kind = T_EOF_SYMBOL;
 | 
						|
            start = pos;
 | 
						|
        }
 | 
						|
        // Include completion: can be triggered by slash, but only in a string
 | 
						|
        else if (*kind == T_SLASH && (tk.isNot(T_STRING_LITERAL) && tk.isNot(T_ANGLE_STRING_LITERAL))) {
 | 
						|
            *kind = T_EOF_SYMBOL;
 | 
						|
            start = pos;
 | 
						|
        }
 | 
						|
        else if (*kind == T_LPAREN) {
 | 
						|
            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
 | 
						|
        else if (*kind == T_STRING_LITERAL || *kind == T_ANGLE_STRING_LITERAL || *kind == T_SLASH) {
 | 
						|
            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);
 | 
						|
                    QString directive = tc.block().text().mid(directiveToken.begin(),
 | 
						|
                                                              directiveToken.length());
 | 
						|
                    if (directive == QLatin1String("include") ||
 | 
						|
                            directive == QLatin1String("include_next") ||
 | 
						|
                            directive == QLatin1String("import")) {
 | 
						|
                        include = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (!include) {
 | 
						|
                *kind = T_EOF_SYMBOL;
 | 
						|
                start = pos;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return start;
 | 
						|
}
 | 
						|
 | 
						|
int CppCompletionAssistProcessor::findStartOfName(int pos) const
 | 
						|
{
 | 
						|
    if (pos == -1)
 | 
						|
        pos = m_interface->position();
 | 
						|
    QChar chr;
 | 
						|
 | 
						|
    // Skip to the start of a name
 | 
						|
    do {
 | 
						|
        chr = m_interface->characterAt(--pos);
 | 
						|
    } while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
 | 
						|
 | 
						|
    return pos + 1;
 | 
						|
}
 | 
						|
 | 
						|
int CppCompletionAssistProcessor::startCompletionHelper()
 | 
						|
{
 | 
						|
    if (m_objcEnabled) {
 | 
						|
        if (tryObjCCompletion())
 | 
						|
            return m_startPosition;
 | 
						|
    }
 | 
						|
 | 
						|
    const int startOfName = findStartOfName();
 | 
						|
    m_startPosition = startOfName;
 | 
						|
    m_model->m_completionOperator = T_EOF_SYMBOL;
 | 
						|
 | 
						|
    int endOfOperator = m_startPosition;
 | 
						|
 | 
						|
    // 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);
 | 
						|
 | 
						|
    const Core::IFile *file = m_interface->file();
 | 
						|
    QString fileName = file->fileName();
 | 
						|
 | 
						|
    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());
 | 
						|
        return m_startPosition;
 | 
						|
    }
 | 
						|
 | 
						|
    // Pre-processor completion
 | 
						|
    if (m_model->m_completionOperator == T_POUND) {
 | 
						|
        completePreprocessor();
 | 
						|
        m_startPosition = startOfName;
 | 
						|
        return m_startPosition;
 | 
						|
    }
 | 
						|
 | 
						|
    // Include completion
 | 
						|
    if (m_model->m_completionOperator == T_STRING_LITERAL
 | 
						|
        || m_model->m_completionOperator == T_ANGLE_STRING_LITERAL
 | 
						|
        || m_model->m_completionOperator == T_SLASH) {
 | 
						|
 | 
						|
        QTextCursor c(m_interface->document());
 | 
						|
        c.setPosition(endOfExpression);
 | 
						|
        if (completeInclude(c))
 | 
						|
            m_startPosition = startOfName;
 | 
						|
        return m_startPosition;
 | 
						|
    }
 | 
						|
 | 
						|
    ExpressionUnderCursor expressionUnderCursor;
 | 
						|
    QTextCursor tc(m_interface->document());
 | 
						|
 | 
						|
    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;
 | 
						|
        m_startPosition = start + 1;
 | 
						|
        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();
 | 
						|
 | 
						|
        if (m_model->m_completionOperator == T_LPAREN) {
 | 
						|
            if (expression.endsWith(QLatin1String("SIGNAL")))
 | 
						|
                m_model->m_completionOperator = T_SIGNAL;
 | 
						|
 | 
						|
            else if (expression.endsWith(QLatin1String("SLOT")))
 | 
						|
                m_model->m_completionOperator = T_SLOT;
 | 
						|
 | 
						|
            else if (m_interface->position() != endOfOperator) {
 | 
						|
                // 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;
 | 
						|
                m_startPosition = startOfName;
 | 
						|
                startOfExpression = m_interface->position();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else if (expression.isEmpty()) {
 | 
						|
        while (startOfExpression > 0 && m_interface->characterAt(startOfExpression).isSpace())
 | 
						|
            --startOfExpression;
 | 
						|
    }
 | 
						|
 | 
						|
    int line = 0, column = 0;
 | 
						|
    Convenience::convertPosition(m_interface->document(), startOfExpression, &line, &column);
 | 
						|
    return startCompletionInternal(fileName, line, column, expression, endOfExpression);
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::tryObjCCompletion()
 | 
						|
{
 | 
						|
    int end = m_interface->position();
 | 
						|
    while (m_interface->characterAt(end).isSpace())
 | 
						|
        ++end;
 | 
						|
    if (m_interface->characterAt(end) != QLatin1Char(']'))
 | 
						|
        return false;
 | 
						|
 | 
						|
    QTextCursor tc(m_interface->document());
 | 
						|
    tc.setPosition(end);
 | 
						|
    BackwardsScanner tokens(tc);
 | 
						|
    if (tokens[tokens.startToken() - 1].isNot(T_RBRACKET))
 | 
						|
        return false;
 | 
						|
 | 
						|
    const int start = tokens.startOfMatchingBrace(tokens.startToken());
 | 
						|
    if (start == tokens.startToken())
 | 
						|
        return false;
 | 
						|
 | 
						|
    const int startPos = tokens[start].begin() + tokens.startPosition();
 | 
						|
    const QString expr = m_interface->textAt(startPos, m_interface->position() - startPos);
 | 
						|
 | 
						|
    Document::Ptr thisDocument = m_model->m_snapshot.document(m_interface->file()->fileName());
 | 
						|
    if (! thisDocument)
 | 
						|
        return false;
 | 
						|
 | 
						|
    typeOfExpression.init(thisDocument, m_model->m_snapshot);
 | 
						|
    int line = 0, column = 0;
 | 
						|
    Convenience::convertPosition(m_interface->document(), m_interface->position(), &line, &column);
 | 
						|
    Scope *scope = thisDocument->scopeAt(line, column);
 | 
						|
    if (!scope)
 | 
						|
        return false;
 | 
						|
 | 
						|
    const QList<LookupItem> items = typeOfExpression(expr, scope);
 | 
						|
    LookupContext lookupContext(thisDocument, m_model->m_snapshot);
 | 
						|
 | 
						|
    foreach (const LookupItem &item, items) {
 | 
						|
        FullySpecifiedType ty = item.type().simplified();
 | 
						|
        if (ty->isPointerType()) {
 | 
						|
            ty = ty->asPointerType()->elementType().simplified();
 | 
						|
 | 
						|
            if (NamedType *namedTy = ty->asNamedType()) {
 | 
						|
                ClassOrNamespace *binding = lookupContext.lookupType(namedTy->name(), item.scope());
 | 
						|
                completeObjCMsgSend(binding, false);
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if (ObjCClass *clazz = ty->asObjCClassType()) {
 | 
						|
                ClassOrNamespace *binding = lookupContext.lookupType(clazz->name(), item.scope());
 | 
						|
                completeObjCMsgSend(binding, true);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_completions.isEmpty())
 | 
						|
        return false;
 | 
						|
 | 
						|
    m_startPosition = m_interface->position();
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::addCompletionItem(const QString &text,
 | 
						|
                                                     const QIcon &icon,
 | 
						|
                                                     int order,
 | 
						|
                                                     const QVariant &data)
 | 
						|
{
 | 
						|
    BasicProposalItem *item = new CppAssistProposalItem;
 | 
						|
    item->setText(text);
 | 
						|
    item->setIcon(icon);
 | 
						|
    item->setOrder(order);
 | 
						|
    item->setData(data);
 | 
						|
    m_completions.append(item);
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::addCompletionItem(CPlusPlus::Symbol *symbol)
 | 
						|
{
 | 
						|
    ConvertToCompletionItem toCompletionItem;
 | 
						|
    BasicProposalItem *item = toCompletionItem(symbol);
 | 
						|
    if (item) {
 | 
						|
        item->setIcon(m_icons.iconForSymbol(symbol));
 | 
						|
        m_completions.append(item);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::completeObjCMsgSend(CPlusPlus::ClassOrNamespace *binding,
 | 
						|
                                                       bool staticClassAccess)
 | 
						|
{
 | 
						|
    QList<Scope*> memberScopes;
 | 
						|
    foreach (Symbol *s, binding->symbols()) {
 | 
						|
        if (ObjCClass *c = s->asObjCClass())
 | 
						|
            memberScopes.append(c);
 | 
						|
    }
 | 
						|
 | 
						|
    foreach (Scope *scope, memberScopes) {
 | 
						|
        for (unsigned i = 0; i < scope->memberCount(); ++i) {
 | 
						|
            Symbol *symbol = scope->memberAt(i);
 | 
						|
 | 
						|
            if (ObjCMethod *method = symbol->type()->asObjCMethodType()) {
 | 
						|
                if (method->isStatic() == staticClassAccess) {
 | 
						|
                    Overview oo;
 | 
						|
                    const SelectorNameId *selectorName =
 | 
						|
                            method->name()->asSelectorNameId();
 | 
						|
                    QString text;
 | 
						|
                    QString data;
 | 
						|
                    if (selectorName->hasArguments()) {
 | 
						|
                        for (unsigned i = 0; i < selectorName->nameCount(); ++i) {
 | 
						|
                            if (i > 0)
 | 
						|
                                text += QLatin1Char(' ');
 | 
						|
                            Symbol *arg = method->argumentAt(i);
 | 
						|
                            text += selectorName->nameAt(i)->identifier()->chars();
 | 
						|
                            text += QLatin1Char(':');
 | 
						|
                            text += TextEditor::Snippet::kVariableDelimiter;
 | 
						|
                            text += QLatin1Char('(');
 | 
						|
                            text += oo(arg->type());
 | 
						|
                            text += QLatin1Char(')');
 | 
						|
                            text += oo(arg->name());
 | 
						|
                            text += TextEditor::Snippet::kVariableDelimiter;
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        text = selectorName->identifier()->chars();
 | 
						|
                    }
 | 
						|
                    data = text;
 | 
						|
 | 
						|
                    if (!text.isEmpty())
 | 
						|
                        addCompletionItem(text, QIcon(), 0, QVariant::fromValue(data));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
 | 
						|
{
 | 
						|
    QString directoryPrefix;
 | 
						|
    if (m_model->m_completionOperator == T_SLASH) {
 | 
						|
        QTextCursor c = cursor;
 | 
						|
        c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
 | 
						|
        QString sel = c.selectedText();
 | 
						|
        int startCharPos = sel.indexOf(QLatin1Char('"'));
 | 
						|
        if (startCharPos == -1) {
 | 
						|
            startCharPos = sel.indexOf(QLatin1Char('<'));
 | 
						|
            m_model->m_completionOperator = T_ANGLE_STRING_LITERAL;
 | 
						|
        } else {
 | 
						|
            m_model->m_completionOperator = T_STRING_LITERAL;
 | 
						|
        }
 | 
						|
        if (startCharPos != -1)
 | 
						|
            directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
 | 
						|
    }
 | 
						|
 | 
						|
    // Make completion for all relevant includes
 | 
						|
    QStringList includePaths = m_interface->includePaths();
 | 
						|
    const QString ¤tFilePath = QFileInfo(m_interface->file()->fileName()).path();
 | 
						|
    if (!includePaths.contains(currentFilePath))
 | 
						|
        includePaths.append(currentFilePath);
 | 
						|
 | 
						|
    const Core::MimeType mimeType =
 | 
						|
            Core::ICore::instance()->mimeDatabase()->findByType(QLatin1String("text/x-c++hdr"));
 | 
						|
    const QStringList suffixes = mimeType.suffixes();
 | 
						|
 | 
						|
    foreach (const QString &includePath, includePaths) {
 | 
						|
        QString realPath = includePath;
 | 
						|
        if (!directoryPrefix.isEmpty()) {
 | 
						|
            realPath += QLatin1Char('/');
 | 
						|
            realPath += directoryPrefix;
 | 
						|
        }
 | 
						|
        completeInclude(realPath, suffixes);
 | 
						|
    }
 | 
						|
 | 
						|
    foreach (const QString &frameworkPath, m_interface->frameworkPaths()) {
 | 
						|
        QString realPath = frameworkPath;
 | 
						|
        if (!directoryPrefix.isEmpty()) {
 | 
						|
            realPath += QLatin1Char('/');
 | 
						|
            realPath += directoryPrefix;
 | 
						|
            realPath += QLatin1String(".framework/Headers");
 | 
						|
        }
 | 
						|
        completeInclude(realPath, suffixes);
 | 
						|
    }
 | 
						|
 | 
						|
    return !m_completions.isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::completeInclude(const QString &realPath,
 | 
						|
                                                   const QStringList &suffixes)
 | 
						|
{
 | 
						|
    QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
 | 
						|
    while (i.hasNext()) {
 | 
						|
        const QString fileName = i.next();
 | 
						|
        const QFileInfo fileInfo = i.fileInfo();
 | 
						|
        const QString suffix = fileInfo.suffix();
 | 
						|
        if (suffix.isEmpty() || suffixes.contains(suffix)) {
 | 
						|
            QString text = fileName.mid(realPath.length() + 1);
 | 
						|
            if (fileInfo.isDir())
 | 
						|
                text += QLatin1Char('/');
 | 
						|
            addCompletionItem(text, m_icons.keywordIcon());
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::completePreprocessor()
 | 
						|
{
 | 
						|
    foreach (const QString &preprocessorCompletion, preprocessorCompletions)
 | 
						|
        addCompletionItem(preprocessorCompletion);
 | 
						|
 | 
						|
    if (objcKeywordsWanted())
 | 
						|
        addCompletionItem(QLatin1String("import"));
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::objcKeywordsWanted() const
 | 
						|
{
 | 
						|
    if (!m_objcEnabled)
 | 
						|
        return false;
 | 
						|
 | 
						|
    const Core::IFile *file = m_interface->file();
 | 
						|
    QString fileName = file->fileName();
 | 
						|
 | 
						|
    const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase();
 | 
						|
    return mdb->findByFile(fileName).type() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE;
 | 
						|
}
 | 
						|
 | 
						|
int CppCompletionAssistProcessor::startCompletionInternal(const QString fileName,
 | 
						|
                                                          unsigned line, unsigned column,
 | 
						|
                                                          const QString &expr,
 | 
						|
                                                          int endOfExpression)
 | 
						|
{
 | 
						|
    QString expression = expr.trimmed();
 | 
						|
 | 
						|
    Document::Ptr thisDocument = m_model->m_snapshot.document(fileName);
 | 
						|
    if (! thisDocument)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    typeOfExpression.init(thisDocument, m_model->m_snapshot);
 | 
						|
 | 
						|
    Scope *scope = thisDocument->scopeAt(line, column);
 | 
						|
    Q_ASSERT(scope != 0);
 | 
						|
 | 
						|
    if (expression.isEmpty()) {
 | 
						|
        if (m_model->m_completionOperator == T_EOF_SYMBOL || m_model->m_completionOperator == T_COLON_COLON) {
 | 
						|
            (void) typeOfExpression(expression, scope);
 | 
						|
            globalCompletion(scope);
 | 
						|
            if (m_completions.isEmpty())
 | 
						|
                return -1;
 | 
						|
            return m_startPosition;
 | 
						|
        }
 | 
						|
 | 
						|
        else if (m_model->m_completionOperator == T_SIGNAL || m_model->m_completionOperator == T_SLOT) {
 | 
						|
            // Apply signal/slot completion on 'this'
 | 
						|
            expression = QLatin1String("this");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    QList<LookupItem> results = typeOfExpression(expression, scope, TypeOfExpression::Preprocess);
 | 
						|
 | 
						|
    if (results.isEmpty()) {
 | 
						|
        if (m_model->m_completionOperator == T_SIGNAL || m_model->m_completionOperator == T_SLOT) {
 | 
						|
            if (! (expression.isEmpty() || expression == QLatin1String("this"))) {
 | 
						|
                expression = QLatin1String("this");
 | 
						|
                results = typeOfExpression(expression, scope);
 | 
						|
            }
 | 
						|
 | 
						|
            if (results.isEmpty())
 | 
						|
                return -1;
 | 
						|
 | 
						|
        } else if (m_model->m_completionOperator == T_LPAREN) {
 | 
						|
            // Find the expression that precedes the current name
 | 
						|
            int index = endOfExpression;
 | 
						|
            while (m_interface->characterAt(index - 1).isSpace())
 | 
						|
                --index;
 | 
						|
            index = findStartOfName(index);
 | 
						|
 | 
						|
            QTextCursor tc(m_interface->document());
 | 
						|
            tc.setPosition(index);
 | 
						|
 | 
						|
            ExpressionUnderCursor expressionUnderCursor;
 | 
						|
            const QString baseExpression = expressionUnderCursor(tc);
 | 
						|
 | 
						|
            // Resolve the type of this expression
 | 
						|
            const QList<LookupItem> results =
 | 
						|
                    typeOfExpression(baseExpression, scope,
 | 
						|
                                     TypeOfExpression::Preprocess);
 | 
						|
 | 
						|
            // If it's a class, add completions for the constructors
 | 
						|
            foreach (const LookupItem &result, results) {
 | 
						|
                if (result.type()->isClassType()) {
 | 
						|
                    if (completeConstructorOrFunction(results, endOfExpression, true))
 | 
						|
                        return m_startPosition;
 | 
						|
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return -1;
 | 
						|
 | 
						|
        } else {
 | 
						|
            // nothing to do.
 | 
						|
            return -1;
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    switch (m_model->m_completionOperator) {
 | 
						|
    case T_LPAREN:
 | 
						|
        if (completeConstructorOrFunction(results, endOfExpression, false))
 | 
						|
            return m_startPosition;
 | 
						|
        break;
 | 
						|
 | 
						|
    case T_DOT:
 | 
						|
    case T_ARROW:
 | 
						|
        if (completeMember(results))
 | 
						|
            return m_startPosition;
 | 
						|
        break;
 | 
						|
 | 
						|
    case T_COLON_COLON:
 | 
						|
        if (completeScope(results))
 | 
						|
            return m_startPosition;
 | 
						|
        break;
 | 
						|
 | 
						|
    case T_SIGNAL:
 | 
						|
        if (completeSignal(results))
 | 
						|
            return m_startPosition;
 | 
						|
        break;
 | 
						|
 | 
						|
    case T_SLOT:
 | 
						|
        if (completeSlot(results))
 | 
						|
            return m_startPosition;
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    } // end of switch
 | 
						|
 | 
						|
    // nothing to do.
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::globalCompletion(CPlusPlus::Scope *currentScope)
 | 
						|
{
 | 
						|
    const LookupContext &context = typeOfExpression.context();
 | 
						|
 | 
						|
    if (m_model->m_completionOperator == T_COLON_COLON) {
 | 
						|
        completeNamespace(context.globalNamespace());
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    QList<ClassOrNamespace *> usingBindings;
 | 
						|
    ClassOrNamespace *currentBinding = 0;
 | 
						|
 | 
						|
    for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
 | 
						|
        if (scope->isBlock()) {
 | 
						|
            if (ClassOrNamespace *binding = context.lookupType(scope)) {
 | 
						|
                for (unsigned i = 0; i < scope->memberCount(); ++i) {
 | 
						|
                    Symbol *member = scope->memberAt(i);
 | 
						|
                    if (! member->name())
 | 
						|
                        continue;
 | 
						|
                    else if (UsingNamespaceDirective *u = member->asUsingNamespaceDirective()) {
 | 
						|
                        if (ClassOrNamespace *b = binding->lookupType(u->name()))
 | 
						|
                            usingBindings.append(b);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } else if (scope->isFunction() || scope->isClass() || scope->isNamespace()) {
 | 
						|
            currentBinding = context.lookupType(scope);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (Scope *scope = currentScope; scope; scope = scope->enclosingScope()) {
 | 
						|
        if (scope->isBlock()) {
 | 
						|
            for (unsigned i = 0; i < scope->memberCount(); ++i) {
 | 
						|
                addCompletionItem(scope->memberAt(i));
 | 
						|
            }
 | 
						|
        } else if (scope->isFunction()) {
 | 
						|
            Function *fun = scope->asFunction();
 | 
						|
            for (unsigned i = 0; i < fun->argumentCount(); ++i) {
 | 
						|
                addCompletionItem(fun->argumentAt(i));
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        } else {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (; currentBinding; currentBinding = currentBinding->parent()) {
 | 
						|
        const QList<Symbol *> symbols = currentBinding->symbols();
 | 
						|
 | 
						|
        if (! symbols.isEmpty()) {
 | 
						|
            if (symbols.first()->isNamespace())
 | 
						|
                completeNamespace(currentBinding);
 | 
						|
            else
 | 
						|
                completeClass(currentBinding);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    foreach (ClassOrNamespace *b, usingBindings)
 | 
						|
        completeNamespace(b);
 | 
						|
 | 
						|
    addKeywords();
 | 
						|
    addMacros(QLatin1String("<configuration>"), context.snapshot());
 | 
						|
    addMacros(context.thisDocument()->fileName(), context.snapshot());
 | 
						|
    addSnippets();
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::completeMember(const QList<CPlusPlus::LookupItem> &baseResults)
 | 
						|
{
 | 
						|
    const LookupContext &context = typeOfExpression.context();
 | 
						|
 | 
						|
    if (baseResults.isEmpty())
 | 
						|
        return false;
 | 
						|
 | 
						|
    ResolveExpression resolveExpression(context);
 | 
						|
 | 
						|
    if (ClassOrNamespace *binding =
 | 
						|
            resolveExpression.baseExpression(baseResults,
 | 
						|
                                             m_model->m_completionOperator,
 | 
						|
                                             &m_model->m_replaceDotForArrow)) {
 | 
						|
        if (binding)
 | 
						|
            completeClass(binding, /*static lookup = */ false);
 | 
						|
 | 
						|
        return ! m_completions.isEmpty();
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::completeScope(const QList<CPlusPlus::LookupItem> &results)
 | 
						|
{
 | 
						|
    const LookupContext &context = typeOfExpression.context();
 | 
						|
    if (results.isEmpty())
 | 
						|
        return false;
 | 
						|
 | 
						|
    foreach (const LookupItem &result, results) {
 | 
						|
        FullySpecifiedType ty = result.type();
 | 
						|
        Scope *scope = result.scope();
 | 
						|
 | 
						|
        if (NamedType *namedTy = ty->asNamedType()) {
 | 
						|
            if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
 | 
						|
                completeClass(b);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
        } else if (Class *classTy = ty->asClassType()) {
 | 
						|
            if (ClassOrNamespace *b = context.lookupType(classTy)) {
 | 
						|
                completeClass(b);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
        } else if (Namespace *nsTy = ty->asNamespaceType()) {
 | 
						|
            if (ClassOrNamespace *b = context.lookupType(nsTy)) {
 | 
						|
                completeNamespace(b);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return ! m_completions.isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::completeNamespace(CPlusPlus::ClassOrNamespace *b)
 | 
						|
{
 | 
						|
    QSet<ClassOrNamespace *> bindingsVisited;
 | 
						|
    QList<ClassOrNamespace *> bindingsToVisit;
 | 
						|
    bindingsToVisit.append(b);
 | 
						|
 | 
						|
    while (! bindingsToVisit.isEmpty()) {
 | 
						|
        ClassOrNamespace *binding = bindingsToVisit.takeFirst();
 | 
						|
        if (! binding || bindingsVisited.contains(binding))
 | 
						|
            continue;
 | 
						|
 | 
						|
        bindingsVisited.insert(binding);
 | 
						|
        bindingsToVisit += binding->usings();
 | 
						|
 | 
						|
        QList<Scope *> scopesToVisit;
 | 
						|
        QSet<Scope *> scopesVisited;
 | 
						|
 | 
						|
        foreach (Symbol *bb, binding->symbols()) {
 | 
						|
            if (Namespace *ns = bb->asNamespace())
 | 
						|
                scopesToVisit.append(ns);
 | 
						|
        }
 | 
						|
 | 
						|
        foreach (Enum *e, binding->enums()) {
 | 
						|
            scopesToVisit.append(e);
 | 
						|
        }
 | 
						|
 | 
						|
        while (! scopesToVisit.isEmpty()) {
 | 
						|
            Scope *scope = scopesToVisit.takeFirst();
 | 
						|
            if (! scope || scopesVisited.contains(scope))
 | 
						|
                continue;
 | 
						|
 | 
						|
            scopesVisited.insert(scope);
 | 
						|
 | 
						|
            for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
 | 
						|
                Symbol *member = *it;
 | 
						|
                addCompletionItem(member);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::completeClass(CPlusPlus::ClassOrNamespace *b, bool staticLookup)
 | 
						|
{
 | 
						|
    QSet<ClassOrNamespace *> bindingsVisited;
 | 
						|
    QList<ClassOrNamespace *> bindingsToVisit;
 | 
						|
    bindingsToVisit.append(b);
 | 
						|
 | 
						|
    while (! bindingsToVisit.isEmpty()) {
 | 
						|
        ClassOrNamespace *binding = bindingsToVisit.takeFirst();
 | 
						|
        if (! binding || bindingsVisited.contains(binding))
 | 
						|
            continue;
 | 
						|
 | 
						|
        bindingsVisited.insert(binding);
 | 
						|
        bindingsToVisit += binding->usings();
 | 
						|
 | 
						|
        QList<Scope *> scopesToVisit;
 | 
						|
        QSet<Scope *> scopesVisited;
 | 
						|
 | 
						|
        foreach (Symbol *bb, binding->symbols()) {
 | 
						|
            if (Class *k = bb->asClass())
 | 
						|
                scopesToVisit.append(k);
 | 
						|
        }
 | 
						|
 | 
						|
        foreach (Enum *e, binding->enums())
 | 
						|
            scopesToVisit.append(e);
 | 
						|
 | 
						|
        while (! scopesToVisit.isEmpty()) {
 | 
						|
            Scope *scope = scopesToVisit.takeFirst();
 | 
						|
            if (! scope || scopesVisited.contains(scope))
 | 
						|
                continue;
 | 
						|
 | 
						|
            scopesVisited.insert(scope);
 | 
						|
 | 
						|
            addCompletionItem(scope); // add a completion item for the injected class name.
 | 
						|
 | 
						|
            for (Scope::iterator it = scope->firstMember(); it != scope->lastMember(); ++it) {
 | 
						|
                Symbol *member = *it;
 | 
						|
                if (member->isFriend()
 | 
						|
                        || member->isQtPropertyDeclaration()
 | 
						|
                        || member->isQtEnum()) {
 | 
						|
                    continue;
 | 
						|
                } else if (! staticLookup && (member->isTypedef() ||
 | 
						|
                                            member->isEnum()    ||
 | 
						|
                                            member->isClass())) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                addCompletionItem(member);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::completeQtMethod(const QList<CPlusPlus::LookupItem> &results, bool wantSignals)
 | 
						|
{
 | 
						|
    if (results.isEmpty())
 | 
						|
        return false;
 | 
						|
 | 
						|
    const LookupContext &context = typeOfExpression.context();
 | 
						|
 | 
						|
    ConvertToCompletionItem toCompletionItem;
 | 
						|
    Overview o;
 | 
						|
    o.setShowReturnTypes(false);
 | 
						|
    o.setShowArgumentNames(false);
 | 
						|
    o.setShowFunctionSignatures(true);
 | 
						|
 | 
						|
    QSet<QString> signatures;
 | 
						|
    foreach (const LookupItem &p, results) {
 | 
						|
        FullySpecifiedType ty = p.type().simplified();
 | 
						|
 | 
						|
        if (PointerType *ptrTy = ty->asPointerType())
 | 
						|
            ty = ptrTy->elementType().simplified();
 | 
						|
        else
 | 
						|
            continue; // not a pointer or a reference to a pointer.
 | 
						|
 | 
						|
        NamedType *namedTy = ty->asNamedType();
 | 
						|
        if (! namedTy) // not a class name.
 | 
						|
            continue;
 | 
						|
 | 
						|
        ClassOrNamespace *b = context.lookupType(namedTy->name(), p.scope());
 | 
						|
        if (! b)
 | 
						|
            continue;
 | 
						|
 | 
						|
        QList<ClassOrNamespace *>todo;
 | 
						|
        QSet<ClassOrNamespace *> processed;
 | 
						|
        QList<Scope *> scopes;
 | 
						|
        todo.append(b);
 | 
						|
        while (!todo.isEmpty()) {
 | 
						|
            ClassOrNamespace *binding = todo.takeLast();
 | 
						|
            if (!processed.contains(binding)) {
 | 
						|
                processed.insert(binding);
 | 
						|
 | 
						|
                foreach (Symbol *s, binding->symbols())
 | 
						|
                    if (Class *clazz = s->asClass())
 | 
						|
                        scopes.append(clazz);
 | 
						|
 | 
						|
                todo.append(binding->usings());
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        foreach (Scope *scope, scopes) {
 | 
						|
            if (! scope->isClass())
 | 
						|
                continue;
 | 
						|
 | 
						|
            for (unsigned i = 0; i < scope->memberCount(); ++i) {
 | 
						|
                Symbol *member = scope->memberAt(i);
 | 
						|
                Function *fun = member->type()->asFunctionType();
 | 
						|
                if (! fun)
 | 
						|
                    continue;
 | 
						|
                if (wantSignals && ! fun->isSignal())
 | 
						|
                    continue;
 | 
						|
                else if (! wantSignals && ! fun->isSlot())
 | 
						|
                    continue;
 | 
						|
                BasicProposalItem *item = toCompletionItem(fun);
 | 
						|
                if (item) {
 | 
						|
                    unsigned count = fun->argumentCount();
 | 
						|
                    while (true) {
 | 
						|
                        BasicProposalItem *ci = item;
 | 
						|
 | 
						|
                        QString signature;
 | 
						|
                        signature += Overview().prettyName(fun->name());
 | 
						|
                        signature += QLatin1Char('(');
 | 
						|
                        for (unsigned i = 0; i < count; ++i) {
 | 
						|
                            Symbol *arg = fun->argumentAt(i);
 | 
						|
                            if (i != 0)
 | 
						|
                                signature += QLatin1Char(',');
 | 
						|
                            signature += o.prettyType(arg->type());
 | 
						|
                        }
 | 
						|
                        signature += QLatin1Char(')');
 | 
						|
 | 
						|
                        const QByteArray normalized =
 | 
						|
                                QMetaObject::normalizedSignature(signature.toLatin1());
 | 
						|
 | 
						|
                        signature = QString::fromLatin1(normalized, normalized.size());
 | 
						|
 | 
						|
                        if (! signatures.contains(signature)) {
 | 
						|
                            signatures.insert(signature);
 | 
						|
 | 
						|
                            ci->setText(signature); // fix the completion item.
 | 
						|
                            m_completions.append(ci);
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer())
 | 
						|
                            --count;
 | 
						|
                        else
 | 
						|
                            break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return ! m_completions.isEmpty();
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::addSnippets()
 | 
						|
{
 | 
						|
    m_completions.append(m_snippetCollector.collect());
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::addKeywords()
 | 
						|
{
 | 
						|
    int keywordLimit = T_FIRST_OBJC_AT_KEYWORD;
 | 
						|
    if (objcKeywordsWanted())
 | 
						|
        keywordLimit = T_LAST_OBJC_AT_KEYWORD + 1;
 | 
						|
 | 
						|
    // keyword completion items.
 | 
						|
    for (int i = T_FIRST_KEYWORD; i < keywordLimit; ++i)
 | 
						|
        addCompletionItem(QLatin1String(Token::name(i)), m_icons.keywordIcon());
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::addMacros(const QString &fileName, const CPlusPlus::Snapshot &snapshot)
 | 
						|
{
 | 
						|
    QSet<QString> processed;
 | 
						|
    QSet<QString> definedMacros;
 | 
						|
 | 
						|
    addMacros_helper(snapshot, fileName, &processed, &definedMacros);
 | 
						|
 | 
						|
    foreach (const QString ¯oName, definedMacros)
 | 
						|
        addCompletionItem(macroName, m_icons.macroIcon());
 | 
						|
}
 | 
						|
 | 
						|
void CppCompletionAssistProcessor::addMacros_helper(const CPlusPlus::Snapshot &snapshot,
 | 
						|
                                                    const QString &fileName,
 | 
						|
                                                    QSet<QString> *processed,
 | 
						|
                                                    QSet<QString> *definedMacros)
 | 
						|
{
 | 
						|
    Document::Ptr doc = snapshot.document(fileName);
 | 
						|
 | 
						|
    if (! doc || processed->contains(doc->fileName()))
 | 
						|
        return;
 | 
						|
 | 
						|
    processed->insert(doc->fileName());
 | 
						|
 | 
						|
    foreach (const Document::Include &i, doc->includes()) {
 | 
						|
        addMacros_helper(snapshot, i.fileName(), processed, definedMacros);
 | 
						|
    }
 | 
						|
 | 
						|
    foreach (const Macro ¯o, doc->definedMacros()) {
 | 
						|
        const QString macroName = QString::fromUtf8(macro.name().constData(), macro.name().length());
 | 
						|
        if (! macro.isHidden())
 | 
						|
            definedMacros->insert(macroName);
 | 
						|
        else
 | 
						|
            definedMacros->remove(macroName);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool CppCompletionAssistProcessor::completeConstructorOrFunction(const QList<CPlusPlus::LookupItem> &results,
 | 
						|
                                                                 int endOfExpression,
 | 
						|
                                                                 bool toolTipOnly)
 | 
						|
{
 | 
						|
    const LookupContext &context = typeOfExpression.context();
 | 
						|
    QList<Function *> functions;
 | 
						|
 | 
						|
    foreach (const LookupItem &result, results) {
 | 
						|
        FullySpecifiedType exprTy = result.type().simplified();
 | 
						|
 | 
						|
        if (Class *klass = asClassOrTemplateClassType(exprTy)) {
 | 
						|
            const Name *className = klass->name();
 | 
						|
            if (! className)
 | 
						|
                continue; // nothing to do for anonymous classes.
 | 
						|
 | 
						|
            for (unsigned i = 0; i < klass->memberCount(); ++i) {
 | 
						|
                Symbol *member = klass->memberAt(i);
 | 
						|
                const Name *memberName = member->name();
 | 
						|
 | 
						|
                if (! memberName)
 | 
						|
                    continue; // skip anonymous member.
 | 
						|
 | 
						|
                else if (memberName->isQualifiedNameId())
 | 
						|
                    continue; // skip
 | 
						|
 | 
						|
                if (Function *funTy = member->type()->asFunctionType()) {
 | 
						|
                    if (memberName->isEqualTo(className)) {
 | 
						|
                        // it's a ctor.
 | 
						|
                        functions.append(funTy);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (functions.isEmpty()) {
 | 
						|
        foreach (const LookupItem &result, results) {
 | 
						|
            FullySpecifiedType ty = result.type().simplified();
 | 
						|
 | 
						|
            if (Function *fun = asFunctionOrTemplateFunctionType(ty)) {
 | 
						|
 | 
						|
                if (! fun->name())
 | 
						|
                    continue;
 | 
						|
                else if (! functions.isEmpty() && enclosingNonTemplateScope(functions.first()) != enclosingNonTemplateScope(fun))
 | 
						|
                    continue; // skip fun, it's an hidden declaration.
 | 
						|
 | 
						|
                bool newOverload = true;
 | 
						|
 | 
						|
                foreach (Function *f, functions) {
 | 
						|
                    if (fun->isEqualTo(f)) {
 | 
						|
                        newOverload = false;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (newOverload)
 | 
						|
                    functions.append(fun);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (functions.isEmpty()) {
 | 
						|
        const Name *functionCallOp = context.control()->operatorNameId(OperatorNameId::FunctionCallOp);
 | 
						|
 | 
						|
        foreach (const LookupItem &result, results) {
 | 
						|
            FullySpecifiedType ty = result.type().simplified();
 | 
						|
            Scope *scope = result.scope();
 | 
						|
 | 
						|
            if (NamedType *namedTy = ty->asNamedType()) {
 | 
						|
                if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
 | 
						|
                    foreach (const LookupItem &r, b->lookup(functionCallOp)) {
 | 
						|
                        Symbol *overload = r.declaration();
 | 
						|
                        FullySpecifiedType overloadTy = overload->type().simplified();
 | 
						|
 | 
						|
                        if (Function *funTy = overloadTy->asFunctionType()) {
 | 
						|
                            functions.append(funTy);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // There are two different kinds of completion we want to provide:
 | 
						|
    // 1. If this is a function call, we want to pop up a tooltip that shows the user
 | 
						|
    // the possible overloads with their argument types and names.
 | 
						|
    // 2. If this is a function definition, we want to offer autocompletion of
 | 
						|
    // the function signature.
 | 
						|
 | 
						|
    // check if function signature autocompletion is appropriate
 | 
						|
    // Also check if the function name is a destructor name.
 | 
						|
    bool isDestructor = false;
 | 
						|
    if (! functions.isEmpty() && ! toolTipOnly) {
 | 
						|
 | 
						|
        // function definitions will only happen in class or namespace scope,
 | 
						|
        // so get the current location's enclosing scope.
 | 
						|
 | 
						|
        // get current line and column
 | 
						|
        int lineSigned = 0, columnSigned = 0;
 | 
						|
        Convenience::convertPosition(m_interface->document(), m_interface->position(), &lineSigned, &columnSigned);
 | 
						|
        unsigned line = lineSigned, column = columnSigned;
 | 
						|
 | 
						|
        // find a scope that encloses the current location, starting from the lastVisibileSymbol
 | 
						|
        // and moving outwards
 | 
						|
 | 
						|
        Scope *sc = context.thisDocument()->scopeAt(line, column);
 | 
						|
 | 
						|
        if (sc && (sc->isClass() || sc->isNamespace())) {
 | 
						|
            // It may still be a function call. If the whole line parses as a function
 | 
						|
            // declaration, we should be certain that it isn't.
 | 
						|
            bool autocompleteSignature = false;
 | 
						|
 | 
						|
            QTextCursor tc(m_interface->document());
 | 
						|
            tc.setPosition(endOfExpression);
 | 
						|
            BackwardsScanner bs(tc);
 | 
						|
            const int startToken = bs.startToken();
 | 
						|
            int lineStartToken = bs.startOfLine(startToken);
 | 
						|
            // make sure the required tokens are actually available
 | 
						|
            bs.LA(startToken - lineStartToken);
 | 
						|
            QString possibleDecl = bs.mid(lineStartToken).trimmed().append("();");
 | 
						|
 | 
						|
            Document::Ptr doc = Document::create(QLatin1String("<completion>"));
 | 
						|
            doc->setSource(possibleDecl.toLatin1());
 | 
						|
            if (doc->parse(Document::ParseDeclaration)) {
 | 
						|
                doc->check();
 | 
						|
                if (SimpleDeclarationAST *sd = doc->translationUnit()->ast()->asSimpleDeclaration()) {
 | 
						|
                    if (sd->declarator_list &&
 | 
						|
                        sd->declarator_list && sd->declarator_list->value->postfix_declarator_list
 | 
						|
                        && sd->declarator_list->value->postfix_declarator_list->value->asFunctionDeclarator()) {
 | 
						|
 | 
						|
                        autocompleteSignature = true;
 | 
						|
 | 
						|
                        CoreDeclaratorAST *coreDecl = sd->declarator_list->value->core_declarator;
 | 
						|
                        if (coreDecl && coreDecl->asDeclaratorId() && coreDecl->asDeclaratorId()->name) {
 | 
						|
                            NameAST *declName = coreDecl->asDeclaratorId()->name;
 | 
						|
                            if (declName->asDestructorName()) {
 | 
						|
                                isDestructor = true;
 | 
						|
                            } else if (QualifiedNameAST *qName = declName->asQualifiedName()) {
 | 
						|
                                if (qName->unqualified_name && qName->unqualified_name->asDestructorName())
 | 
						|
                                    isDestructor = true;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (autocompleteSignature && !isDestructor) {
 | 
						|
                // set up signature autocompletion
 | 
						|
                foreach (Function *f, functions) {
 | 
						|
                    Overview overview;
 | 
						|
                    overview.setShowArgumentNames(true);
 | 
						|
                    overview.setShowDefaultArguments(false);
 | 
						|
 | 
						|
                    // gets: "parameter list) cv-spec",
 | 
						|
                    QString completion = overview(f->type()).mid(1);
 | 
						|
 | 
						|
                    addCompletionItem(completion, QIcon(), 0,
 | 
						|
                                      QVariant::fromValue(CompleteFunctionDeclaration(f)));
 | 
						|
                }
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (! functions.empty() && !isDestructor) {
 | 
						|
        m_hintProposal = createHintProposal(functions);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 |