clangcompletioncontextanalyzer.cpp 7.35 KB
Newer Older
1 2
/****************************************************************************
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5 6 7 8 9 10 11
**
** This file is part of Qt Creator.
**
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 24 25 26 27
**
****************************************************************************/

#include "clangcompletioncontextanalyzer.h"

28 29
#include "clangactivationsequencecontextprocessor.h"
#include "clangactivationsequenceprocessor.h"
30

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
#include <texteditor/codeassist/assistinterface.h>

#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/SimpleLexer.h>

#include <utils/qtcassert.h>

#include <QDebug>
#include <QTextBlock>
#include <QTextCursor>

using namespace CPlusPlus;

namespace {

bool isTokenForIncludePathCompletion(unsigned tokenKind)
{
    return tokenKind == T_STRING_LITERAL
        || tokenKind == T_ANGLE_STRING_LITERAL
        || tokenKind == T_SLASH;
}

bool isTokenForPassThrough(unsigned tokenKind)
{
    return tokenKind == T_EOF_SYMBOL
        || tokenKind == T_DOT
        || tokenKind == T_COLON_COLON
        || tokenKind == T_ARROW
        || tokenKind == T_DOT_STAR;
}

} // anonymous namespace

namespace ClangCodeModel {
namespace Internal {

ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
69
        const ClangCompletionAssistInterface *assistInterface,
70 71 72 73 74 75 76 77 78 79 80
        CPlusPlus::LanguageFeatures languageFeatures)
    : m_interface(assistInterface)
    , m_languageFeatures(languageFeatures)
{
}

void ClangCompletionContextAnalyzer::analyze()
{
    QTC_ASSERT(m_interface, return);
    setActionAndClangPosition(PassThroughToLibClang, -1);

81 82
    ActivationSequenceContextProcessor activationSequenceContextProcessor(m_interface);
    m_completionOperator = activationSequenceContextProcessor.completionKind();
83 84 85
    int afterOperatorPosition = activationSequenceContextProcessor.startOfNamePosition();
    m_positionEndOfExpression = activationSequenceContextProcessor.operatorStartPosition();
    m_positionForProposal = activationSequenceContextProcessor.startOfNamePosition();
86

87 88 89 90
    const bool actionIsSet = handleNonFunctionCall(afterOperatorPosition);
    if (!actionIsSet) {
        handleCommaInFunctionCall();
        handleFunctionCall(afterOperatorPosition);
91 92 93
    }
}

94 95
ClangCompletionContextAnalyzer::FunctionInfo
ClangCompletionContextAnalyzer::analyzeFunctionCall(int endOfOperator) const
96
{
97 98
    int index = ActivationSequenceContextProcessor::skipPrecedingWhitespace(m_interface,
                                                                            endOfOperator);
99 100 101 102 103
    QTextCursor textCursor(m_interface->textDocument());
    textCursor.setPosition(index);

    ExpressionUnderCursor euc(m_languageFeatures);
    index = euc.startOfFunctionCall(textCursor);
104
    index = ActivationSequenceContextProcessor::skipPrecedingWhitespace(m_interface, index);
105 106
    const int functionNameStart = ActivationSequenceContextProcessor::findStartOfName(m_interface,
                                                                                      index);
107 108 109 110 111 112 113 114 115 116 117

    QTextCursor textCursor2(m_interface->textDocument());
    textCursor2.setPosition(functionNameStart);
    textCursor2.setPosition(index, QTextCursor::KeepAnchor);

    FunctionInfo info;
    info.functionNamePosition = functionNameStart;
    info.functionName = textCursor2.selectedText().trimmed();
    return info;
}

118 119
void ClangCompletionContextAnalyzer::setActionAndClangPosition(CompletionAction action,
                                                               int position)
120
{
121 122 123 124
    QTC_CHECK(position >= -1);
    m_completionAction = action;
    m_positionForClang = position;
}
125

126 127
void
ClangCompletionContextAnalyzer::setAction(ClangCompletionContextAnalyzer::CompletionAction action)
128 129 130
{
    setActionAndClangPosition(action, -1);
}
131

132 133 134 135 136 137 138 139 140 141
void ClangCompletionContextAnalyzer::handleCommaInFunctionCall()
{
    if (m_completionOperator == T_COMMA) {
        ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
        QTextCursor textCursor(m_interface->textDocument());
        textCursor.setPosition(m_positionEndOfExpression);
        const int start = expressionUnderCursor.startOfFunctionCall(textCursor);
        m_positionEndOfExpression = start;
        m_positionForProposal = start + 1; // After '(' of function call
        m_completionOperator = T_LPAREN;
142
    }
143 144 145 146 147 148 149 150 151
}

void ClangCompletionContextAnalyzer::handleFunctionCall(int afterOperatorPosition)
{
    if (m_completionOperator == T_LPAREN) {
        ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
        QTextCursor textCursor(m_interface->textDocument());
        textCursor.setPosition(m_positionEndOfExpression);
        const QString expression = expressionUnderCursor(textCursor);
152

153 154 155 156 157 158 159 160 161 162
        if (expression.endsWith(QLatin1String("SIGNAL"))) {
            setActionAndClangPosition(CompleteSignal, afterOperatorPosition);
        } else if (expression.endsWith(QLatin1String("SLOT"))) {
            setActionAndClangPosition(CompleteSlot, afterOperatorPosition);
        } else if (m_interface->position() != afterOperatorPosition) {
            // No function completion if cursor is not after '(' or ','
            m_positionForProposal = afterOperatorPosition;
            setActionAndClangPosition(PassThroughToLibClang, afterOperatorPosition);
        } else {
            const FunctionInfo functionInfo = analyzeFunctionCall(afterOperatorPosition);
163 164 165 166 167 168 169 170
            if (functionInfo.isValid()) {
                m_functionName = functionInfo.functionName;
                setActionAndClangPosition(PassThroughToLibClangAfterLeftParen,
                                          functionInfo.functionNamePosition);
            } else {
                m_positionForProposal = afterOperatorPosition;
                setActionAndClangPosition(PassThroughToLibClang, afterOperatorPosition);
            }
171 172
        }
    }
173 174
}

175
bool ClangCompletionContextAnalyzer::handleNonFunctionCall(int position)
176
{
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    if (isTokenForPassThrough(m_completionOperator)) {
        setActionAndClangPosition(PassThroughToLibClang, position);
        return true;
    } else if (m_completionOperator == T_DOXY_COMMENT) {
        setAction(CompleteDoxygenKeyword);
        return true;
    } else if (m_completionOperator == T_POUND) {
        // TODO: Check if libclang can complete preprocessor directives
        setAction(CompletePreprocessorDirective);
        return true;
    } else if (isTokenForIncludePathCompletion(m_completionOperator)) {
        setAction(CompleteIncludePath);
        return true;
    }

    return false;
193 194 195 196
}

} // namespace Internal
} // namespace ClangCodeModel