diff options
author | Marco Bubke <marco.bubke@theqtcompany.com> | 2015-07-23 13:01:02 +0200 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@theqtcompany.com> | 2015-07-23 11:13:05 +0000 |
commit | ae5d92d6182ecdf64b3d5453ebcff9fc0c589016 (patch) | |
tree | bb6d233102ca890ff5afa6ce3daec9c962cc26ec /src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp | |
parent | 5a791e88394513a7fd632263f95b632dd84e1cfd (diff) | |
download | qt-creator-ae5d92d6182ecdf64b3d5453ebcff9fc0c589016.tar.gz |
Clang: Refactor ClangCompletionContextAnalyzer
Change-Id: Ib42ddc672da8b068591129e2e0b9652d3e07ad58
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
Diffstat (limited to 'src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp')
-rw-r--r-- | src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp | 301 |
1 files changed, 74 insertions, 227 deletions
diff --git a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp index 568eb9e1e8..f8d63a4a3e 100644 --- a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp +++ b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp @@ -31,6 +31,9 @@ #include "clangcompletioncontextanalyzer.h" +#include "activationsequenceprocessor.h" +#include "activationsequencecontextprocessor.h" + #include <texteditor/codeassist/assistinterface.h> #include <cplusplus/BackwardsScanner.h> @@ -47,80 +50,6 @@ using namespace CPlusPlus; 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; -} - bool isTokenForIncludePathCompletion(unsigned tokenKind) { return tokenKind == T_STRING_LITERAL @@ -143,7 +72,7 @@ namespace ClangCodeModel { namespace Internal { ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer( - const TextEditor::AssistInterface *assistInterface, + const ClangCompletionAssistInterface *assistInterface, CPlusPlus::LanguageFeatures languageFeatures) : m_interface(assistInterface) , m_languageFeatures(languageFeatures) @@ -153,67 +82,20 @@ ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer( void ClangCompletionContextAnalyzer::analyze() { QTC_ASSERT(m_interface, return); - const int startOfName = findStartOfName(); - m_positionForProposal = startOfName; setActionAndClangPosition(PassThroughToLibClang, -1); - const int endOfOperator = skipPrecedingWhitespace(startOfName); - m_completionOperator = T_EOF_SYMBOL; - m_positionEndOfExpression = startOfOperator(endOfOperator, &m_completionOperator, - /*want function call =*/ true); + ActivationSequenceContextProcessor activationSequenceContextProcessor(m_interface); + m_completionOperator = activationSequenceContextProcessor.completionKind(); + int afterOperatorPosition = activationSequenceContextProcessor.positionAfterOperator(); + m_positionEndOfExpression = activationSequenceContextProcessor.positionBeforeOperator(); + m_positionForProposal = activationSequenceContextProcessor.positionAfterOperator(); - if (isTokenForPassThrough(m_completionOperator)) { - setActionAndClangPosition(PassThroughToLibClang, endOfOperator); - return; - } else if (m_completionOperator == T_DOXY_COMMENT) { - setActionAndClangPosition(CompleteDoxygenKeyword, -1); - return; - } else if (m_completionOperator == T_POUND) { - // TODO: Check if libclang can complete preprocessor directives - setActionAndClangPosition(CompletePreprocessorDirective, -1); - return; - } else if (isTokenForIncludePathCompletion(m_completionOperator)) { - setActionAndClangPosition(CompleteIncludePath, -1); - return; - } - - ExpressionUnderCursor expressionUnderCursor(m_languageFeatures); - QTextCursor textCursor(m_interface->textDocument()); + const bool actionIsSet = handleNonFunctionCall(afterOperatorPosition); - if (m_completionOperator == T_COMMA) { // For function hints - textCursor.setPosition(m_positionEndOfExpression); - const int start = expressionUnderCursor.startOfFunctionCall(textCursor); - QTC_ASSERT(start != -1, setActionAndClangPosition(PassThroughToLibClang, startOfName); return); - m_positionEndOfExpression = start; - m_positionForProposal = start + 1; // After '(' of function call - m_completionOperator = T_LPAREN; - } - - if (m_completionOperator == T_LPAREN) { - textCursor.setPosition(m_positionEndOfExpression); - const QString expression = expressionUnderCursor(textCursor); - - if (expression.endsWith(QLatin1String("SIGNAL"))) { - setActionAndClangPosition(CompleteSignal, endOfOperator); - } else if (expression.endsWith(QLatin1String("SLOT"))) { - setActionAndClangPosition(CompleteSlot, endOfOperator); - } else if (m_interface->position() != endOfOperator) { - // No function completion if cursor is not after '(' or ',' - m_positionForProposal = startOfName; - setActionAndClangPosition(PassThroughToLibClang, endOfOperator); - } else { - const FunctionInfo functionInfo = analyzeFunctionCall(endOfOperator); - m_functionName = functionInfo.functionName; - setActionAndClangPosition(PassThroughToLibClangAfterLeftParen, - functionInfo.functionNamePosition); - } - - return; + if (!actionIsSet) { + handleCommaInFunctionCall(); + handleFunctionCall(afterOperatorPosition); } - - QTC_CHECK(!"Unexpected completion context"); - setActionAndClangPosition(PassThroughToLibClang, startOfName); - return; } ClangCompletionContextAnalyzer::FunctionInfo ClangCompletionContextAnalyzer::analyzeFunctionCall( @@ -260,110 +142,75 @@ int ClangCompletionContextAnalyzer::skipPrecedingWhitespace(int position) const return position; } -int ClangCompletionContextAnalyzer::startOfOperator(int pos, - unsigned *kind, - bool wantFunctionCall) const +void ClangCompletionContextAnalyzer::setActionAndClangPosition(CompletionAction action, + int position) { - 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->textDocument()); - 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; - } - } else if (*kind == T_COMMA) { - ExpressionUnderCursor expressionUnderCursor(m_languageFeatures); - if (expressionUnderCursor.startOfFunctionCall(tc) == -1) { - *kind = T_EOF_SYMBOL; - start = pos; - } - } + QTC_CHECK(position >= -1); + m_completionAction = action; + m_positionForClang = position; +} - SimpleLexer tokenize; - tokenize.setLanguageFeatures(m_languageFeatures); - tokenize.setSkipComments(false); - const Tokens &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); +void ClangCompletionContextAnalyzer::setAction(ClangCompletionContextAnalyzer::CompletionAction action) +{ + setActionAndClangPosition(action, -1); +} - 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.bytesBegin(), - directiveToken.bytes()); - if (directive == QLatin1String("include") || - directive == QLatin1String("include_next") || - directive == QLatin1String("import")) { - include = true; - } - } - } - - if (!include) { - *kind = T_EOF_SYMBOL; - start = pos; - } - } +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; } +} + +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); - return start; + 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); + m_functionName = functionInfo.functionName; + setActionAndClangPosition(PassThroughToLibClangAfterLeftParen, + functionInfo.functionNamePosition); + } + } } -void ClangCompletionContextAnalyzer::setActionAndClangPosition(CompletionAction action, - int position) +bool ClangCompletionContextAnalyzer::handleNonFunctionCall(int position) { - QTC_CHECK(position >= -1); - m_completionAction = action; - m_positionForClang = position; + 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; } } // namespace Internal |