Commit 91429aa7 authored by Nikolai Kosjar's avatar Nikolai Kosjar

Clang: Fix completion position for clang and proposal

* Rename some members/functions to clarify their meaning.

* Ensure that the position for the proposal widget is at start of the
  identifer, so that the filter prefix will be found correctly in the
  GenericProposalWidget. For certain cases the completion were
  calculated but the widget was never shown:

  Case 1:

    void f()
    {
        <COMPLETION_CURSOR>
    }

  Case 2:

    void f()
    {
        st<COMPLETION_CURSOR>
    }

  Case 3:

    if (true)
      <COMPLETION_CURSOR>

  Case 4:

    foo. mem<COMPLETION_CURSOR>

Change-Id: Ie79e01e8a22f8ec306136ec4ccbfffd544edd573
Reviewed-by: default avatarErik Verbruggen <erik.verbruggen@theqtcompany.com>
parent 49b1d357
......@@ -46,8 +46,8 @@ ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(const Cla
: m_textCursor(assistInterface->textDocument()),
m_assistInterface(assistInterface),
m_positionInDocument(assistInterface->position()),
m_positionAfterOperator(m_positionInDocument),
m_positionBeforeOperator(m_positionAfterOperator)
m_startOfNamePosition(m_positionInDocument),
m_operatorStartPosition(m_positionInDocument)
{
m_textCursor.setPosition(m_positionInDocument);
......@@ -65,19 +65,19 @@ const QTextCursor &ActivationSequenceContextProcessor::textCursor_forTestOnly()
return m_textCursor;
}
int ActivationSequenceContextProcessor::positionAfterOperator() const
int ActivationSequenceContextProcessor::startOfNamePosition() const
{
return m_positionAfterOperator;
return m_startOfNamePosition;
}
int ActivationSequenceContextProcessor::positionBeforeOperator() const
int ActivationSequenceContextProcessor::operatorStartPosition() const
{
return m_positionBeforeOperator;
return m_operatorStartPosition;
}
void ActivationSequenceContextProcessor::process()
{
skipeWhiteSpacesAndIdentifierBeforeCursor();
goBackToStartOfName();
processActivationSequence();
if (m_completionKind != CPlusPlus::T_EOF_SYMBOL) {
......@@ -90,20 +90,21 @@ void ActivationSequenceContextProcessor::process()
processSlashOutsideOfAString();
processLeftParen();
processPreprocessorInclude();
resetPositionForEOFCompletionKind();
}
resetPositionsForEOFCompletionKind();
}
void ActivationSequenceContextProcessor::processActivationSequence()
{
const auto activationSequence = m_assistInterface->textAt(m_positionInDocument - 3, 3);
const int nonSpacePosition = findStartOfNonSpaceChar(m_startOfNamePosition);
const auto activationSequence = m_assistInterface->textAt(nonSpacePosition - 3, 3);
ActivationSequenceProcessor activationSequenceProcessor(activationSequence,
m_positionInDocument,
nonSpacePosition,
true);
m_completionKind = activationSequenceProcessor.completionKind();
m_positionBeforeOperator = activationSequenceProcessor.position();
m_operatorStartPosition = activationSequenceProcessor.operatorStartPosition();
}
void ActivationSequenceContextProcessor::processStringLiteral()
......@@ -221,27 +222,37 @@ void ActivationSequenceContextProcessor::processPreprocessorInclude()
}
}
void ActivationSequenceContextProcessor::resetPositionForEOFCompletionKind()
void ActivationSequenceContextProcessor::resetPositionsForEOFCompletionKind()
{
if (m_completionKind == CPlusPlus::T_EOF_SYMBOL)
m_positionBeforeOperator = m_positionInDocument;
m_operatorStartPosition = m_positionInDocument;
}
int ActivationSequenceContextProcessor::findStartOfNonSpaceChar(int startPosition)
{
int position = startPosition;
while (m_assistInterface->characterAt(position - 1).isSpace())
--position;
return position;
}
void ActivationSequenceContextProcessor::skipeWhiteSpacesAndIdentifierBeforeCursor()
int ActivationSequenceContextProcessor::findStartOfName(int startPosition)
{
QTextDocument *document = m_assistInterface->textDocument();
int position = startPosition;
QChar character;
do {
character = m_assistInterface->characterAt(--position);
} while (character.isLetterOrNumber() || character == QLatin1Char('_'));
const QRegExp findNonWhiteSpaceRegularExpression(QStringLiteral("[^\\s\\w]"));
return position + 1;
}
auto nonWhiteSpaceTextCursor = document->find(findNonWhiteSpaceRegularExpression,
m_positionInDocument,
QTextDocument::FindBackward);
void ActivationSequenceContextProcessor::goBackToStartOfName()
{
m_startOfNamePosition = findStartOfName(m_positionInDocument);
if (!nonWhiteSpaceTextCursor.isNull()) {
m_positionInDocument = nonWhiteSpaceTextCursor.position();
m_positionAfterOperator = m_positionInDocument;
m_textCursor.setPosition(m_positionInDocument);
}
if (m_startOfNamePosition != m_positionInDocument)
m_textCursor.setPosition(m_startOfNamePosition);
}
} // namespace Internal
......
......@@ -50,15 +50,16 @@ public:
ActivationSequenceContextProcessor(const ClangCompletionAssistInterface *assistInterface);
CPlusPlus::Kind completionKind() const;
int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>"
int operatorStartPosition() const; // e.g. points to '.' for "foo.bar<CURSOR>"
const QTextCursor &textCursor_forTestOnly() const;
int positionAfterOperator() const;
int positionBeforeOperator() const;
protected:
void process();
void skipeWhiteSpacesAndIdentifierBeforeCursor();
int findStartOfNonSpaceChar(int startPosition);
int findStartOfName(int startPosition);
void goBackToStartOfName();
void processActivationSequence();
void processStringLiteral();
void processComma();
......@@ -69,7 +70,7 @@ protected:
void processSlashOutsideOfAString();
void processLeftParen();
void processPreprocessorInclude();
void resetPositionForEOFCompletionKind();
void resetPositionsForEOFCompletionKind();
bool isCompletionKindStringLiteralOrSlash() const;
bool isProbablyPreprocessorIncludeDirective() const;
......@@ -80,9 +81,9 @@ private:
CPlusPlus::Token m_token;
const ClangCompletionAssistInterface *m_assistInterface;
int m_tokenIndex;
int m_positionInDocument;
int m_positionAfterOperator;
int m_positionBeforeOperator;
const int m_positionInDocument;
int m_startOfNamePosition;
int m_operatorStartPosition;
CPlusPlus::Kind m_completionKind;
};
......
......@@ -71,7 +71,7 @@ int ActivationSequenceProcessor::offset() const
return m_offset;
}
int ActivationSequenceProcessor::position() const
int ActivationSequenceProcessor::operatorStartPosition() const
{
return m_positionInDocument - m_offset;
}
......
......@@ -47,7 +47,7 @@ public:
CPlusPlus::Kind completionKind() const;
int offset() const;
int position() const;
int operatorStartPosition() const; // e.g. points to '.' for "foo.bar<CURSOR>"
private:
void extractCharactersBeforePosition(const QString &activationString);
......
......@@ -363,7 +363,7 @@ int ClangCompletionAssistProcessor::startOfOperator(int positionInDocument,
*kind = activationSequenceProcessor.completionKind();
int start = activationSequenceProcessor.position();
int start = activationSequenceProcessor.operatorStartPosition();
if (start != positionInDocument) {
QTextCursor tc(m_interface->textDocument());
tc.setPosition(positionInDocument);
......
......@@ -86,9 +86,9 @@ void ClangCompletionContextAnalyzer::analyze()
ActivationSequenceContextProcessor activationSequenceContextProcessor(m_interface);
m_completionOperator = activationSequenceContextProcessor.completionKind();
int afterOperatorPosition = activationSequenceContextProcessor.positionAfterOperator();
m_positionEndOfExpression = activationSequenceContextProcessor.positionBeforeOperator();
m_positionForProposal = activationSequenceContextProcessor.positionAfterOperator();
int afterOperatorPosition = activationSequenceContextProcessor.startOfNamePosition();
m_positionEndOfExpression = activationSequenceContextProcessor.operatorStartPosition();
m_positionForProposal = activationSequenceContextProcessor.startOfNamePosition();
const bool actionIsSet = handleNonFunctionCall(afterOperatorPosition);
if (!actionIsSet) {
......
......@@ -54,7 +54,7 @@ TEST(ActivationSequeneContextProcessor, TextCursorPosition)
ClangCompletionAssistInterface interface("foobar", 4);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.textCursor_forTestOnly().position(), 4);
ASSERT_THAT(processor.textCursor_forTestOnly().position(), 0);
}
TEST(ActivationSequeneContextProcessor, StringLiteral)
......
......@@ -47,14 +47,14 @@ MATCHER_P3(HasResult, completionKind, offset, newPosition,
std::string(negation ? "hasn't" : "has")
+ " result of completion kind " + PrintToString(Token::name(completionKind))
+ ", offset " + PrintToString(offset)
+ " and new position in document" + PrintToString(newPosition))
+ " and new operator start position" + PrintToString(newPosition))
{
if (arg.completionKind() != completionKind
|| arg.offset() != offset
|| arg.position() != newPosition) {
|| arg.operatorStartPosition() != newPosition) {
*result_listener << "completion kind is " << PrintToString(Token::name(arg.completionKind()))
<< ", offset is " << PrintToString(arg.offset())
<< " and new position in document is " << PrintToString(arg.position());
<< " and new operator start position is " << PrintToString(arg.operatorStartPosition());
return false;
}
......
......@@ -172,6 +172,20 @@ CCA ClangCompletionContextAnalyzer::runAnalyzer(const char *text)
return analyzer;
}
TEST_F(ClangCompletionContextAnalyzer, WordsBeforeCursor)
{
auto analyzer = runAnalyzer("foo bar@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AfterSpace)
{
auto analyzer = runAnalyzer("foo @");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfDotMember)
{
auto analyzer = runAnalyzer("o.mem@");
......@@ -183,7 +197,7 @@ TEST_F(ClangCompletionContextAnalyzer, AtEndOfDotMemberWithSpaceInside)
{
auto analyzer = runAnalyzer("o. mem@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -4, -4, positionInText));
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AtBeginOfDotMember)
......@@ -197,7 +211,7 @@ TEST_F(ClangCompletionContextAnalyzer, AtBeginOfDotMemberWithSpaceInside)
{
auto analyzer = runAnalyzer("o. @mem");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -1, -1, positionInText));
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfArrow)
......@@ -211,7 +225,7 @@ TEST_F(ClangCompletionContextAnalyzer, AtEndOfArrowWithSpaceInside)
{
auto analyzer = runAnalyzer("o-> mem@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -4, -4, positionInText));
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AtBeginOfArrow)
......@@ -225,7 +239,7 @@ TEST_F(ClangCompletionContextAnalyzer, AtBeginOfArrowWithSpaceInside)
{
auto analyzer = runAnalyzer("o-> @mem");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -1, -1, positionInText));
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, ParameteOneAtCall)
......@@ -246,7 +260,7 @@ TEST_F(ClangCompletionContextAnalyzer, ParameteTwoWithSpaceAtCall)
{
auto analyzer = runAnalyzer("f(1, @");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -1, -1, positionInText));
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, -5, -3, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, ParameteOneAtSignal)
......@@ -396,13 +410,45 @@ TEST_F(ClangCompletionContextAnalyzer, DoxygenMarkerInNonDoxygenComment2)
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, OneLineComment)
TEST_F(ClangCompletionContextAnalyzer, AtEndOfOneLineComment)
{
auto analyzer = runAnalyzer("// text@");
auto analyzer = runAnalyzer("// comment@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, AfterOneLineCommentLine)
{
auto analyzer = runAnalyzer("// comment\n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AfterEmptyOneLineComment)
{
auto analyzer = runAnalyzer("//\n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AfterOneLineDoxygenComment1)
{
auto analyzer = runAnalyzer("/// comment\n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, AfterOneLineDoxygenComment2)
{
auto analyzer = runAnalyzer("//! comment \n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText));
}
TEST_F(ClangCompletionContextAnalyzer, BeginEndComment)
{
auto analyzer = runAnalyzer("/* text@ */");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment