2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2015-07-23 13:01:02 +02:00
|
|
|
|
2015-11-30 09:43:50 +01:00
|
|
|
#include "clangactivationsequencecontextprocessor.h"
|
2015-07-23 13:01:02 +02:00
|
|
|
|
2015-11-30 09:43:50 +01:00
|
|
|
#include "clangactivationsequenceprocessor.h"
|
2015-07-23 13:01:02 +02:00
|
|
|
|
|
|
|
|
#include <cplusplus/BackwardsScanner.h>
|
|
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
|
|
|
|
#include <cplusplus/SimpleLexer.h>
|
2021-06-18 16:30:03 +02:00
|
|
|
#include <utils/textutils.h>
|
2015-07-23 13:01:02 +02:00
|
|
|
|
|
|
|
|
#include <QTextDocument>
|
|
|
|
|
|
|
|
|
|
namespace ClangCodeModel {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2021-06-18 16:30:03 +02:00
|
|
|
ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
|
|
|
|
|
QTextDocument *document, int position, CPlusPlus::LanguageFeatures languageFeatures)
|
|
|
|
|
: m_textCursor(document),
|
|
|
|
|
m_document(document),
|
|
|
|
|
m_languageFeatures(languageFeatures),
|
|
|
|
|
m_positionInDocument(position),
|
2015-07-23 16:06:48 +02:00
|
|
|
m_startOfNamePosition(m_positionInDocument),
|
|
|
|
|
m_operatorStartPosition(m_positionInDocument)
|
2015-07-23 13:01:02 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
m_textCursor.setPosition(m_positionInDocument);
|
|
|
|
|
|
|
|
|
|
process();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const
|
|
|
|
|
{
|
|
|
|
|
return m_completionKind;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QTextCursor &ActivationSequenceContextProcessor::textCursor_forTestOnly() const
|
|
|
|
|
{
|
|
|
|
|
return m_textCursor;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
int ActivationSequenceContextProcessor::startOfNamePosition() const
|
2015-07-23 13:01:02 +02:00
|
|
|
{
|
2015-07-23 16:06:48 +02:00
|
|
|
return m_startOfNamePosition;
|
2015-07-23 13:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
int ActivationSequenceContextProcessor::operatorStartPosition() const
|
2015-07-23 13:01:02 +02:00
|
|
|
{
|
2015-07-23 16:06:48 +02:00
|
|
|
return m_operatorStartPosition;
|
2015-07-23 13:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::process()
|
|
|
|
|
{
|
2015-07-23 16:06:48 +02:00
|
|
|
goBackToStartOfName();
|
2015-07-23 13:01:02 +02:00
|
|
|
processActivationSequence();
|
|
|
|
|
|
|
|
|
|
if (m_completionKind != CPlusPlus::T_EOF_SYMBOL) {
|
|
|
|
|
processStringLiteral();
|
|
|
|
|
processComma();
|
|
|
|
|
generateTokens();
|
|
|
|
|
processDoxygenComment();
|
|
|
|
|
processComment();
|
|
|
|
|
processInclude();
|
|
|
|
|
processSlashOutsideOfAString();
|
2018-08-20 13:15:13 +02:00
|
|
|
processLeftParenOrBrace();
|
2015-07-23 13:01:02 +02:00
|
|
|
processPreprocessorInclude();
|
|
|
|
|
}
|
2015-07-23 16:06:48 +02:00
|
|
|
|
|
|
|
|
resetPositionsForEOFCompletionKind();
|
2015-07-23 13:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processActivationSequence()
|
|
|
|
|
{
|
2021-06-18 16:30:03 +02:00
|
|
|
const int nonSpacePosition = skipPrecedingWhitespace(m_document, m_startOfNamePosition);
|
|
|
|
|
const auto activationSequence = Utils::Text::textAt(QTextCursor(m_document),
|
|
|
|
|
nonSpacePosition - 3, 3);
|
2015-07-23 13:01:02 +02:00
|
|
|
ActivationSequenceProcessor activationSequenceProcessor(activationSequence,
|
2015-07-23 16:06:48 +02:00
|
|
|
nonSpacePosition,
|
2015-07-23 13:01:02 +02:00
|
|
|
true);
|
|
|
|
|
|
|
|
|
|
m_completionKind = activationSequenceProcessor.completionKind();
|
2015-07-23 16:06:48 +02:00
|
|
|
m_operatorStartPosition = activationSequenceProcessor.operatorStartPosition();
|
2015-07-23 13:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processStringLiteral()
|
|
|
|
|
{
|
|
|
|
|
if (m_completionKind == CPlusPlus::T_STRING_LITERAL) {
|
|
|
|
|
QTextCursor selectionTextCursor = m_textCursor;
|
|
|
|
|
selectionTextCursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
|
|
|
|
|
QString selection = selectionTextCursor.selectedText();
|
|
|
|
|
if (selection.indexOf(QLatin1Char('"')) < selection.length() - 1)
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processComma()
|
|
|
|
|
{
|
|
|
|
|
if (m_completionKind == CPlusPlus::T_COMMA) {
|
2021-06-18 16:30:03 +02:00
|
|
|
CPlusPlus::ExpressionUnderCursor expressionUnderCursor(m_languageFeatures);
|
2015-07-23 13:01:02 +02:00
|
|
|
if (expressionUnderCursor.startOfFunctionCall(m_textCursor) == -1)
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::generateTokens()
|
|
|
|
|
{
|
|
|
|
|
CPlusPlus::SimpleLexer tokenize;
|
2021-06-18 16:30:03 +02:00
|
|
|
tokenize.setLanguageFeatures(m_languageFeatures);
|
2015-07-23 13:01:02 +02:00
|
|
|
tokenize.setSkipComments(false);
|
|
|
|
|
auto state = CPlusPlus::BackwardsScanner::previousBlockState(m_textCursor.block());
|
|
|
|
|
m_tokens = tokenize(m_textCursor.block().text(), state);
|
|
|
|
|
int leftOfCursorTokenIndex = std::max(0, m_textCursor.positionInBlock() - 1);
|
|
|
|
|
m_tokenIndex= CPlusPlus::SimpleLexer::tokenBefore(m_tokens, leftOfCursorTokenIndex); // get the token at the left of the cursor
|
|
|
|
|
if (m_tokenIndex > -1)
|
|
|
|
|
m_token = m_tokens.at(m_tokenIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processDoxygenComment()
|
|
|
|
|
{
|
|
|
|
|
if (m_completionKind == CPlusPlus::T_DOXY_COMMENT
|
|
|
|
|
&& !(m_token.is(CPlusPlus::T_DOXY_COMMENT)
|
|
|
|
|
|| m_token.is(CPlusPlus::T_CPP_DOXY_COMMENT)))
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processComment()
|
|
|
|
|
{
|
|
|
|
|
if (m_token.is(CPlusPlus::T_COMMENT) || m_token.is(CPlusPlus::T_CPP_COMMENT))
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processInclude()
|
|
|
|
|
{
|
2024-04-15 17:28:56 +02:00
|
|
|
if (m_token.isStringLiteral() && !isCompletionKindStringLiteralOrSlash())
|
2015-07-23 13:01:02 +02:00
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processSlashOutsideOfAString()
|
|
|
|
|
{
|
|
|
|
|
if (m_completionKind ==CPlusPlus::T_SLASH
|
|
|
|
|
&& (m_token.isNot(CPlusPlus::T_STRING_LITERAL)
|
|
|
|
|
&& m_token.isNot(CPlusPlus::T_ANGLE_STRING_LITERAL)))
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 13:15:13 +02:00
|
|
|
void ActivationSequenceContextProcessor::processLeftParenOrBrace()
|
2015-07-23 13:01:02 +02:00
|
|
|
{
|
2018-08-20 13:15:13 +02:00
|
|
|
if (m_completionKind == CPlusPlus::T_LPAREN || m_completionKind == CPlusPlus::T_LBRACE) {
|
2015-07-23 13:01:02 +02:00
|
|
|
if (m_tokenIndex > 0) {
|
|
|
|
|
// look at the token at the left of T_LPAREN
|
|
|
|
|
const CPlusPlus::Token &previousToken = m_tokens.at(m_tokenIndex - 1);
|
|
|
|
|
switch (previousToken.kind()) {
|
|
|
|
|
case CPlusPlus::T_IDENTIFIER:
|
|
|
|
|
case CPlusPlus::T_GREATER:
|
|
|
|
|
case CPlusPlus::T_SIGNAL:
|
|
|
|
|
case CPlusPlus::T_SLOT:
|
|
|
|
|
break; // good
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// that's a bad token :)
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ActivationSequenceContextProcessor::isCompletionKindStringLiteralOrSlash() const
|
|
|
|
|
{
|
|
|
|
|
return m_completionKind == CPlusPlus::T_STRING_LITERAL
|
|
|
|
|
|| m_completionKind == CPlusPlus::T_ANGLE_STRING_LITERAL
|
|
|
|
|
|| m_completionKind == CPlusPlus::T_SLASH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ActivationSequenceContextProcessor::isProbablyPreprocessorIncludeDirective() const
|
|
|
|
|
{
|
|
|
|
|
return m_tokens.size() >= 3
|
|
|
|
|
&& m_tokens.at(0).is(CPlusPlus::T_POUND)
|
|
|
|
|
&& m_tokens.at(1).is(CPlusPlus::T_IDENTIFIER)
|
|
|
|
|
&& (m_tokens.at(2).is(CPlusPlus::T_STRING_LITERAL)
|
|
|
|
|
|| m_tokens.at(2).is(CPlusPlus::T_ANGLE_STRING_LITERAL));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivationSequenceContextProcessor::processPreprocessorInclude()
|
|
|
|
|
{
|
|
|
|
|
if (isCompletionKindStringLiteralOrSlash()) {
|
|
|
|
|
if (isProbablyPreprocessorIncludeDirective()) {
|
|
|
|
|
const CPlusPlus::Token &directiveToken = m_tokens.at(1);
|
|
|
|
|
QString directive = m_textCursor.block().text().mid(directiveToken.bytesBegin(),
|
|
|
|
|
directiveToken.bytes());
|
|
|
|
|
if (directive != QStringLiteral("include")
|
|
|
|
|
&& directive != QStringLiteral("include_next")
|
|
|
|
|
&& directive != QStringLiteral("import"))
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
} else {
|
|
|
|
|
m_completionKind = CPlusPlus::T_EOF_SYMBOL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
void ActivationSequenceContextProcessor::resetPositionsForEOFCompletionKind()
|
2015-07-23 13:01:02 +02:00
|
|
|
{
|
|
|
|
|
if (m_completionKind == CPlusPlus::T_EOF_SYMBOL)
|
2015-07-23 16:06:48 +02:00
|
|
|
m_operatorStartPosition = m_positionInDocument;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-18 16:30:03 +02:00
|
|
|
int ActivationSequenceContextProcessor::skipPrecedingWhitespace(const QTextDocument *document,
|
|
|
|
|
int startPosition)
|
2015-07-23 16:06:48 +02:00
|
|
|
{
|
|
|
|
|
int position = startPosition;
|
2021-06-18 16:30:03 +02:00
|
|
|
while (document->characterAt(position - 1).isSpace())
|
2015-07-23 16:06:48 +02:00
|
|
|
--position;
|
|
|
|
|
return position;
|
2015-07-23 13:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-24 15:35:20 +02:00
|
|
|
static bool isValidIdentifierChar(const QChar &character)
|
|
|
|
|
{
|
|
|
|
|
return character.isLetterOrNumber()
|
|
|
|
|
|| character == QLatin1Char('_')
|
|
|
|
|
|| character.isHighSurrogate()
|
|
|
|
|
|| character.isLowSurrogate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ActivationSequenceContextProcessor::findStartOfName(
|
2021-06-18 16:30:03 +02:00
|
|
|
const QTextDocument *document,
|
2017-10-17 12:48:07 +02:00
|
|
|
int startPosition,
|
|
|
|
|
NameCategory category)
|
2015-07-23 13:01:02 +02:00
|
|
|
{
|
2015-07-23 16:06:48 +02:00
|
|
|
int position = startPosition;
|
|
|
|
|
QChar character;
|
2017-10-17 12:48:07 +02:00
|
|
|
|
|
|
|
|
if (category == NameCategory::Function
|
2021-06-18 16:30:03 +02:00
|
|
|
&& position > 2 && document->characterAt(position - 1) == '>'
|
|
|
|
|
&& document->characterAt(position - 2) != '-') {
|
2017-08-10 09:57:18 +02:00
|
|
|
uint unbalancedLessGreater = 1;
|
|
|
|
|
--position;
|
|
|
|
|
while (unbalancedLessGreater > 0 && position > 2) {
|
2021-06-18 16:30:03 +02:00
|
|
|
character = document->characterAt(--position);
|
2017-08-10 09:57:18 +02:00
|
|
|
// Do not count -> usage inside temlate argument list
|
|
|
|
|
if (character == '<')
|
|
|
|
|
--unbalancedLessGreater;
|
2021-06-18 16:30:03 +02:00
|
|
|
else if (character == '>' && document->characterAt(position-1) != '-')
|
2017-08-10 09:57:18 +02:00
|
|
|
++unbalancedLessGreater;
|
|
|
|
|
}
|
2021-06-18 16:30:03 +02:00
|
|
|
position = skipPrecedingWhitespace(document, position) - 1;
|
2017-08-10 09:57:18 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
do {
|
2021-06-18 16:30:03 +02:00
|
|
|
character = document->characterAt(--position);
|
2015-07-24 15:35:20 +02:00
|
|
|
} while (isValidIdentifierChar(character));
|
2015-07-23 13:01:02 +02:00
|
|
|
|
2021-06-18 16:30:03 +02:00
|
|
|
int prevPosition = skipPrecedingWhitespace(document, position);
|
2017-10-17 12:48:07 +02:00
|
|
|
if (category == NameCategory::Function
|
2021-06-18 16:30:03 +02:00
|
|
|
&& document->characterAt(prevPosition) == ':'
|
|
|
|
|
&& document->characterAt(prevPosition - 1) == ':') {
|
2017-08-10 09:57:18 +02:00
|
|
|
// Handle :: case - go recursive
|
2021-06-18 16:30:03 +02:00
|
|
|
prevPosition = skipPrecedingWhitespace(document, prevPosition - 2);
|
|
|
|
|
return findStartOfName(document, prevPosition + 1, category);
|
2017-08-10 09:57:18 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
return position + 1;
|
|
|
|
|
}
|
2015-07-23 13:01:02 +02:00
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
void ActivationSequenceContextProcessor::goBackToStartOfName()
|
|
|
|
|
{
|
2021-01-07 12:44:05 +01:00
|
|
|
CPlusPlus::SimpleLexer tokenize;
|
2021-06-18 16:30:03 +02:00
|
|
|
tokenize.setLanguageFeatures(m_languageFeatures);
|
2021-01-07 12:44:05 +01:00
|
|
|
tokenize.setSkipComments(false);
|
|
|
|
|
const int state = CPlusPlus::BackwardsScanner::previousBlockState(m_textCursor.block());
|
|
|
|
|
const CPlusPlus::Tokens tokens = tokenize(m_textCursor.block().text(), state);
|
|
|
|
|
const int tokenPos = std::max(0, m_textCursor.positionInBlock() - 1);
|
|
|
|
|
const int tokIndex = CPlusPlus::SimpleLexer::tokenAt(tokens, tokenPos);
|
|
|
|
|
if (tokIndex > -1 && tokens.at(tokIndex).isStringLiteral()) {
|
|
|
|
|
const int tokenStart = tokens.at(tokIndex).utf16charOffset;
|
2021-01-21 13:26:26 +01:00
|
|
|
const int slashIndex = m_textCursor.block().text().lastIndexOf(
|
|
|
|
|
'/',
|
|
|
|
|
std::min(m_textCursor.positionInBlock(), int(m_textCursor.block().text().length() - 1)));
|
2021-01-07 12:44:05 +01:00
|
|
|
m_startOfNamePosition = m_textCursor.block().position() + std::max(slashIndex, tokenStart)
|
|
|
|
|
+ 1;
|
|
|
|
|
} else {
|
2021-06-18 16:30:03 +02:00
|
|
|
m_startOfNamePosition = findStartOfName(m_document, m_positionInDocument);
|
2021-01-07 12:44:05 +01:00
|
|
|
}
|
2015-07-23 13:01:02 +02:00
|
|
|
|
2015-07-23 16:06:48 +02:00
|
|
|
if (m_startOfNamePosition != m_positionInDocument)
|
|
|
|
|
m_textCursor.setPosition(m_startOfNamePosition);
|
2015-07-23 13:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangCodeModel
|
|
|
|
|
|