summaryrefslogtreecommitdiff
path: root/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp
diff options
context:
space:
mode:
authorMarco Bubke <marco.bubke@theqtcompany.com>2015-07-23 13:01:02 +0200
committerNikolai Kosjar <nikolai.kosjar@theqtcompany.com>2015-07-23 11:13:05 +0000
commitae5d92d6182ecdf64b3d5453ebcff9fc0c589016 (patch)
treebb6d233102ca890ff5afa6ce3daec9c962cc26ec /src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp
parent5a791e88394513a7fd632263f95b632dd84e1cfd (diff)
downloadqt-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.cpp301
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