ClangCodeModel: Remove libclang-based completion and function hints

Change-Id: I742fb14b1aba3ba1f35a5c80bf553d2a735cac48
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2022-04-27 16:02:08 +02:00
parent efd25fb43f
commit 6da7babc4a
41 changed files with 115 additions and 5048 deletions

View File

@@ -11,17 +11,11 @@ add_qtc_plugin(ClangCodeModel
SOURCES SOURCES
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
clangassistproposalitem.cpp clangassistproposalitem.h
clangassistproposalmodel.cpp clangassistproposalmodel.h
clangbackendcommunicator.cpp clangbackendcommunicator.h clangbackendcommunicator.cpp clangbackendcommunicator.h
clangbackendlogging.cpp clangbackendlogging.h clangbackendlogging.cpp clangbackendlogging.h
clangbackendreceiver.cpp clangbackendreceiver.h clangbackendreceiver.cpp clangbackendreceiver.h
clangbackendsender.cpp clangbackendsender.h clangbackendsender.cpp clangbackendsender.h
clangcodemodelplugin.cpp clangcodemodelplugin.h clangcodemodelplugin.cpp clangcodemodelplugin.h
clangcompletionassistinterface.cpp clangcompletionassistinterface.h
clangcompletionassistprocessor.cpp clangcompletionassistprocessor.h
clangcompletionassistprovider.cpp clangcompletionassistprovider.h
clangcompletionchunkstotextconverter.cpp clangcompletionchunkstotextconverter.h
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
clangconstants.h clangconstants.h
clangdclient.cpp clangdclient.h clangdclient.cpp clangdclient.h
@@ -32,7 +26,6 @@ add_qtc_plugin(ClangCodeModel
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
clangfixitoperation.cpp clangfixitoperation.h clangfixitoperation.cpp clangfixitoperation.h
clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h
clangfunctionhintmodel.cpp clangfunctionhintmodel.h
clangdlocatorfilters.cpp clangdlocatorfilters.h clangdlocatorfilters.cpp clangdlocatorfilters.h
clangmodelmanagersupport.cpp clangmodelmanagersupport.h clangmodelmanagersupport.cpp clangmodelmanagersupport.h
clangpreprocessorassistproposalitem.cpp clangpreprocessorassistproposalitem.h clangpreprocessorassistproposalitem.cpp clangpreprocessorassistproposalitem.h
@@ -57,9 +50,7 @@ extend_qtc_plugin(ClangCodeModel
extend_qtc_plugin(ClangCodeModel extend_qtc_plugin(ClangCodeModel
CONDITION WITH_TESTS CONDITION WITH_TESTS
SOURCES SOURCES
test/clangautomationutils.cpp test/clangautomationutils.h
test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h
test/clangcodecompletion_test.cpp test/clangcodecompletion_test.h
test/clangdtests.cpp test/clangdtests.h test/clangdtests.cpp test/clangdtests.h
test/data/clangtestdata.qrc test/data/clangtestdata.qrc
) )

View File

@@ -52,13 +52,6 @@ ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
process(); process();
} }
ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
const ClangCompletionAssistInterface *interface)
: ActivationSequenceContextProcessor(interface->textDocument(), interface->position(),
interface->languageFeatures())
{
}
CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const
{ {
return m_completionKind; return m_completionKind;

View File

@@ -25,8 +25,6 @@
#pragma once #pragma once
#include <clangcodemodel/clangcompletionassistinterface.h>
#include <cplusplus/Token.h> #include <cplusplus/Token.h>
#include <QTextCursor> #include <QTextCursor>
@@ -43,7 +41,6 @@ class ActivationSequenceContextProcessor
public: public:
ActivationSequenceContextProcessor(QTextDocument *document, int position, ActivationSequenceContextProcessor(QTextDocument *document, int position,
CPlusPlus::LanguageFeatures languageFeatures); CPlusPlus::LanguageFeatures languageFeatures);
ActivationSequenceContextProcessor(const ClangCompletionAssistInterface *interface);
CPlusPlus::Kind completionKind() const; CPlusPlus::Kind completionKind() const;
int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>" int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>"

View File

@@ -1,536 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangassistproposalitem.h"
#include "clangcompletionchunkstotextconverter.h"
#include "clangfixitoperation.h"
#include "clangutils.h"
#include <cplusplus/Icons.h>
#include <cplusplus/MatchingText.h>
#include <cplusplus/SimpleLexer.h>
#include <cplusplus/Token.h>
#include <texteditor/completionsettings.h>
#include <texteditor/texteditor.h>
#include <texteditor/texteditorsettings.h>
#include <QCoreApplication>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextDocument>
#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <utils/qtcassert.h>
using namespace CPlusPlus;
using namespace ClangBackEnd;
using namespace TextEditor;
using namespace Utils;
namespace ClangCodeModel {
namespace Internal {
bool ClangAssistProposalItem::prematurelyApplies(const QChar &typedCharacter) const
{
bool applies = false;
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)
applies = QString::fromLatin1("(,").contains(typedCharacter);
else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
applies = (typedCharacter == QLatin1Char('/')) && text().endsWith(QLatin1Char('/'));
else if (firstCodeCompletion().completionKind == CodeCompletion::ObjCMessageCompletionKind)
applies = QString::fromLatin1(";.,").contains(typedCharacter);
else
applies = QString::fromLatin1(";.,:(").contains(typedCharacter);
if (applies)
m_typedCharacter = typedCharacter;
return applies;
}
bool ClangAssistProposalItem::implicitlyApplies() const
{
return true;
}
static QString methodDefinitionParameters(const CodeCompletionChunks &chunks)
{
QString result;
auto typedTextChunkIt = std::find_if(chunks.cbegin(), chunks.cend(),
[](const CodeCompletionChunk &chunk) {
return chunk.kind == CodeCompletionChunk::TypedText;
});
if (typedTextChunkIt == chunks.cend())
return result;
std::for_each(++typedTextChunkIt, chunks.cend(), [&result](const CodeCompletionChunk &chunk) {
if (chunk.kind == CodeCompletionChunk::Placeholder && chunk.text.contains('=')) {
Utf8String text = chunk.text.mid(0, chunk.text.indexOf('='));
if (text.endsWith(' '))
text.chop(1);
result += text;
} else {
result += chunk.text;
}
});
return result;
}
static bool skipParenForFunctionLikeSnippet(const std::vector<int> &placeholderPositions,
const QString &text,
int position)
{
return placeholderPositions.size() == 1
&& position > 0
&& text[position - 1] == '('
&& text[position] == ')'
&& position + 1 == text.size();
}
static bool isFuncDeclAsSingleTypedText(const CodeCompletion &completion)
{
// There is no libclang API to tell function call items from declaration items apart.
// However, the chunks differ for these items (c-index-test -code-completion-at=...):
// An (override) declaration (available in derived class scope):
// CXXMethod:{TypedText void hello() override} (40)
// A function call:
// CXXMethod:{ResultType void}{TypedText hello}{LeftParen (}{RightParen )} (36)
return completion.completionKind == CodeCompletion::FunctionDefinitionCompletionKind
&& completion.chunks.size() == 1
&& completion.chunks[0].kind == CodeCompletionChunk::TypedText;
}
void ClangAssistProposalItem::apply(TextDocumentManipulatorInterface &manipulator,
int basePosition) const
{
const CodeCompletion ccr = firstCodeCompletion();
if (!ccr.requiredFixIts.empty()) {
// Important: Calculate shift before changing the document.
basePosition += fixItsShift(manipulator);
ClangFixItOperation fixItOperation(Utf8String(), ccr.requiredFixIts);
fixItOperation.perform();
}
QString textToBeInserted = m_text;
QString extraCharacters;
int extraLength = 0;
int cursorOffset = 0;
bool setAutoCompleteSkipPos = false;
int currentPosition = manipulator.currentPosition();
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
extraCharacters += QLatin1Char(')');
if (m_typedCharacter == QLatin1Char('(')) // Eat the opening parenthesis
m_typedCharacter = QChar();
} else if (ccr.completionKind == CodeCompletion::KeywordCompletionKind) {
CompletionChunksToTextConverter converter;
converter.setupForKeywords();
converter.parseChunks(ccr.chunks);
textToBeInserted = converter.text();
if (converter.hasPlaceholderPositions()) {
const std::vector<int> &placeholderPositions = converter.placeholderPositions();
const int position = placeholderPositions[0];
cursorOffset = position - converter.text().size();
// If the snippet looks like a function call, e.g. "sizeof(<PLACEHOLDER>)",
// ensure that we can "overtype" ')' after inserting it.
setAutoCompleteSkipPos = skipParenForFunctionLikeSnippet(placeholderPositions,
textToBeInserted,
position);
}
} else if (ccr.completionKind == CodeCompletion::NamespaceCompletionKind) {
CompletionChunksToTextConverter converter;
converter.parseChunks(ccr.chunks); // Appends "::" after name space name
textToBeInserted = converter.text();
// Clang does not provide the "::" chunk consistently for namespaces, e.g.
//
// namespace a { namespace b { namespace c {} } }
// <CURSOR> // complete "a" ==> "a::"
// a::<CURSOR> // complete "b" ==> "b", not "b::"
//
// Remove "::" to avoid any confusion for now.
if (textToBeInserted.endsWith("::"))
textToBeInserted.chop(2);
} else if (!ccr.text.isEmpty()) {
const CompletionSettings &completionSettings =
TextEditorSettings::completionSettings();
const bool autoInsertBrackets = completionSettings.m_autoInsertBrackets;
if (autoInsertBrackets &&
(ccr.completionKind == CodeCompletion::FunctionCompletionKind
|| ccr.completionKind == CodeCompletion::FunctionDefinitionCompletionKind
|| ccr.completionKind == CodeCompletion::DestructorCompletionKind
|| ccr.completionKind == CodeCompletion::ConstructorCompletionKind
|| ccr.completionKind == CodeCompletion::SignalCompletionKind
|| ccr.completionKind == CodeCompletion::SlotCompletionKind)) {
// When the user typed the opening parenthesis, he'll likely also type the closing one,
// in which case it would be annoying if we put the cursor after the already automatically
// inserted closing parenthesis.
const bool skipClosingParenthesis = m_typedCharacter != QLatin1Char('(');
QTextCursor cursor = manipulator.textCursorAt(basePosition);
bool abandonParen = false;
if (matchPreviousWord(manipulator, cursor, "&")) {
moveToPreviousWord(manipulator, cursor);
moveToPreviousChar(manipulator, cursor);
const QChar prevChar = manipulator.characterAt(cursor.position());
cursor.setPosition(basePosition);
abandonParen = QString("(;,{}").contains(prevChar);
}
if (!abandonParen) {
const bool isFullDecl = isFuncDeclAsSingleTypedText(ccr);
if (isFullDecl)
extraCharacters += QLatin1Char(';');
abandonParen = isAtUsingDeclaration(manipulator, basePosition) || isFullDecl;
}
if (!abandonParen && ccr.completionKind == CodeCompletion::FunctionDefinitionCompletionKind) {
const CodeCompletionChunk resultType = ccr.chunks.first();
if (resultType.kind == CodeCompletionChunk::ResultType) {
if (matchPreviousWord(manipulator, cursor, resultType.text.toString())) {
extraCharacters += methodDefinitionParameters(ccr.chunks);
// To skip the next block.
abandonParen = true;
}
} else {
// Do nothing becasue it's not a function definition.
// It's a clang bug that the function might miss a ResultType chunk
// when the base class method is called from the overriding method
// of the derived class. For example:
// void Derived::foo() override { Base::<complete here> }
}
}
if (!abandonParen) {
if (completionSettings.m_spaceAfterFunctionName)
extraCharacters += QLatin1Char(' ');
extraCharacters += QLatin1Char('(');
if (m_typedCharacter == QLatin1Char('('))
m_typedCharacter = QChar();
// If the function doesn't return anything, automatically place the semicolon,
// unless we're doing a scope completion (then it might be function definition).
const QChar characterAtCursor = manipulator.characterAt(currentPosition);
bool endWithSemicolon = m_typedCharacter == QLatin1Char(';')/*
|| (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON)*/; //###
const QChar semicolon = m_typedCharacter.isNull() ? QLatin1Char(';') : m_typedCharacter;
if (endWithSemicolon && characterAtCursor == semicolon) {
endWithSemicolon = false;
m_typedCharacter = QChar();
}
// If the function takes no arguments, automatically place the closing parenthesis
if (!hasOverloadsWithParameters() && !ccr.hasParameters && skipClosingParenthesis) {
extraCharacters += QLatin1Char(')');
if (endWithSemicolon) {
extraCharacters += semicolon;
m_typedCharacter = QChar();
}
} else {
const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraCharacters += QLatin1Char(')');
--cursorOffset;
setAutoCompleteSkipPos = true;
if (endWithSemicolon) {
extraCharacters += semicolon;
--cursorOffset;
m_typedCharacter = QChar();
}
}
}
}
}
}
// Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
if (!m_typedCharacter.isNull()) {
extraCharacters += m_typedCharacter;
if (cursorOffset != 0)
--cursorOffset;
}
// Avoid inserting characters that are already there
QTextCursor cursor = manipulator.textCursorAt(basePosition);
cursor.movePosition(QTextCursor::EndOfWord);
const QString textAfterCursor = manipulator.textAt(currentPosition,
cursor.position() - currentPosition);
if (textToBeInserted != textAfterCursor
&& textToBeInserted.indexOf(textAfterCursor, currentPosition - basePosition) >= 0) {
currentPosition = cursor.position();
}
for (int i = 0; i < extraCharacters.length(); ++i) {
const QChar a = extraCharacters.at(i);
const QChar b = manipulator.characterAt(currentPosition + i);
if (a == b)
++extraLength;
else
break;
}
textToBeInserted += extraCharacters;
const int length = currentPosition - basePosition + extraLength;
const bool isReplaced = manipulator.replace(basePosition, length, textToBeInserted);
manipulator.setCursorPosition(basePosition + textToBeInserted.length());
if (isReplaced) {
if (cursorOffset)
manipulator.setCursorPosition(manipulator.currentPosition() + cursorOffset);
if (setAutoCompleteSkipPos)
manipulator.setAutoCompleteSkipPosition(manipulator.currentPosition());
if (ccr.completionKind == CodeCompletion::KeywordCompletionKind)
manipulator.autoIndent(basePosition, textToBeInserted.size());
}
}
void ClangAssistProposalItem::setText(const QString &text)
{
m_text = text;
}
QString ClangAssistProposalItem::text() const
{
return m_text;
}
const QVector<ClangBackEnd::FixItContainer> &ClangAssistProposalItem::firstCompletionFixIts() const
{
return firstCodeCompletion().requiredFixIts;
}
std::pair<int, int> fixItPositionsRange(const FixItContainer &fixIt, const QTextCursor &cursor)
{
const QTextBlock startLine = cursor.document()->findBlockByNumber(fixIt.range.start.line - 1);
const QTextBlock endLine = cursor.document()->findBlockByNumber(fixIt.range.end.line - 1);
const int fixItStartPos = Text::positionInText(
cursor.document(),
fixIt.range.start.line,
cppEditorColumn(startLine, fixIt.range.start.column));
const int fixItEndPos = Text::positionInText(
cursor.document(),
fixIt.range.end.line,
cppEditorColumn(endLine, fixIt.range.end.column));
return std::make_pair(fixItStartPos, fixItEndPos);
}
static QString textReplacedByFixit(const FixItContainer &fixIt)
{
TextEditorWidget *textEditorWidget = TextEditorWidget::currentTextEditorWidget();
if (!textEditorWidget)
return QString();
const std::pair<int, int> fixItPosRange = fixItPositionsRange(fixIt,
textEditorWidget->textCursor());
return textEditorWidget->textAt(fixItPosRange.first,
fixItPosRange.second - fixItPosRange.first);
}
QString ClangAssistProposalItem::fixItText() const
{
const FixItContainer &fixIt = firstCompletionFixIts().first();
return QCoreApplication::translate("ClangCodeModel::ClangAssistProposalItem",
"Requires changing \"%1\" to \"%2\"")
.arg(textReplacedByFixit(fixIt), fixIt.text.toString());
}
int ClangAssistProposalItem::fixItsShift(const TextDocumentManipulatorInterface &manipulator) const
{
const QVector<ClangBackEnd::FixItContainer> &requiredFixIts = firstCompletionFixIts();
if (requiredFixIts.empty())
return 0;
int shift = 0;
const QTextCursor cursor = manipulator.textCursorAt(0);
for (const FixItContainer &fixIt : requiredFixIts) {
const std::pair<int, int> fixItPosRange = fixItPositionsRange(fixIt, cursor);
shift += fixIt.text.toString().length() - (fixItPosRange.second - fixItPosRange.first);
}
return shift;
}
QIcon ClangAssistProposalItem::icon() const
{
using namespace CPlusPlus::Icons;
static const char SNIPPET_ICON_PATH[] = ":/texteditor/images/snippet.png";
static const QIcon snippetIcon = QIcon(QLatin1String(SNIPPET_ICON_PATH));
const ClangBackEnd::CodeCompletion &completion = firstCodeCompletion();
switch (completion.completionKind) {
case CodeCompletion::ClassCompletionKind:
case CodeCompletion::TemplateClassCompletionKind:
case CodeCompletion::TypeAliasCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Class);
case CodeCompletion::EnumerationCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Enum);
case CodeCompletion::EnumeratorCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Enumerator);
case CodeCompletion::ConstructorCompletionKind:
case CodeCompletion::DestructorCompletionKind:
case CodeCompletion::FunctionCompletionKind:
case CodeCompletion::FunctionDefinitionCompletionKind:
case CodeCompletion::TemplateFunctionCompletionKind:
case CodeCompletion::ObjCMessageCompletionKind:
switch (completion.availability) {
case CodeCompletion::Available:
case CodeCompletion::Deprecated:
return CodeModelIcon::iconForType(CodeModelIcon::FuncPublic);
default:
return CodeModelIcon::iconForType(CodeModelIcon::FuncPrivate);
}
case CodeCompletion::SignalCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Signal);
case CodeCompletion::SlotCompletionKind:
switch (completion.availability) {
case CodeCompletion::Available:
case CodeCompletion::Deprecated:
return CodeModelIcon::iconForType(CodeModelIcon::SlotPublic);
case CodeCompletion::NotAccessible:
case CodeCompletion::NotAvailable:
return CodeModelIcon::iconForType(CodeModelIcon::SlotPrivate);
}
break;
case CodeCompletion::NamespaceCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Namespace);
case CodeCompletion::PreProcessorCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Macro);
case CodeCompletion::VariableCompletionKind:
switch (completion.availability) {
case CodeCompletion::Available:
case CodeCompletion::Deprecated:
return CodeModelIcon::iconForType(CodeModelIcon::VarPublic);
default:
return CodeModelIcon::iconForType(CodeModelIcon::VarPrivate);
}
case CodeCompletion::KeywordCompletionKind:
return CodeModelIcon::iconForType(CodeModelIcon::Keyword);
case CodeCompletion::ClangSnippetKind:
return snippetIcon;
case CodeCompletion::Other:
return CodeModelIcon::iconForType(CodeModelIcon::Unknown);
default:
break;
}
return QIcon();
}
QString ClangAssistProposalItem::detail() const
{
QString detail;
for (const ClangBackEnd::CodeCompletion &codeCompletion : m_codeCompletions) {
if (!detail.isEmpty())
detail += "<br>";
detail += CompletionChunksToTextConverter::convertToToolTipWithHtml(
codeCompletion.chunks, codeCompletion.completionKind);
if (!codeCompletion.briefComment.isEmpty())
detail += "<br>" + codeCompletion.briefComment.toString();
}
if (requiresFixIts())
detail += "<br><br><b>" + fixItText() + "</b>";
return detail;
}
bool ClangAssistProposalItem::isKeyword() const
{
// KeywordCompletionKind includes real keywords but also "code patterns"/snippets.
return m_codeCompletions[0].completionKind == CodeCompletion::KeywordCompletionKind;
}
Qt::TextFormat ClangAssistProposalItem::detailFormat() const
{
return Qt::RichText;
}
bool ClangAssistProposalItem::isSnippet() const
{
return false;
}
bool ClangAssistProposalItem::isValid() const
{
return true;
}
quint64 ClangAssistProposalItem::hash() const
{
return 0;
}
bool ClangAssistProposalItem::requiresFixIts() const
{
return !firstCompletionFixIts().empty();
}
bool ClangAssistProposalItem::hasOverloadsWithParameters() const
{
return m_hasOverloadsWithParameters;
}
void ClangAssistProposalItem::setHasOverloadsWithParameters(bool hasOverloadsWithParameters)
{
m_hasOverloadsWithParameters = hasOverloadsWithParameters;
}
void ClangAssistProposalItem::keepCompletionOperator(unsigned compOp)
{
m_completionOperator = compOp;
}
void ClangAssistProposalItem::appendCodeCompletion(const CodeCompletion &codeCompletion)
{
m_codeCompletions.push_back(codeCompletion);
}
const ClangBackEnd::CodeCompletion &ClangAssistProposalItem::firstCodeCompletion() const
{
return m_codeCompletions.at(0);
}
void ClangAssistProposalItem::removeFirstCodeCompletion()
{
QTC_ASSERT(!m_codeCompletions.empty(), return;);
m_codeCompletions.erase(m_codeCompletions.begin());
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,80 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include <codecompletion.h>
#include <texteditor/codeassist/assistproposaliteminterface.h>
#include <QString>
namespace ClangCodeModel {
namespace Internal {
class ClangAssistProposalItem final : public TextEditor::AssistProposalItemInterface
{
friend bool operator<(const ClangAssistProposalItem &first, const ClangAssistProposalItem &second);
public:
~ClangAssistProposalItem() noexcept override = default;
bool prematurelyApplies(const QChar &typedCharacter) const final;
bool implicitlyApplies() const final;
void apply(TextEditor::TextDocumentManipulatorInterface &manipulator, int basePosition) const final;
void setText(const QString &text);
QString text() const final;
QIcon icon() const final;
QString detail() const final;
bool isKeyword() const final;
Qt::TextFormat detailFormat() const final;
bool isSnippet() const final;
bool isValid() const final;
quint64 hash() const final;
bool requiresFixIts() const final;
void keepCompletionOperator(unsigned compOp);
bool hasOverloadsWithParameters() const;
void setHasOverloadsWithParameters(bool hasOverloadsWithParameters);
void appendCodeCompletion(const ClangBackEnd::CodeCompletion &firstCodeCompletion);
const ClangBackEnd::CodeCompletion &firstCodeCompletion() const;
void removeFirstCodeCompletion();
private:
const QVector<ClangBackEnd::FixItContainer> &firstCompletionFixIts() const;
QString fixItText() const;
int fixItsShift(const TextEditor::TextDocumentManipulatorInterface &manipulator) const;
std::vector<ClangBackEnd::CodeCompletion> m_codeCompletions;
QList<ClangBackEnd::CodeCompletion> m_overloads;
bool m_hasOverloadsWithParameters = false;
QString m_text;
unsigned m_completionOperator;
mutable QChar m_typedCharacter;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,68 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangassistproposalitem.h"
#include "clangassistproposalmodel.h"
#include <texteditor/codeassist/assistproposaliteminterface.h>
#include <algorithm>
namespace ClangCodeModel {
namespace Internal {
constexpr int SORT_LIMIT = 30000;
bool ClangAssistProposalModel::containsDuplicates() const
{
return false;
}
bool ClangAssistProposalModel::isSortable(const QString &/*prefix*/) const
{
return m_currentItems.size() <= SORT_LIMIT;
}
void ClangAssistProposalModel::sort(const QString &/*prefix*/)
{
using TextEditor::AssistProposalItemInterface;
auto currentItemsCompare = [](AssistProposalItemInterface *first,
AssistProposalItemInterface *second) {
if (first->proposalMatch() != second->proposalMatch()) {
return static_cast<int>(first->proposalMatch())
< static_cast<int>(second->proposalMatch());
}
return false;
};
// Keep the order for the items with the same priority and name.
std::stable_sort(m_currentItems.begin(), m_currentItems.end(), currentItemsCompare);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,49 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include <cplusplus/Token.h>
#include <texteditor/codeassist/genericproposalmodel.h>
#include <clangsupport/clangsupport_global.h>
namespace ClangCodeModel {
namespace Internal {
class ClangAssistProposalModel : public TextEditor::GenericProposalModel
{
public:
ClangAssistProposalModel() = default;
bool containsDuplicates() const override;
bool isSortable(const QString &prefix) const override;
void sort(const QString &prefix) override;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -26,7 +26,6 @@
#include "clangbackendcommunicator.h" #include "clangbackendcommunicator.h"
#include "clangbackendlogging.h" #include "clangbackendlogging.h"
#include "clangcompletionassistprocessor.h"
#include "clangmodelmanagersupport.h" #include "clangmodelmanagersupport.h"
#include "clangutils.h" #include "clangutils.h"
@@ -99,8 +98,6 @@ BackendCommunicator::BackendCommunicator()
m_receiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); }); m_receiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); });
connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
this, &BackendCommunicator::onEditorAboutToClose);
connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
this, &BackendCommunicator::setupDummySender); this, &BackendCommunicator::setupDummySender);
auto globalFCB = GlobalFileChangeBlocker::instance(); auto globalFCB = GlobalFileChangeBlocker::instance();
@@ -192,11 +189,6 @@ void BackendCommunicator::documentVisibilityChanged()
visibleCppEditorDocumentsFilePaths()); visibleCppEditorDocumentsFilePaths());
} }
bool BackendCommunicator::isNotWaitingForCompletion() const
{
return !m_receiver.isExpectingCompletionsMessage();
}
void BackendCommunicator::setBackendJobsPostponed(bool postponed) void BackendCommunicator::setBackendJobsPostponed(bool postponed)
{ {
if (postponed) { if (postponed) {
@@ -349,12 +341,6 @@ void BackendCommunicator::onConnectedToBackend()
initializeBackendWithCurrentData(); initializeBackendWithCurrentData();
} }
void BackendCommunicator::onEditorAboutToClose(Core::IEditor *editor)
{
if (auto *textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor))
m_receiver.deleteProcessorsOfEditorWidget(textEditor->editorWidget());
}
void BackendCommunicator::setupDummySender() void BackendCommunicator::setupDummySender()
{ {
m_sender.reset(new DummyBackendSender); m_sender.reset(new DummyBackendSender);
@@ -443,26 +429,5 @@ void BackendCommunicator::unsavedFilesRemoved(const FileContainers &fileContaine
m_sender->unsavedFilesRemoved(message); m_sender->unsavedFilesRemoved(message);
} }
void BackendCommunicator::requestCompletions(ClangCompletionAssistProcessor *assistProcessor,
const QString &filePath,
quint32 line,
quint32 column,
qint32 funcNameStartLine,
qint32 funcNameStartColumn)
{
const RequestCompletionsMessage message(filePath,
line,
column,
funcNameStartLine,
funcNameStartColumn);
m_sender->requestCompletions(message);
m_receiver.addExpectedCompletionsMessage(message.ticketNumber, assistProcessor);
}
void BackendCommunicator::cancelCompletions(TextEditor::IAssistProcessor *processor)
{
m_receiver.cancelProcessor(processor);
}
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -48,8 +48,6 @@ namespace TextEditor { class IAssistProcessor; }
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
class ClangCompletionAssistProcessor;
class BackendCommunicator : public QObject class BackendCommunicator : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -84,16 +82,7 @@ public:
void unsavedFilesUpdatedFromCppEditorDocument(const QString &filePath); void unsavedFilesUpdatedFromCppEditorDocument(const QString &filePath);
void unsavedFilesRemoved(const FileContainers &fileContainers); void unsavedFilesRemoved(const FileContainers &fileContainers);
void requestCompletions(ClangCompletionAssistProcessor *assistProcessor,
const QString &filePath,
quint32 line,
quint32 column,
qint32 funcNameStartLine = -1,
qint32 funcNameStartColumn = -1);
void cancelCompletions(TextEditor::IAssistProcessor *processor);
void updateChangeContentStartPosition(const QString &filePath, int position); void updateChangeContentStartPosition(const QString &filePath, int position);
bool isNotWaitingForCompletion() const;
void setBackendJobsPostponed(bool postponed); void setBackendJobsPostponed(bool postponed);
@@ -107,7 +96,6 @@ private:
void setupDummySender(); void setupDummySender();
void onConnectedToBackend(); void onConnectedToBackend();
void onEditorAboutToClose(Core::IEditor *editor);
void logExecutableDoesNotExist(); void logExecutableDoesNotExist();
void logRestartedDueToUnexpectedFinish(); void logRestartedDueToUnexpectedFinish();

View File

@@ -27,7 +27,6 @@
#include "clangbackendlogging.h" #include "clangbackendlogging.h"
#include "clangcompletionassistprocessor.h"
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include <clangsupport/clangcodemodelclientmessages.h> #include <clangsupport/clangcodemodelclientmessages.h>
@@ -71,55 +70,8 @@ void BackendReceiver::setAliveHandler(const BackendReceiver::AliveHandler &handl
m_aliveHandler = handler; m_aliveHandler = handler;
} }
void BackendReceiver::addExpectedCompletionsMessage(
quint64 ticket,
ClangCompletionAssistProcessor *processor)
{
QTC_ASSERT(processor, return);
QTC_CHECK(!m_assistProcessorsTable.contains(ticket));
m_assistProcessorsTable.insert(ticket, processor);
}
void BackendReceiver::cancelProcessor(TextEditor::IAssistProcessor *processor)
{
for (auto it = m_assistProcessorsTable.cbegin(), end = m_assistProcessorsTable.cend();
it != end; ++it)
{
if (it.value() == processor) {
m_assistProcessorsTable.erase(it);
return;
}
}
}
void BackendReceiver::deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *textEditorWidget)
{
QList<quint64> toRemove;
for (auto it = m_assistProcessorsTable.cbegin(), end = m_assistProcessorsTable.cend();
it != end; ++it)
{
ClangCompletionAssistProcessor *assistProcessor = it.value();
if (assistProcessor->textEditorWidget() == textEditorWidget) {
delete assistProcessor;
toRemove.append(it.key());
}
}
for (quint64 item : toRemove)
m_assistProcessorsTable.remove(item);
}
bool BackendReceiver::isExpectingCompletionsMessage() const
{
return !m_assistProcessorsTable.isEmpty();
}
void BackendReceiver::reset() void BackendReceiver::reset()
{ {
// Clean up waiting assist processors
for (ClangCompletionAssistProcessor *processor : m_assistProcessorsTable)
processor->setAsyncProposalAvailable(nullptr);
m_assistProcessorsTable.clear();
// Clean up futures for references; TODO: Remove duplication // Clean up futures for references; TODO: Remove duplication
for (ReferencesEntry &entry : m_referencesTable) { for (ReferencesEntry &entry : m_referencesTable) {
entry.futureInterface.cancel(); entry.futureInterface.cancel();
@@ -155,10 +107,6 @@ void BackendReceiver::completions(const ClangBackEnd::CompletionsMessage &messag
{ {
qCDebugIpc() << "CompletionsMessage with" << message.codeCompletions.size() qCDebugIpc() << "CompletionsMessage with" << message.codeCompletions.size()
<< "items"; << "items";
const quint64 ticket = message.ticketNumber;
if (ClangCompletionAssistProcessor *processor = m_assistProcessorsTable.take(ticket))
processor->handleAvailableCompletions(message.codeCompletions);
} }
void BackendReceiver::annotations(const ClangBackEnd::AnnotationsMessage &message) void BackendReceiver::annotations(const ClangBackEnd::AnnotationsMessage &message)

View File

@@ -43,8 +43,6 @@ class TextEditorWidget;
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
class ClangCompletionAssistProcessor;
class BackendReceiver : public ClangBackEnd::ClangCodeModelClientInterface class BackendReceiver : public ClangBackEnd::ClangCodeModelClientInterface
{ {
public: public:
@@ -54,12 +52,6 @@ public:
using AliveHandler = std::function<void ()>; using AliveHandler = std::function<void ()>;
void setAliveHandler(const AliveHandler &handler); void setAliveHandler(const AliveHandler &handler);
void addExpectedCompletionsMessage(quint64 ticket, ClangCompletionAssistProcessor *processor);
void cancelProcessor(TextEditor::IAssistProcessor *processor);
void deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *textEditorWidget);
bool isExpectingCompletionsMessage() const;
void reset(); void reset();
private: private:
@@ -74,7 +66,6 @@ private:
private: private:
AliveHandler m_aliveHandler; AliveHandler m_aliveHandler;
QHash<quint64, ClangCompletionAssistProcessor *> m_assistProcessorsTable;
struct ReferencesEntry { struct ReferencesEntry {
ReferencesEntry() = default; ReferencesEntry() = default;

View File

@@ -29,10 +29,6 @@ QtcPlugin {
"clangactivationsequencecontextprocessor.h", "clangactivationsequencecontextprocessor.h",
"clangactivationsequenceprocessor.cpp", "clangactivationsequenceprocessor.cpp",
"clangactivationsequenceprocessor.h", "clangactivationsequenceprocessor.h",
"clangassistproposalitem.cpp",
"clangassistproposalitem.h",
"clangassistproposalmodel.cpp",
"clangassistproposalmodel.h",
"clangbackendcommunicator.cpp", "clangbackendcommunicator.cpp",
"clangbackendcommunicator.h", "clangbackendcommunicator.h",
"clangbackendlogging.cpp", "clangbackendlogging.cpp",
@@ -43,14 +39,6 @@ QtcPlugin {
"clangbackendsender.h", "clangbackendsender.h",
"clangcodemodelplugin.cpp", "clangcodemodelplugin.cpp",
"clangcodemodelplugin.h", "clangcodemodelplugin.h",
"clangcompletionassistinterface.cpp",
"clangcompletionassistinterface.h",
"clangcompletionassistprocessor.cpp",
"clangcompletionassistprocessor.h",
"clangcompletionassistprovider.cpp",
"clangcompletionassistprovider.h",
"clangcompletionchunkstotextconverter.cpp",
"clangcompletionchunkstotextconverter.h",
"clangcompletioncontextanalyzer.cpp", "clangcompletioncontextanalyzer.cpp",
"clangcompletioncontextanalyzer.h", "clangcompletioncontextanalyzer.h",
"clangconstants.h", "clangconstants.h",
@@ -72,8 +60,6 @@ QtcPlugin {
"clangfixitoperation.h", "clangfixitoperation.h",
"clangfixitoperationsextractor.cpp", "clangfixitoperationsextractor.cpp",
"clangfixitoperationsextractor.h", "clangfixitoperationsextractor.h",
"clangfunctionhintmodel.cpp",
"clangfunctionhintmodel.h",
"clangmodelmanagersupport.cpp", "clangmodelmanagersupport.cpp",
"clangmodelmanagersupport.h", "clangmodelmanagersupport.h",
"clangpreprocessorassistproposalitem.cpp", "clangpreprocessorassistproposalitem.cpp",
@@ -121,12 +107,8 @@ QtcPlugin {
condition: qtc.testsEnabled condition: qtc.testsEnabled
prefix: "test/" prefix: "test/"
files: [ files: [
"clangautomationutils.cpp",
"clangautomationutils.h",
"clangbatchfileprocessor.cpp", "clangbatchfileprocessor.cpp",
"clangbatchfileprocessor.h", "clangbatchfileprocessor.h",
"clangcodecompletion_test.cpp",
"clangcodecompletion_test.h",
"clangdtests.cpp", "clangdtests.cpp",
"clangdtests.h", "clangdtests.h",
"data/clangtestdata.qrc", "data/clangtestdata.qrc",

View File

@@ -31,7 +31,6 @@
#ifdef WITH_TESTS #ifdef WITH_TESTS
# include "test/clangbatchfileprocessor.h" # include "test/clangbatchfileprocessor.h"
# include "test/clangcodecompletion_test.h"
# include "test/clangdtests.h" # include "test/clangdtests.h"
#endif #endif
@@ -209,7 +208,6 @@ void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const
QVector<QObject *> ClangCodeModelPlugin::createTestObjects() const QVector<QObject *> ClangCodeModelPlugin::createTestObjects() const
{ {
return { return {
new Tests::ClangCodeCompletionTest,
new Tests::ClangdTestCompletion, new Tests::ClangdTestCompletion,
new Tests::ClangdTestExternalChanges, new Tests::ClangdTestExternalChanges,
new Tests::ClangdTestFindReferences, new Tests::ClangdTestFindReferences,

View File

@@ -1,81 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangcompletionassistinterface.h"
#include <texteditor/texteditor.h>
namespace ClangCodeModel {
namespace Internal {
ClangCompletionAssistInterface::ClangCompletionAssistInterface(BackendCommunicator &communicator, CompletionType type,
const TextEditor::TextEditorWidget *textEditorWidget,
int position,
const Utils::FilePath &fileName,
TextEditor::AssistReason reason,
const ProjectExplorer::HeaderPaths &headerPaths,
const CPlusPlus::LanguageFeatures &features)
: AssistInterface(textEditorWidget->document(), position, fileName, reason)
, m_communicator(communicator)
, m_type(type)
, m_headerPaths(headerPaths)
, m_languageFeatures(features)
, m_textEditorWidget(textEditorWidget)
{
}
bool ClangCompletionAssistInterface::objcEnabled() const
{
return true; // TODO:
}
const ProjectExplorer::HeaderPaths &ClangCompletionAssistInterface::headerPaths() const
{
return m_headerPaths;
}
CPlusPlus::LanguageFeatures ClangCompletionAssistInterface::languageFeatures() const
{
return m_languageFeatures;
}
void ClangCompletionAssistInterface::setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths)
{
m_headerPaths = headerPaths;
}
const TextEditor::TextEditorWidget *ClangCompletionAssistInterface::textEditorWidget() const
{
return m_textEditorWidget;
}
BackendCommunicator &ClangCompletionAssistInterface::communicator() const
{
return m_communicator;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,69 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include "clangbackendcommunicator.h"
#include "clangutils.h"
#include <texteditor/codeassist/assistinterface.h>
namespace ClangCodeModel {
namespace Internal {
enum class CompletionType { FunctionHint, Other };
class ClangCompletionAssistInterface: public TextEditor::AssistInterface
{
public:
ClangCompletionAssistInterface(BackendCommunicator &communicator,
CompletionType type,
const TextEditor::TextEditorWidget *textEditorWidget,
int position,
const Utils::FilePath &fileName,
TextEditor::AssistReason reason,
const ProjectExplorer::HeaderPaths &headerPaths,
const CPlusPlus::LanguageFeatures &features);
BackendCommunicator &communicator() const;
CompletionType type() const { return m_type; }
bool objcEnabled() const;
const ProjectExplorer::HeaderPaths &headerPaths() const;
CPlusPlus::LanguageFeatures languageFeatures() const;
const TextEditor::TextEditorWidget *textEditorWidget() const;
void setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths); // For tests
private:
BackendCommunicator &m_communicator;
const CompletionType m_type;
QStringList m_options;
ProjectExplorer::HeaderPaths m_headerPaths;
CPlusPlus::LanguageFeatures m_languageFeatures;
const TextEditor::TextEditorWidget *m_textEditorWidget;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,718 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangassistproposalitem.h"
#include "clangactivationsequenceprocessor.h"
#include "clangassistproposalmodel.h"
#include "clangcompletionassistprocessor.h"
#include "clangcompletioncontextanalyzer.h"
#include "clangfixitoperation.h"
#include "clangfunctionhintmodel.h"
#include "clangcompletionchunkstotextconverter.h"
#include "clangpreprocessorassistproposalitem.h"
#include <cppeditor/cppdoxygen.h>
#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/cpptoolsreuse.h>
#include <cppeditor/editordocumenthandle.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <texteditor/texteditorsettings.h>
#include <cplusplus/Icons.h>
#include <clangsupport/filecontainer.h>
#include <utils/algorithm.h>
#include <utils/mimeutils.h>
#include <utils/optional.h>
#include <utils/porting.h>
#include <utils/qtcassert.h>
#include <utils/textutils.h>
#include <QDirIterator>
#include <QPair>
#include <QTextDocument>
namespace ClangCodeModel {
namespace Internal {
using ClangBackEnd::CodeCompletion;
using TextEditor::AssistProposalItemInterface;
static void addAssistProposalItem(QList<AssistProposalItemInterface *> &items,
const CodeCompletion &codeCompletion,
const QString &name)
{
auto item = new ClangAssistProposalItem;
items.push_back(item);
item->setText(name);
item->setOrder(int(codeCompletion.priority));
item->appendCodeCompletion(codeCompletion);
}
// Add the next CXXMethod or CXXConstructor which is the overload for another existing item.
static void addFunctionOverloadAssistProposalItem(QList<AssistProposalItemInterface *> &items,
AssistProposalItemInterface *sameItem,
const ClangCompletionAssistInterface *interface,
const CodeCompletion &codeCompletion,
const QString &name)
{
auto *item = static_cast<ClangAssistProposalItem *>(sameItem);
item->setHasOverloadsWithParameters(codeCompletion.hasParameters);
if (codeCompletion.completionKind == CodeCompletion::ConstructorCompletionKind) {
// It's the constructor, currently constructor definitions do not lead here.
// CLANG-UPGRADE-CHECK: Can we get here with constructor definition?
item->appendCodeCompletion(codeCompletion);
return;
}
QTextCursor cursor = interface->textEditorWidget()->textCursor();
cursor.setPosition(interface->position());
cursor.movePosition(QTextCursor::StartOfWord);
const ClangBackEnd::CodeCompletionChunk resultType = codeCompletion.chunks.first();
if (matchPreviousWord(*interface->textEditorWidget(), cursor, resultType.text.toString())) {
// Function definition completion - do not merge completions together.
addAssistProposalItem(items, codeCompletion, name);
} else {
item->appendCodeCompletion(codeCompletion);
}
}
// Check if they are both CXXMethod or CXXConstructor.
static bool isTheSameFunctionOverload(const CodeCompletion &completion,
const QString &name,
ClangAssistProposalItem *lastItem)
{
return completion.completionKind == lastItem->firstCodeCompletion().completionKind
&& lastItem->text() == name;
}
QList<AssistProposalItemInterface *> ClangCompletionAssistProcessor::toAssistProposalItems(
const CodeCompletions &completions) const
{
// TODO: Handle Qt4's SIGNAL/SLOT
// Possibly check for m_completionOperator == T_SIGNAL
// Possibly check for codeCompletion.completionKind == CodeCompletion::SignalCompletionKind
QList<AssistProposalItemInterface *> items;
items.reserve(completions.size());
// If there are signals among the candidates, we employ the built-in code model to find out
// whether the cursor was on the second argument of a (dis)connect() call.
// If so, we offer only signals, as nothing else makes sense in that context.
bool considerOnlySignals = false;
if (m_position != -1 && Utils::anyOf(completions, [](const CodeCompletion &c) {
return c.completionKind == CodeCompletion::SignalCompletionKind;
})) {
considerOnlySignals = CppEditor::CppModelManager::instance()
->positionRequiresSignal(m_interface->filePath().toString(), m_content, m_position);
}
for (const CodeCompletion &codeCompletion : completions) {
if (codeCompletion.text.isEmpty())
continue; // It's an OverloadCandidate which has text but no typedText.
if (considerOnlySignals
&& codeCompletion.completionKind != CodeCompletion::ClassCompletionKind
&& codeCompletion.completionKind != CodeCompletion::NamespaceCompletionKind
&& codeCompletion.completionKind != CodeCompletion::SignalCompletionKind) {
continue;
}
// Don't offer symbols that are not accessible here.
if (codeCompletion.availability == CodeCompletion::NotAvailable
|| codeCompletion.availability == CodeCompletion::NotAccessible) {
continue;
}
const QString name = codeCompletion.completionKind == CodeCompletion::KeywordCompletionKind
? CompletionChunksToTextConverter::convertToName(codeCompletion.chunks)
: codeCompletion.text.toString();
if (items.empty()) {
addAssistProposalItem(items, codeCompletion, name);
} else {
auto *lastItem = static_cast<ClangAssistProposalItem *>(items.last());
if (isTheSameFunctionOverload(codeCompletion, name, lastItem)) {
addFunctionOverloadAssistProposalItem(items, items.back(), m_interface.data(),
codeCompletion, name);
} else {
addAssistProposalItem(items, codeCompletion, name);
}
}
}
return items;
}
using namespace CPlusPlus;
using namespace TextEditor;
ClangCompletionAssistProcessor::ClangCompletionAssistProcessor()
: CppCompletionAssistProcessor(100)
, m_completionOperator(T_EOF_SYMBOL)
{
}
ClangCompletionAssistProcessor::~ClangCompletionAssistProcessor() = default;
IAssistProposal *ClangCompletionAssistProcessor::perform(const AssistInterface *interface)
{
m_interface.reset(static_cast<const ClangCompletionAssistInterface *>(interface));
if (interface->reason() != ExplicitlyInvoked && !accepts()) {
m_requestSent = false;
return nullptr;
}
return startCompletionHelper(); // == 0 if results are calculated asynchronously
}
// All completions require fix-it, apply this fix-it now.
CodeCompletions ClangCompletionAssistProcessor::applyCompletionFixIt(const CodeCompletions &completions)
{
// CLANG-UPGRADE-CHECK: Currently we rely on fact that there are only 2 possible fix-it types:
// 'dot to arrow' and 'arrow to dot' and they can't appear for the same item.
// However if we get multiple options which trigger for the same code we need to improve this
// algorithm. Check comments to FixIts field of CodeCompletionResult and which fix-its are used
// to construct results in SemaCodeComplete.cpp.
const CodeCompletion &completion = completions.front();
const ClangBackEnd::FixItContainer &fixIt = completion.requiredFixIts.front();
ClangFixItOperation fixItOperation(Utf8String(), completion.requiredFixIts);
fixItOperation.perform();
const int fixItLength = fixIt.range.end.column - fixIt.range.start.column;
const QString fixItText = fixIt.text.toString();
m_positionForProposal += fixItText.length() - fixItLength;
CodeCompletions completionsWithoutFixIts;
completionsWithoutFixIts.reserve(completions.size());
for (const CodeCompletion &completion : completions) {
CodeCompletion completionCopy = completion;
completionCopy.requiredFixIts.clear();
completionsWithoutFixIts.push_back(completionCopy);
}
return completionsWithoutFixIts;
}
void ClangCompletionAssistProcessor::handleAvailableCompletions(const CodeCompletions &completions)
{
QTC_CHECK(m_completions.isEmpty());
if (m_sentRequestType == FunctionHintCompletion && !completions.isEmpty()) {
const CodeCompletion &firstCompletion = completions.front();
if (firstCompletion.completionKind == CodeCompletion::FunctionOverloadCompletionKind) {
setAsyncProposalAvailable(createFunctionHintProposal(completions));
return;
}
if (!m_fallbackToNormalCompletion) {
// We must report back to the code assistant under all circumstances
setAsyncProposalAvailable(nullptr);
return;
}
// else: Proceed with a normal completion in case:
// 1) it was not a function call, but e.g. a function declaration like "void f("
// 2) '{' meant not a constructor call.
}
//m_sentRequestType == NormalCompletion or function signatures were empty
// Completions are sorted the way that all items with fix-its come after all items without them
// therefore it's enough to check only the first one.
if (!completions.isEmpty() && !completions.front().requiredFixIts.isEmpty())
m_completions = toAssistProposalItems(applyCompletionFixIt(completions));
else
m_completions = toAssistProposalItems(completions);
if (m_addSnippets && !m_completions.isEmpty())
addSnippets();
setAsyncProposalAvailable(createProposal());
}
const TextEditorWidget *ClangCompletionAssistProcessor::textEditorWidget() const
{
return m_interface->textEditorWidget();
}
/// Seach backwards in the document starting from pos to find the first opening
/// parenthesis. Nested parenthesis are skipped.
static int findOpenParen(QTextDocument *document, int start)
{
unsigned parenCount = 1;
for (int position = start; position >= 0; --position) {
const QChar ch = document->characterAt(position);
if (ch == QLatin1Char('(')) {
--parenCount;
if (parenCount == 0)
return position;
} else if (ch == QLatin1Char(')')) {
++parenCount;
}
}
return -1;
}
static QByteArray modifyInput(QTextDocument *doc, int endOfExpression) {
int comma = endOfExpression;
while (comma > 0) {
const QChar ch = doc->characterAt(comma);
if (ch == QLatin1Char(','))
break;
if (ch == QLatin1Char(';') || ch == QLatin1Char('{') || ch == QLatin1Char('}')) {
// Safety net: we don't seem to have "connect(pointer, SIGNAL(" as
// input, so stop searching.
comma = -1;
break;
}
--comma;
}
if (comma < 0)
return QByteArray();
const int openBrace = findOpenParen(doc, comma);
if (openBrace < 0)
return QByteArray();
QByteArray modifiedInput = doc->toPlainText().toUtf8();
const int len = endOfExpression - comma;
QByteArray replacement(len - 4, ' ');
replacement.append(")->");
modifiedInput.replace(comma, len, replacement);
modifiedInput.insert(openBrace, '(');
return modifiedInput;
}
static QChar lastPrecedingNonWhitespaceChar(const ClangCompletionAssistInterface *interface)
{
int pos = interface->position();
while (pos >= 0 && interface->characterAt(pos).isSpace())
--pos;
return pos >= 0 ? interface->characterAt(pos) : QChar::Null;
}
IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper()
{
ClangCompletionContextAnalyzer analyzer(m_interface.data(), m_interface->languageFeatures());
analyzer.analyze();
m_completionOperator = analyzer.completionOperator();
m_positionForProposal = analyzer.positionForProposal();
m_addSnippets = analyzer.addSnippets();
QByteArray modifiedFileContent;
const ClangCompletionContextAnalyzer::CompletionAction action = analyzer.completionAction();
switch (action) {
case ClangCompletionContextAnalyzer::CompleteDoxygenKeyword:
if (completeDoxygenKeywords())
return createProposal();
break;
case ClangCompletionContextAnalyzer::CompleteIncludePath:
m_completions = completeInclude(analyzer.positionEndOfExpression(), m_completionOperator,
m_interface.data(), m_interface->headerPaths());
if (!m_completions.isEmpty())
return createProposal();
break;
case ClangCompletionContextAnalyzer::CompletePreprocessorDirective:
if (completePreprocessorDirectives())
return createProposal();
break;
case ClangCompletionContextAnalyzer::CompleteSignal:
case ClangCompletionContextAnalyzer::CompleteSlot:
modifiedFileContent = modifyInput(m_interface->textDocument(),
analyzer.positionEndOfExpression());
Q_FALLTHROUGH();
case ClangCompletionContextAnalyzer::PassThroughToLibClang: {
m_sentRequestType = NormalCompletion;
m_requestSent = sendCompletionRequest(analyzer.positionForClang(),
modifiedFileContent);
break;
}
case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: {
m_sentRequestType = FunctionHintCompletion;
if (lastPrecedingNonWhitespaceChar(m_interface.data()) == ',')
m_fallbackToNormalCompletion = false;
m_requestSent = sendCompletionRequest(analyzer.positionForClang(), QByteArray(),
analyzer.functionNameStart());
break;
}
case ClangCompletionContextAnalyzer::CompleteNone:
default:
break;
}
return nullptr;
}
int ClangCompletionAssistProcessor::startOfOperator(int positionInDocument,
unsigned *kind,
bool wantFunctionCall) const
{
auto activationSequence = m_interface->textAt(positionInDocument - 3, 3);
ActivationSequenceProcessor activationSequenceProcessor(activationSequence,
positionInDocument,
wantFunctionCall);
*kind = activationSequenceProcessor.completionKind();
int start = activationSequenceProcessor.operatorStartPosition();
CppCompletionAssistProcessor::startOfOperator(m_interface->textDocument(),
positionInDocument,
kind,
start,
m_interface->languageFeatures());
return start;
}
int ClangCompletionAssistProcessor::findStartOfName(int pos) const
{
if (pos == -1)
pos = m_interface->position();
QChar chr;
// Skip to the start of a name
do {
chr = m_interface->characterAt(--pos);
} while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
return pos + 1;
}
bool ClangCompletionAssistProcessor::accepts() const
{
const int pos = m_interface->position();
unsigned token = T_EOF_SYMBOL;
const int start = startOfOperator(pos, &token, /*want function call=*/ true);
if (start != pos) {
if (token == T_POUND) {
const int column = pos - m_interface->textDocument()->findBlock(start).position();
if (column != 1)
return false;
}
return true;
} else {
// Trigger completion after n characters of a name have been typed, when not editing an existing name
QChar characterUnderCursor = m_interface->characterAt(pos);
if (!characterUnderCursor.isLetterOrNumber() && characterUnderCursor != QLatin1Char('_')) {
const int startOfName = findStartOfName(pos);
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
const QChar firstCharacter = m_interface->characterAt(startOfName);
if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
return !CppEditor::isInCommentOrString(m_interface.data(),
m_interface->languageFeatures());
}
}
}
}
return false;
}
/**
* @brief Creates completion proposals for #include and given cursor
* @param position - cursor placed after opening bracked or quote
* @param completionOperator - the type of token
* @param interface - relevant document data
* @param headerPaths - the include paths
* @return the list of completion items
*/
QList<AssistProposalItemInterface *> ClangCompletionAssistProcessor::completeInclude(
int position, unsigned completionOperator, const TextEditor::AssistInterface *interface,
const ProjectExplorer::HeaderPaths &headerPaths)
{
QTextCursor cursor(interface->textDocument());
cursor.setPosition(position);
QString directoryPrefix;
if (completionOperator == T_SLASH) {
QTextCursor c = cursor;
c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString sel = c.selectedText();
int startCharPos = sel.indexOf(QLatin1Char('"'));
if (startCharPos == -1) {
startCharPos = sel.indexOf(QLatin1Char('<'));
completionOperator = T_ANGLE_STRING_LITERAL;
} else {
completionOperator = T_STRING_LITERAL;
}
if (startCharPos != -1)
directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
}
// Make completion for all relevant includes
ProjectExplorer::HeaderPaths allHeaderPaths = headerPaths;
const auto currentFilePath = ProjectExplorer::HeaderPath::makeUser(
interface->filePath().toFileInfo().path());
if (!allHeaderPaths.contains(currentFilePath))
allHeaderPaths.append(currentFilePath);
const ::Utils::MimeType mimeType = ::Utils::mimeTypeForName("text/x-c++hdr");
const QStringList suffixes = mimeType.suffixes();
QList<AssistProposalItemInterface *> completions;
foreach (const ProjectExplorer::HeaderPath &headerPath, allHeaderPaths) {
QString realPath = headerPath.path;
if (!directoryPrefix.isEmpty()) {
realPath += QLatin1Char('/');
realPath += directoryPrefix;
if (headerPath.type == ProjectExplorer::HeaderPathType::Framework)
realPath += QLatin1String(".framework/Headers");
}
completions << completeIncludePath(realPath, suffixes, completionOperator);
}
QList<QPair<AssistProposalItemInterface *, QString>> completionsForSorting;
for (AssistProposalItemInterface * const item : qAsConst(completions)) {
QString s = item->text();
s.replace('/', QChar(0)); // The dir separator should compare less than anything else.
completionsForSorting << qMakePair(item, s);
}
Utils::sort(completionsForSorting, [](const auto &left, const auto &right) {
return left.second < right.second;
});
for (int i = 0; i < completionsForSorting.count(); ++i)
completions[i] = completionsForSorting[i].first;
return completions;
}
/**
* @brief Finds #include completion proposals using given include path
* @param realPath - one of directories where compiler searches includes
* @param suffixes - file suffixes for C/C++ header files
* @return a list of matching completion items
*/
QList<AssistProposalItemInterface *> ClangCompletionAssistProcessor::completeIncludePath(
const QString &realPath, const QStringList &suffixes, unsigned completionOperator)
{
QList<AssistProposalItemInterface *> completions;
QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
//: Parent folder for proposed #include completion
const QString hint = tr("Location: %1").arg(QDir::toNativeSeparators(QDir::cleanPath(realPath)));
while (i.hasNext()) {
const QString fileName = i.next();
const QFileInfo fileInfo = i.fileInfo();
const QString suffix = fileInfo.suffix();
if (suffix.isEmpty() || suffixes.contains(suffix)) {
QString text = fileName.mid(realPath.length() + 1);
if (fileInfo.isDir())
text += QLatin1Char('/');
auto *item = new ClangPreprocessorAssistProposalItem;
item->setText(text);
item->setDetail(hint);
item->setIcon(CPlusPlus::Icons::keywordIcon());
item->setCompletionOperator(completionOperator);
completions.append(item);
}
}
return completions;
}
bool ClangCompletionAssistProcessor::completePreprocessorDirectives()
{
foreach (const QString &preprocessorCompletion, preprocessorCompletions())
addCompletionItem(preprocessorCompletion,
Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro));
if (m_interface->objcEnabled())
addCompletionItem(QLatin1String("import"),
Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro));
return !m_completions.isEmpty();
}
bool ClangCompletionAssistProcessor::completeDoxygenKeywords()
{
for (int i = 1; i < CppEditor::T_DOXY_LAST_TAG; ++i)
addCompletionItem(QString::fromLatin1(CppEditor::doxygenTagSpell(i)), CPlusPlus::Icons::keywordIcon());
return !m_completions.isEmpty();
}
void ClangCompletionAssistProcessor::addCompletionItem(const QString &text,
const QIcon &icon,
int order)
{
auto *item = new ClangPreprocessorAssistProposalItem;
item->setText(text);
item->setIcon(icon);
item->setOrder(order);
item->setCompletionOperator(m_completionOperator);
m_completions.append(item);
}
ClangCompletionAssistProcessor::UnsavedFileContentInfo
ClangCompletionAssistProcessor::unsavedFileContent(const QByteArray &customFileContent) const
{
const bool hasCustomModification = !customFileContent.isEmpty();
UnsavedFileContentInfo info;
info.isDocumentModified = hasCustomModification || m_interface->textDocument()->isModified();
info.unsavedContent = hasCustomModification
? customFileContent
: m_interface->textDocument()->toPlainText().toUtf8();
return info;
}
void ClangCompletionAssistProcessor::sendFileContent(const QByteArray &customFileContent)
{
// TODO: Revert custom modification after the completions
const UnsavedFileContentInfo info = unsavedFileContent(customFileContent);
BackendCommunicator &communicator = m_interface->communicator();
communicator.documentsChanged({{m_interface->filePath().toString(),
Utf8String::fromByteArray(info.unsavedContent),
info.isDocumentModified,
uint(m_interface->textDocument()->revision())}});
}
namespace {
bool shouldSendDocumentForCompletion(const QString &filePath,
int completionPosition)
{
CppEditor::CppEditorDocumentHandle *document = cppDocument(filePath);
if (document) {
auto &sendTracker = document->sendTracker();
return sendTracker.shouldSendRevisionWithCompletionPosition(int(document->revision()),
completionPosition);
}
return true;
}
bool shouldSendCodeCompletion(const QString &filePath,
int completionPosition)
{
CppEditor::CppEditorDocumentHandle *document = cppDocument(filePath);
if (document) {
auto &sendTracker = document->sendTracker();
return sendTracker.shouldSendCompletion(completionPosition);
}
return true;
}
void setLastDocumentRevision(const QString &filePath)
{
CppEditor::CppEditorDocumentHandle *document = cppDocument(filePath);
if (document)
document->sendTracker().setLastSentRevision(int(document->revision()));
}
void setLastCompletionPosition(const QString &filePath,
int completionPosition)
{
CppEditor::CppEditorDocumentHandle *document = cppDocument(filePath);
if (document)
document->sendTracker().setLastCompletionPosition(completionPosition);
}
}
ClangCompletionAssistProcessor::Position
ClangCompletionAssistProcessor::extractLineColumn(int position)
{
if (position < 0)
return {-1, -1};
int line = -1, column = -1;
Utils::Text::convertPosition(m_interface->textDocument(), position, &line, &column);
column = clangColumn(m_interface->textDocument()->findBlock(position), column);
return {line, column};
}
bool ClangCompletionAssistProcessor::sendCompletionRequest(int position,
const QByteArray &customFileContent,
int functionNameStartPosition)
{
const QString filePath = m_interface->filePath().toString();
auto &communicator = m_interface->communicator();
if (shouldSendCodeCompletion(filePath, position) || communicator.isNotWaitingForCompletion()) {
if (shouldSendDocumentForCompletion(filePath, position)) {
sendFileContent(customFileContent);
setLastDocumentRevision(filePath);
}
const Position cursorPosition = extractLineColumn(position);
const Position functionNameStart = extractLineColumn(functionNameStartPosition);
communicator.requestCompletions(this,
filePath,
uint(cursorPosition.line),
uint(cursorPosition.column),
functionNameStart.line,
functionNameStart.column);
setLastCompletionPosition(filePath, position);
if (m_sentRequestType == NormalCompletion) {
if (!customFileContent.isEmpty())
m_content = customFileContent;
else if (const CppEditor::CppEditorDocumentHandle * const doc = cppDocument(filePath))
m_content = doc->contents();
m_position = position;
}
return true;
}
return false;
}
TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal()
{
m_requestSent = false;
TextEditor::GenericProposalModelPtr model(new ClangAssistProposalModel());
model->loadContent(m_completions);
return new GenericProposal(m_positionForProposal, model);
}
IAssistProposal *ClangCompletionAssistProcessor::createFunctionHintProposal(
const ClangBackEnd::CodeCompletions &completions)
{
m_requestSent = false;
TextEditor::FunctionHintProposalModelPtr model(new ClangFunctionHintModel(completions));
return new FunctionHintProposal(m_positionForProposal, model);
}
void ClangCompletionAssistProcessor::cancel()
{
m_interface->communicator().cancelCompletions(this);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,117 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include "clangcompletionassistinterface.h"
#include <cppeditor/cppcompletionassistprocessor.h>
#include <clangsupport/codecompletion.h>
#include <QCoreApplication>
#include <QTextCursor>
namespace TextEditor {
class AssistInterface;
class AssistProposalItemInterface;
}
namespace ClangCodeModel {
namespace Internal {
using ClangBackEnd::CodeCompletions;
using ClangBackEnd::CompletionCorrection;
class ClangCompletionAssistProcessor : public CppEditor::CppCompletionAssistProcessor
{
Q_DECLARE_TR_FUNCTIONS(ClangCodeModel::Internal::ClangCompletionAssistProcessor)
public:
ClangCompletionAssistProcessor();
~ClangCompletionAssistProcessor() override;
static QList<TextEditor::AssistProposalItemInterface *> completeInclude(
int position, unsigned completionOperator,
const TextEditor::AssistInterface *interface,
const ProjectExplorer::HeaderPaths &headerPaths);
TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
void handleAvailableCompletions(const CodeCompletions &completions);
bool running() final { return m_requestSent; }
const TextEditor::TextEditorWidget *textEditorWidget() const;
private:
void cancel() override;
TextEditor::IAssistProposal *startCompletionHelper();
int startOfOperator(int pos, unsigned *kind, bool wantFunctionCall) const;
int findStartOfName(int pos = -1) const;
bool accepts() const;
TextEditor::IAssistProposal *createProposal();
TextEditor::IAssistProposal *createFunctionHintProposal(
const CodeCompletions &completions);
QList<TextEditor::AssistProposalItemInterface *> toAssistProposalItems(
const CodeCompletions &completions) const;
static QList<TextEditor::AssistProposalItemInterface *> completeIncludePath(
const QString &realPath, const QStringList &suffixes, unsigned completionOperator);
bool completePreprocessorDirectives();
bool completeDoxygenKeywords();
void addCompletionItem(const QString &text,
const QIcon &icon = QIcon(),
int order = 0);
struct UnsavedFileContentInfo {
QByteArray unsavedContent;
bool isDocumentModified = false;
};
UnsavedFileContentInfo unsavedFileContent(const QByteArray &customFileContent) const;
void sendFileContent(const QByteArray &customFileContent);
bool sendCompletionRequest(int position,
const QByteArray &customFileContent,
int functionNameStartPosition = -1);
CodeCompletions applyCompletionFixIt(const CodeCompletions &completions);
private:
struct Position { int line; int column; };
Position extractLineColumn(int position);
QScopedPointer<const ClangCompletionAssistInterface> m_interface;
unsigned m_completionOperator;
enum CompletionRequestType { NormalCompletion, FunctionHintCompletion };
CompletionRequestType m_sentRequestType = NormalCompletion;
int m_position = -1;
QByteArray m_content;
bool m_requestSent = false;
bool m_addSnippets = false; // For type == Type::NormalCompletion
bool m_fallbackToNormalCompletion = true;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,87 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangcompletionassistprovider.h"
#include "clangcompletionassistprocessor.h"
#include "clangeditordocumentprocessor.h"
#include "clangutils.h"
#include <cplusplus/Token.h>
#include <cppeditor/cppcompletionassistprocessor.h>
#include <cppeditor/projectpart.h>
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/iassistprovider.h>
#include <texteditor/texteditor.h>
#include <utils/qtcassert.h>
namespace ClangCodeModel {
namespace Internal {
ClangCompletionAssistProvider::ClangCompletionAssistProvider(BackendCommunicator &communicator,
CompletionType type)
: m_communicator(communicator), m_type(type)
{
}
TextEditor::IAssistProvider::RunType ClangCompletionAssistProvider::runType() const
{
return Asynchronous;
}
TextEditor::IAssistProcessor *ClangCompletionAssistProvider::createProcessor(
const TextEditor::AssistInterface *) const
{
return new ClangCompletionAssistProcessor;
}
TextEditor::AssistInterface *ClangCompletionAssistProvider::createAssistInterface(
const Utils::FilePath &filePath,
const TextEditor::TextEditorWidget *textEditorWidget,
const CPlusPlus::LanguageFeatures & /*languageFeatures*/,
int position,
TextEditor::AssistReason reason) const
{
const CppEditor::ProjectPart::ConstPtr projectPart = projectPartForFileBasedOnProcessor(
filePath.toString());
if (projectPart) {
return new ClangCompletionAssistInterface(m_communicator,
m_type,
textEditorWidget,
position,
filePath,
reason,
projectPart->headerPaths,
projectPart->languageFeatures);
}
return nullptr;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,61 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include "clangbackendcommunicator.h"
#include "clangcompletionassistinterface.h"
#include <cppeditor/cppcompletionassistprovider.h>
#include <texteditor/codeassist/assistinterface.h>
namespace ClangCodeModel {
namespace Internal {
class ClangCompletionAssistProvider : public CppEditor::CppCompletionAssistProvider
{
Q_OBJECT
public:
ClangCompletionAssistProvider(BackendCommunicator &communicator, CompletionType type);
IAssistProvider::RunType runType() const override;
TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override;
TextEditor::AssistInterface *createAssistInterface(
const Utils::FilePath &filePath,
const TextEditor::TextEditorWidget *textEditorWidget,
const CPlusPlus::LanguageFeatures &languageFeatures,
int position,
TextEditor::AssistReason reason) const override;
private:
BackendCommunicator &m_communicator;
CompletionType m_type;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,366 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangcompletionchunkstotextconverter.h"
#include <QtGlobal>
#include <algorithm>
#include <functional>
namespace ClangCodeModel {
namespace Internal {
void CompletionChunksToTextConverter::parseChunks(
const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks)
{
m_text.clear();
m_placeholderPositions.clear();
m_codeCompletionChunks = codeCompletionChunks;
addExtraVerticalSpaceBetweenBraces();
std::for_each(m_codeCompletionChunks.cbegin(),
m_codeCompletionChunks.cend(),
[this] (const ClangBackEnd::CodeCompletionChunk &chunk)
{
parseDependentOnTheOptionalState(chunk);
m_previousCodeCompletionChunk = chunk;
});
}
void CompletionChunksToTextConverter::setAddPlaceHolderText(bool addPlaceHolderText)
{
m_addPlaceHolderText = addPlaceHolderText;
}
void CompletionChunksToTextConverter::setAddPlaceHolderPositions(bool addPlaceHolderPositions)
{
m_addPlaceHolderPositions = addPlaceHolderPositions;
}
void CompletionChunksToTextConverter::setAddResultType(bool addResultType)
{
m_addResultType = addResultType;
}
void CompletionChunksToTextConverter::setAddSpaces(bool addSpaces)
{
m_addSpaces = addSpaces;
}
void CompletionChunksToTextConverter::setHonorVerticalSpace(bool honor)
{
m_honorVerticalSpace = honor;
}
void CompletionChunksToTextConverter::setAddExtraVerticalSpaceBetweenBraces(
bool addExtraVerticalSpaceBetweenBraces)
{
m_addExtraVerticalSpaceBetweenBraces = addExtraVerticalSpaceBetweenBraces;
}
void CompletionChunksToTextConverter::setEmphasizeOptional(bool emphasizeOptional)
{
m_emphasizeOptional = emphasizeOptional;
}
void CompletionChunksToTextConverter::setAddOptional(bool addOptional)
{
m_addOptional = addOptional;
}
void CompletionChunksToTextConverter::setPlaceHolderToEmphasize(int placeHolderNumber)
{
m_placeHolderPositionToEmphasize = placeHolderNumber;
}
void CompletionChunksToTextConverter::setCompletionKind(const ClangBackEnd::CodeCompletion::Kind kind)
{
m_codeCompletionKind = kind;
}
void CompletionChunksToTextConverter::setupForKeywords()
{
setAddPlaceHolderPositions(true);
setAddSpaces(true);
setAddExtraVerticalSpaceBetweenBraces(true);
}
const QString &CompletionChunksToTextConverter::text() const
{
return m_text;
}
const std::vector<int> &CompletionChunksToTextConverter::placeholderPositions() const
{
return m_placeholderPositions;
}
bool CompletionChunksToTextConverter::hasPlaceholderPositions() const
{
return m_placeholderPositions.size() > 0;
}
QString CompletionChunksToTextConverter::convertToFunctionSignatureWithHtml(
const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks,
ClangBackEnd::CodeCompletion::Kind codeCompletionKind,
int parameterToEmphasize)
{
CompletionChunksToTextConverter converter;
converter.setAddPlaceHolderText(true);
converter.setAddResultType(true);
converter.setTextFormat(TextFormat::Html);
converter.setAddOptional(true);
converter.setEmphasizeOptional(true);
converter.setAddPlaceHolderPositions(true);
converter.setPlaceHolderToEmphasize(parameterToEmphasize);
converter.setCompletionKind(codeCompletionKind);
converter.parseChunks(codeCompletionChunks);
return converter.text();
}
QString CompletionChunksToTextConverter::convertToName(
const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks)
{
CompletionChunksToTextConverter converter;
converter.setHonorVerticalSpace(false);
converter.parseChunks(codeCompletionChunks);
return converter.text();
}
QString CompletionChunksToTextConverter::convertToToolTipWithHtml(
const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks,
ClangBackEnd::CodeCompletion::Kind codeCompletionKind)
{
CompletionChunksToTextConverter converter;
converter.setAddPlaceHolderText(true);
converter.setAddSpaces(true);
converter.setAddExtraVerticalSpaceBetweenBraces(true);
converter.setAddOptional(true);
converter.setTextFormat(TextFormat::Html);
converter.setEmphasizeOptional(true);
converter.setAddResultType(true);
converter.setCompletionKind(codeCompletionKind);
converter.parseChunks(codeCompletionChunks);
return converter.text();
}
void CompletionChunksToTextConverter::parse(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk)
{
using ClangBackEnd::CodeCompletionChunk;
switch (codeCompletionChunk.kind) {
case CodeCompletionChunk::ResultType: parseResultType(codeCompletionChunk.text); break;
// Do not rely on CurrentParameter because it might be wrong for
// invalid code. Instead, handle it as PlaceHolder.
case CodeCompletionChunk::CurrentParameter:
case CodeCompletionChunk::Placeholder:
parsePlaceHolder(codeCompletionChunk); break;
case CodeCompletionChunk::LeftParen: parseLeftParen(codeCompletionChunk); break;
case CodeCompletionChunk::LeftBrace: parseLeftBrace(codeCompletionChunk); break;
case CodeCompletionChunk::VerticalSpace:
if (!m_honorVerticalSpace)
break;
Q_FALLTHROUGH();
default: parseText(codeCompletionChunk.text); break;
}
}
void CompletionChunksToTextConverter::parseDependentOnTheOptionalState(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk)
{
wrapInCursiveTagIfOptional(codeCompletionChunk);
if (isNotOptionalOrAddOptionals(codeCompletionChunk))
parse(codeCompletionChunk);
}
void CompletionChunksToTextConverter::parseResultType(const Utf8String &resultTypeText)
{
if (m_addResultType)
m_text += inDesiredTextFormat(resultTypeText) + QChar(QChar::Space);
}
void CompletionChunksToTextConverter::parseText(const Utf8String &text)
{
if (canAddSpace()
&& m_previousCodeCompletionChunk.kind == ClangBackEnd::CodeCompletionChunk::RightBrace) {
m_text += QChar(QChar::Space);
}
m_text += inDesiredTextFormat(text);
}
void CompletionChunksToTextConverter::wrapInCursiveTagIfOptional(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk)
{
if (m_addOptional) {
if (m_emphasizeOptional && m_textFormat == TextFormat::Html) {
if (!m_previousCodeCompletionChunk.isOptional && codeCompletionChunk.isOptional)
m_text += QStringLiteral("<i>");
else if (m_previousCodeCompletionChunk.isOptional && !codeCompletionChunk.isOptional)
m_text += QStringLiteral("</i>");
}
}
}
void CompletionChunksToTextConverter::parsePlaceHolder(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk)
{
if (m_addPlaceHolderText) {
appendText(inDesiredTextFormat(codeCompletionChunk.text),
emphasizeCurrentPlaceHolder());
}
if (m_addPlaceHolderPositions)
m_placeholderPositions.push_back(m_text.size());
}
void CompletionChunksToTextConverter::parseLeftParen(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk)
{
if (canAddSpace())
m_text += QChar(QChar::Space);
m_text += codeCompletionChunk.text.toString();
}
void CompletionChunksToTextConverter::parseLeftBrace(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk)
{
if (canAddSpace())
m_text += QChar(QChar::Space);
m_text += codeCompletionChunk.text.toString();
}
void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces()
{
if (m_addExtraVerticalSpaceBetweenBraces)
addExtraVerticalSpaceBetweenBraces(m_codeCompletionChunks.begin());
}
void CompletionChunksToTextConverter::addExtraVerticalSpaceBetweenBraces(
const ClangBackEnd::CodeCompletionChunks::iterator &begin)
{
using ClangBackEnd::CodeCompletionChunk;
const auto leftBraceCompare = [] (const CodeCompletionChunk &chunk) {
return chunk.kind == CodeCompletionChunk::LeftBrace;
};
const auto rightBraceCompare = [] (const CodeCompletionChunk &chunk) {
return chunk.kind == CodeCompletionChunk::RightBrace;
};
const auto verticalSpaceCompare = [] (const CodeCompletionChunk &chunk) {
return chunk.kind == CodeCompletionChunk::VerticalSpace;
};
auto leftBrace = std::find_if(begin, m_codeCompletionChunks.end(), leftBraceCompare);
if (leftBrace != m_codeCompletionChunks.end()) {
auto rightBrace = std::find_if(leftBrace, m_codeCompletionChunks.end(), rightBraceCompare);
if (rightBrace != m_codeCompletionChunks.end()) {
auto verticalSpaceCount = std::count_if(leftBrace, rightBrace, verticalSpaceCompare);
if (verticalSpaceCount <= 1) {
auto distance = std::distance(leftBrace, rightBrace);
CodeCompletionChunk verticalSpaceChunck(CodeCompletionChunk::VerticalSpace,
Utf8StringLiteral("\n"));
auto verticalSpace = m_codeCompletionChunks.insert(std::next(leftBrace),
verticalSpaceChunck);
std::advance(verticalSpace, distance);
rightBrace = verticalSpace;
}
auto begin = std::next(rightBrace);
if (begin != m_codeCompletionChunks.end())
addExtraVerticalSpaceBetweenBraces(begin);
}
}
}
QString CompletionChunksToTextConverter::inDesiredTextFormat(const Utf8String &text) const
{
if (m_textFormat == TextFormat::Html)
return text.toString().toHtmlEscaped();
else
return text.toString();
}
bool CompletionChunksToTextConverter::emphasizeCurrentPlaceHolder() const
{
if (m_addPlaceHolderPositions) {
const uint currentPlaceHolderPosition = uint(m_placeholderPositions.size() + 1);
return uint(m_placeHolderPositionToEmphasize) == currentPlaceHolderPosition;
}
return false;
}
void CompletionChunksToTextConverter::setTextFormat(TextFormat textFormat)
{
m_textFormat = textFormat;
}
void CompletionChunksToTextConverter::appendText(const QString &text, bool boldFormat)
{
if (boldFormat && m_textFormat == TextFormat::Html)
m_text += QStringLiteral("<b>") + text + QStringLiteral("</b>");
else
m_text += text;
}
bool CompletionChunksToTextConverter::canAddSpace() const
{
return m_addSpaces
&& m_previousCodeCompletionChunk.kind != ClangBackEnd::CodeCompletionChunk::HorizontalSpace
&& m_previousCodeCompletionChunk.kind != ClangBackEnd::CodeCompletionChunk::RightAngle
&& m_codeCompletionKind != ClangBackEnd::CodeCompletion::FunctionCompletionKind;
}
bool CompletionChunksToTextConverter::isNotOptionalOrAddOptionals(
const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk) const
{
return !codeCompletionChunk.isOptional || m_addOptional;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,114 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include <clangsupport/codecompletion.h>
#include <clangsupport/utf8string.h>
#include <QString>
#include <vector>
namespace ClangCodeModel {
namespace Internal {
class CompletionChunksToTextConverter
{
public:
void parseChunks(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
enum class TextFormat {
Plain,
Html
};
void setTextFormat(TextFormat textFormat);
void setAddPlaceHolderText(bool addPlaceHolderText);
void setAddPlaceHolderPositions(bool addPlaceHolderPositions);
void setAddResultType(bool addResultType);
void setAddSpaces(bool addSpaces);
void setHonorVerticalSpace(bool honor);
void setAddExtraVerticalSpaceBetweenBraces(bool addExtraVerticalSpaceBetweenBraces);
void setEmphasizeOptional(bool emphasizeOptional); // Only for Html format
void setAddOptional(bool addOptional);
void setPlaceHolderToEmphasize(int placeHolderNumber);
void setCompletionKind(const ClangBackEnd::CodeCompletion::Kind kind);
void setupForKeywords();
const QString &text() const;
const std::vector<int> &placeholderPositions() const;
bool hasPlaceholderPositions() const;
static QString convertToName(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
static QString convertToKeywords(const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks);
static QString convertToToolTipWithHtml(
const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks,
ClangBackEnd::CodeCompletion::Kind codeCompletionKind);
static QString convertToFunctionSignatureWithHtml(
const ClangBackEnd::CodeCompletionChunks &codeCompletionChunks,
ClangBackEnd::CodeCompletion::Kind codeCompletionKind,
int parameterToEmphasize = -1);
private:
void parse(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void parseDependentOnTheOptionalState(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void parseResultType(const Utf8String &text);
void parseText(const Utf8String &text);
void wrapInCursiveTagIfOptional(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void parsePlaceHolder(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void parseLeftParen(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void parseLeftBrace(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk);
void addExtraVerticalSpaceBetweenBraces();
void addExtraVerticalSpaceBetweenBraces(const ClangBackEnd::CodeCompletionChunks::iterator &);
QString inDesiredTextFormat(const Utf8String &text) const;
void appendText(const QString &text, bool boldFormat = false); // Boldness only in Html format
bool canAddSpace() const;
bool isNotOptionalOrAddOptionals(const ClangBackEnd::CodeCompletionChunk &codeCompletionChunk) const;
bool emphasizeCurrentPlaceHolder() const;
private:
std::vector<int> m_placeholderPositions;
ClangBackEnd::CodeCompletionChunks m_codeCompletionChunks;
ClangBackEnd::CodeCompletionChunk m_previousCodeCompletionChunk;
QString m_text;
int m_placeHolderPositionToEmphasize = -1;
TextFormat m_textFormat = TextFormat::Plain;
ClangBackEnd::CodeCompletion::Kind m_codeCompletionKind = ClangBackEnd::CodeCompletion::Other;
bool m_addPlaceHolderText = false;
bool m_addPlaceHolderPositions = false;
bool m_addResultType = false;
bool m_addSpaces = false;
bool m_honorVerticalSpace = true;
bool m_addExtraVerticalSpaceBetweenBraces = false;
bool m_emphasizeOptional = false;
bool m_addOptional = false;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -39,6 +39,7 @@
#include <QDebug> #include <QDebug>
#include <QTextBlock> #include <QTextBlock>
#include <QTextCursor> #include <QTextCursor>
#include <QTextDocument>
using namespace CPlusPlus; using namespace CPlusPlus;
@@ -65,15 +66,6 @@ bool isTokenForPassThrough(unsigned tokenKind)
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
const ClangCompletionAssistInterface *assistInterface,
CPlusPlus::LanguageFeatures languageFeatures)
: ClangCompletionContextAnalyzer(assistInterface->textDocument(), assistInterface->position(),
assistInterface->type() == CompletionType::FunctionHint,
languageFeatures)
{
}
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer( ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
QTextDocument *document, int position, bool isFunctionHint, QTextDocument *document, int position, bool isFunctionHint,
CPlusPlus::LanguageFeatures languageFeatures) CPlusPlus::LanguageFeatures languageFeatures)

View File

@@ -38,14 +38,9 @@ namespace TextEditor { class AssistInterface; }
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
class ClangCompletionAssistInterface;
class ClangCompletionContextAnalyzer class ClangCompletionContextAnalyzer
{ {
public: public:
ClangCompletionContextAnalyzer() = delete;
ClangCompletionContextAnalyzer(const ClangCompletionAssistInterface *assistInterface,
CPlusPlus::LanguageFeatures languageFeatures);
ClangCompletionContextAnalyzer(QTextDocument *document, int position, bool isFunctionHint, ClangCompletionContextAnalyzer(QTextDocument *document, int position, bool isFunctionHint,
CPlusPlus::LanguageFeatures languageFeatures); CPlusPlus::LanguageFeatures languageFeatures);
void analyze(); void analyze();

View File

@@ -25,7 +25,6 @@
#include "clangdclient.h" #include "clangdclient.h"
#include "clangcompletionassistprocessor.h"
#include "clangcompletioncontextanalyzer.h" #include "clangcompletioncontextanalyzer.h"
#include "clangconstants.h" #include "clangconstants.h"
#include "clangdqpropertyhighlighter.h" #include "clangdqpropertyhighlighter.h"
@@ -903,8 +902,7 @@ private:
= projectPartForFile(interface->filePath().toString()); = projectPartForFile(interface->filePath().toString());
if (projectPart) if (projectPart)
headerPaths = projectPart->headerPaths; headerPaths = projectPart->headerPaths;
completions = ClangCompletionAssistProcessor::completeInclude( completions = completeInclude(m_endPos, m_completionOperator, interface, headerPaths);
m_endPos, m_completionOperator, interface, headerPaths);
break; break;
} }
} }
@@ -927,6 +925,107 @@ private:
return item; return item;
} }
/**
* @brief Creates completion proposals for #include and given cursor
* @param position - cursor placed after opening bracked or quote
* @param completionOperator - the type of token
* @param interface - relevant document data
* @param headerPaths - the include paths
* @return the list of completion items
*/
static QList<AssistProposalItemInterface *> completeInclude(
int position, unsigned completionOperator, const TextEditor::AssistInterface *interface,
const ProjectExplorer::HeaderPaths &headerPaths)
{
QTextCursor cursor(interface->textDocument());
cursor.setPosition(position);
QString directoryPrefix;
if (completionOperator == T_SLASH) {
QTextCursor c = cursor;
c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
QString sel = c.selectedText();
int startCharPos = sel.indexOf(QLatin1Char('"'));
if (startCharPos == -1) {
startCharPos = sel.indexOf(QLatin1Char('<'));
completionOperator = T_ANGLE_STRING_LITERAL;
} else {
completionOperator = T_STRING_LITERAL;
}
if (startCharPos != -1)
directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
}
// Make completion for all relevant includes
ProjectExplorer::HeaderPaths allHeaderPaths = headerPaths;
const auto currentFilePath = ProjectExplorer::HeaderPath::makeUser(
interface->filePath().toFileInfo().path());
if (!allHeaderPaths.contains(currentFilePath))
allHeaderPaths.append(currentFilePath);
const ::Utils::MimeType mimeType = ::Utils::mimeTypeForName("text/x-c++hdr");
const QStringList suffixes = mimeType.suffixes();
QList<AssistProposalItemInterface *> completions;
foreach (const ProjectExplorer::HeaderPath &headerPath, allHeaderPaths) {
QString realPath = headerPath.path;
if (!directoryPrefix.isEmpty()) {
realPath += QLatin1Char('/');
realPath += directoryPrefix;
if (headerPath.type == ProjectExplorer::HeaderPathType::Framework)
realPath += QLatin1String(".framework/Headers");
}
completions << completeIncludePath(realPath, suffixes, completionOperator);
}
QList<QPair<AssistProposalItemInterface *, QString>> completionsForSorting;
for (AssistProposalItemInterface * const item : qAsConst(completions)) {
QString s = item->text();
s.replace('/', QChar(0)); // The dir separator should compare less than anything else.
completionsForSorting << qMakePair(item, s);
}
Utils::sort(completionsForSorting, [](const auto &left, const auto &right) {
return left.second < right.second;
});
for (int i = 0; i < completionsForSorting.count(); ++i)
completions[i] = completionsForSorting[i].first;
return completions;
}
/**
* @brief Finds #include completion proposals using given include path
* @param realPath - one of directories where compiler searches includes
* @param suffixes - file suffixes for C/C++ header files
* @return a list of matching completion items
*/
static QList<AssistProposalItemInterface *> completeIncludePath(
const QString &realPath, const QStringList &suffixes, unsigned completionOperator)
{
QList<AssistProposalItemInterface *> completions;
QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
//: Parent folder for proposed #include completion
const QString hint = ClangdClient::tr("Location: %1")
.arg(QDir::toNativeSeparators(QDir::cleanPath(realPath)));
while (i.hasNext()) {
const QString fileName = i.next();
const QFileInfo fileInfo = i.fileInfo();
const QString suffix = fileInfo.suffix();
if (suffix.isEmpty() || suffixes.contains(suffix)) {
QString text = fileName.mid(realPath.length() + 1);
if (fileInfo.isDir())
text += QLatin1Char('/');
auto *item = new ClangPreprocessorAssistProposalItem;
item->setText(text);
item->setDetail(hint);
item->setIcon(CPlusPlus::Icons::keywordIcon());
item->setCompletionOperator(completionOperator);
completions.append(item);
}
}
return completions;
}
ClangdClient * const m_client; ClangdClient * const m_client;
const int m_position; const int m_position;
const int m_endPos; const int m_endPos;

View File

@@ -153,7 +153,7 @@ CppEditor::SemanticInfo ClangEditorDocumentProcessor::recalculateSemanticInfo()
CppEditor::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser() CppEditor::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser()
{ {
return m_parser; return m_builtinProcessor.parser();
} }
CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot() CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot()

View File

@@ -1,125 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangfunctionhintmodel.h"
#include "clangcompletionchunkstotextconverter.h"
#include <cplusplus/SimpleLexer.h>
namespace ClangCodeModel {
namespace Internal {
using namespace CPlusPlus;
ClangFunctionHintModel::ClangFunctionHintModel(const ClangBackEnd::CodeCompletions &functionSymbols)
: m_functionSymbols(functionSymbols)
, m_currentArgument(-1)
{
}
void ClangFunctionHintModel::reset()
{
}
int ClangFunctionHintModel::size() const
{
return m_functionSymbols.size();
}
QString ClangFunctionHintModel::text(int index) const
{
const ClangBackEnd::CodeCompletionChunks &chunks = m_functionSymbols.at(index).chunks;
const QString signatureWithEmphasizedCurrentParameter
= CompletionChunksToTextConverter::convertToFunctionSignatureWithHtml(
chunks,
m_functionSymbols.at(index).completionKind,
m_currentArgument + 1);
return signatureWithEmphasizedCurrentParameter;
}
QString ClangFunctionHintModel::id(int index) const
{
QString chunks;
for (const ClangBackEnd::CodeCompletionChunk &chunk : m_functionSymbols.at(index).chunks)
chunks += chunk.text;
return chunks;
}
int ClangFunctionHintModel::activeArgument(const QString &prefix) const
{
int activeArgumentNumber = 0;
int unbalancedParens = 0; // expressions
int unbalancedBraces = 0; // initializer lists
int unbalancedBrackets = 0; // lambda-capture
int unbalancedLessGreater = 0; // template arguments
SimpleLexer tokenize;
const Tokens tokens = tokenize(prefix);
for (const Token &token : tokens) {
if (token.is(T_LPAREN)) {
++unbalancedParens;
} else if (token.is(T_RPAREN)) {
--unbalancedParens;
} else if (token.is(T_LBRACE)) {
++unbalancedBraces;
} else if (token.is(T_RBRACE)) {
--unbalancedBraces;
} else if (token.is(T_LBRACKET)) {
++unbalancedBrackets;
} else if (token.is(T_RBRACKET)) {
--unbalancedBrackets;
} else if (token.is(T_LESS)) {
++unbalancedLessGreater;
} else if (token.is(T_GREATER)) {
--unbalancedLessGreater;
} else if (!unbalancedParens
&& !unbalancedBraces
&& !unbalancedBrackets
&& !unbalancedLessGreater
&& token.is(T_COMMA)) {
++activeArgumentNumber;
}
}
if (unbalancedParens < 0
|| unbalancedBraces < 0
|| unbalancedBrackets < 0
|| unbalancedLessGreater < 0) {
return -1;
}
if (activeArgumentNumber != m_currentArgument)
m_currentArgument = activeArgumentNumber;
return activeArgumentNumber;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,52 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include <codecompletion.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
namespace ClangCodeModel {
namespace Internal {
class ClangFunctionHintModel : public TextEditor::IFunctionHintProposalModel
{
public:
ClangFunctionHintModel(const ClangBackEnd::CodeCompletions &functionSymbols);
void reset() override;
int size() const override;
QString text(int index) const override;
QString id(int index) const override;
int activeArgument(const QString &prefix) const override;
private:
ClangBackEnd::CodeCompletions m_functionSymbols;
mutable int m_currentArgument;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -104,10 +104,7 @@ static const QList<TextEditor::TextDocument *> allCppDocuments()
return Utils::qobject_container_cast<TextEditor::TextDocument *>(documents); return Utils::qobject_container_cast<TextEditor::TextDocument *>(documents);
} }
ClangModelManagerSupport::ClangModelManagerSupport() ClangModelManagerSupport::ClangModelManagerSupport() : m_refactoringEngine(new RefactoringEngine)
: m_completionAssistProvider(m_communicator, CompletionType::Other)
, m_functionHintAssistProvider(m_communicator, CompletionType::FunctionHint)
, m_refactoringEngine(new RefactoringEngine)
{ {
QTC_CHECK(!m_instance); QTC_CHECK(!m_instance);
m_instance = this; m_instance = this;
@@ -175,12 +172,12 @@ ClangModelManagerSupport::~ClangModelManagerSupport()
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::completionAssistProvider() CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::completionAssistProvider()
{ {
return &m_completionAssistProvider; return nullptr;
} }
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAssistProvider() CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAssistProvider()
{ {
return &m_functionHintAssistProvider; return nullptr;
} }
void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data, void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,

View File

@@ -25,7 +25,7 @@
#pragma once #pragma once
#include "clangcompletionassistprovider.h" #include "clangbackendcommunicator.h"
#include "clanguiheaderondiskmanager.h" #include "clanguiheaderondiskmanager.h"
#include <cppeditor/cppmodelmanagersupport.h> #include <cppeditor/cppmodelmanagersupport.h>
@@ -143,8 +143,6 @@ private:
UiHeaderOnDiskManager m_uiHeaderOnDiskManager; UiHeaderOnDiskManager m_uiHeaderOnDiskManager;
BackendCommunicator m_communicator; BackendCommunicator m_communicator;
ClangCompletionAssistProvider m_completionAssistProvider;
ClangCompletionAssistProvider m_functionHintAssistProvider;
std::unique_ptr<CppEditor::RefactoringEngineInterface> m_refactoringEngine; std::unique_ptr<CppEditor::RefactoringEngineInterface> m_refactoringEngine;
QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings; QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings;

View File

@@ -1,140 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangautomationutils.h"
#include "../clangcompletionassistinterface.h"
#include "../clangcompletionassistprovider.h"
#include <texteditor/codeassist/assistinterface.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/completionassistprovider.h>
#include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/iassistproposal.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QElapsedTimer>
#include <QScopedPointer>
namespace ClangCodeModel {
namespace Internal {
class WaitForAsyncCompletions
{
public:
enum WaitResult { GotResults, GotInvalidResults, Timeout };
WaitResult wait(TextEditor::IAssistProcessor *processor,
TextEditor::AssistInterface *assistInterface,
int timeoutInMs)
{
QTC_ASSERT(processor, return Timeout);
QTC_ASSERT(assistInterface, return Timeout);
bool gotResults = false;
processor->setAsyncCompletionAvailableHandler(
[this, processor, &gotResults] (TextEditor::IAssistProposal *proposal) {
QTC_ASSERT(proposal, return);
QTC_CHECK(!processor->running());
proposalModel = proposal->model();
delete proposal;
gotResults = true;
});
// Are there any immediate results?
if (TextEditor::IAssistProposal *proposal = processor->perform(assistInterface)) {
proposalModel = proposal->model();
delete proposal;
QTC_ASSERT(proposalModel, return GotInvalidResults);
return GotResults;
}
// There are not any, so wait for async results.
QElapsedTimer timer;
timer.start();
while (!gotResults) {
if (timer.elapsed() >= timeoutInMs) {
processor->cancel();
return Timeout;
}
QCoreApplication::processEvents();
}
return proposalModel ? GotResults : GotInvalidResults;
}
public:
TextEditor::ProposalModelPtr proposalModel;
};
TextEditor::ProposalModelPtr completionResults(TextEditor::BaseTextEditor *textEditor,
const QStringList &includePaths,
int timeOutInMs)
{
using namespace TextEditor;
auto textEditorWidget = TextEditorWidget::fromEditor(textEditor);
QTC_ASSERT(textEditorWidget, return TextEditor::ProposalModelPtr());
AssistInterface *assistInterface = textEditorWidget->createAssistInterface(
TextEditor::Completion, TextEditor::ExplicitlyInvoked);
QTC_ASSERT(assistInterface, return TextEditor::ProposalModelPtr());
if (!includePaths.isEmpty()) {
auto clangAssistInterface = static_cast<ClangCompletionAssistInterface *>(assistInterface);
clangAssistInterface->setHeaderPaths(ProjectExplorer::toUserHeaderPaths(includePaths));
}
CompletionAssistProvider *assistProvider
= textEditor->textDocument()->completionAssistProvider();
QTC_ASSERT(qobject_cast<ClangCompletionAssistProvider *>(assistProvider),
return TextEditor::ProposalModelPtr());
QTC_ASSERT(assistProvider, return TextEditor::ProposalModelPtr());
QTC_ASSERT(assistProvider->runType() == IAssistProvider::Asynchronous,
return TextEditor::ProposalModelPtr());
QScopedPointer<IAssistProcessor> processor(assistProvider->createProcessor(assistInterface));
QTC_ASSERT(processor, return TextEditor::ProposalModelPtr());
WaitForAsyncCompletions waitForCompletions;
const WaitForAsyncCompletions::WaitResult result = waitForCompletions.wait(processor.data(),
assistInterface,
timeOutInMs);
QTC_ASSERT(result == WaitForAsyncCompletions::GotResults,
return TextEditor::ProposalModelPtr());
return waitForCompletions.proposalModel;
}
QString qrcPath(const QByteArray &relativeFilePath)
{
return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,46 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include <texteditor/codeassist/iassistproposalmodel.h>
#include <QList>
#include <QSharedPointer>
#include <QString>
namespace TextEditor { class BaseTextEditor; }
namespace ClangCodeModel {
namespace Internal {
TextEditor::ProposalModelPtr completionResults(TextEditor::BaseTextEditor *textEditor,
const QStringList &includePaths = QStringList(),
int timeOutInMs = 10000);
QString qrcPath(const QByteArray &relativeFilePath);
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -25,8 +25,6 @@
#include "clangbatchfileprocessor.h" #include "clangbatchfileprocessor.h"
#include "clangautomationutils.h"
#include <clangcodemodel/clangeditordocumentprocessor.h> #include <clangcodemodel/clangeditordocumentprocessor.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
@@ -442,45 +440,6 @@ Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments,
return Command::Ptr(new InsertTextCommand(context, textToInsert)); return Command::Ptr(new InsertTextCommand(context, textToInsert));
} }
class CompleteCommand : public Command
{
public:
CompleteCommand(const CommandContext &context);
bool run() override;
static Command::Ptr parse(BatchFileLineTokenizer &arguments,
const CommandContext &context);
};
CompleteCommand::CompleteCommand(const CommandContext &context)
: Command(context)
{
}
bool CompleteCommand::run()
{
qCDebug(debug) << "line" << context().lineNumber << "CompleteCommand";
TextEditor::BaseTextEditor *editor = currentTextEditor();
QTC_ASSERT(editor, return false);
const QString documentFilePath = editor->document()->filePath().toString();
auto *processor = ClangEditorDocumentProcessor::get(documentFilePath);
QTC_ASSERT(processor, return false);
return !completionResults(editor, QStringList(), timeOutInMs()).isNull();
}
Command::Ptr CompleteCommand::parse(BatchFileLineTokenizer &arguments,
const CommandContext &context)
{
Q_UNUSED(arguments)
Q_UNUSED(context)
return Command::Ptr(new CompleteCommand(context));
}
class SetCursorCommand : public Command class SetCursorCommand : public Command
{ {
public: public:
@@ -687,7 +646,6 @@ BatchFileParser::BatchFileParser(const QString &filePath,
m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse); m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse);
m_commandParsers.insert("setCursor", &SetCursorCommand::parse); m_commandParsers.insert("setCursor", &SetCursorCommand::parse);
m_commandParsers.insert("insertText", &InsertTextCommand::parse); m_commandParsers.insert("insertText", &InsertTextCommand::parse);
m_commandParsers.insert("complete", &CompleteCommand::parse);
m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse); m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse);
} }

View File

@@ -1,846 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "clangcodecompletion_test.h"
#include "clangautomationutils.h"
#include "clangbatchfileprocessor.h"
#include "../clangcompletionassistinterface.h"
#include "../clangmodelmanagersupport.h"
#include <clangcodemodel/clangeditordocumentprocessor.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
#include <cppeditor/cppcodemodelsettings.h>
#include <cppeditor/cpptoolsreuse.h>
#include <cppeditor/cpptoolstestcase.h>
#include <cppeditor/modelmanagertesthelper.h>
#include <cppeditor/projectinfo.h>
#include <languageclient/languageclientmanager.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <clangsupport/clangcodemodelservermessages.h>
#include <utils/changeset.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QtTest>
using namespace ClangCodeModel;
using namespace ClangCodeModel::Internal;
namespace {
CppEditor::Tests::TemporaryDir *globalTemporaryDir()
{
static CppEditor::Tests::TemporaryDir dir;
QTC_CHECK(dir.isValid());
return &dir;
}
QByteArray readFile(const QString &filePath)
{
QFile file(filePath);
QTC_ASSERT(file.open(QFile::ReadOnly | QFile::Text), return QByteArray());
return file.readAll();
}
bool writeFile(const QString &filePath, const QByteArray &contents)
{
QFile file(filePath);
if (!file.open(QFile::WriteOnly | QFile::Text))
return false;
if (file.write(contents) != contents.size())
return false;
return true;
}
class ChangeDocumentReloadSetting
{
public:
ChangeDocumentReloadSetting(Core::IDocument::ReloadSetting reloadSetting)
: m_previousValue(Core::EditorManager::reloadSetting())
{
Core::EditorManager::setReloadSetting(reloadSetting);
}
~ChangeDocumentReloadSetting()
{
Core::EditorManager::setReloadSetting(m_previousValue);
}
private:
Core::IDocument::ReloadSetting m_previousValue;
};
class TestDocument
{
public:
TestDocument(const QByteArray &fileName, CppEditor::Tests::TemporaryDir *temporaryDir = nullptr)
{
QTC_ASSERT(!fileName.isEmpty(), return);
const QResource resource(qrcPath("completion/" + fileName));
QTC_ASSERT(resource.isValid(), return);
const QByteArray contents = QByteArray(reinterpret_cast<const char*>(resource.data()),
resource.size());
finish(fileName, contents, temporaryDir);
}
static TestDocument fromExistingFile(const QString &filePath)
{
TestDocument testDocument;
QTC_ASSERT(!filePath.isEmpty(), return testDocument);
testDocument.filePath = filePath;
testDocument.cursorPosition = findCursorMarkerPosition(readFile(filePath));
return testDocument;
}
static TestDocument fromString(const QByteArray &fileName, const QByteArray &contents,
CppEditor::Tests::TemporaryDir *tempDir = nullptr)
{
TestDocument testDocument;
testDocument.finish(fileName, contents, tempDir);
return testDocument;
}
static int findCursorMarkerPosition(const QByteArray &contents)
{
return contents.indexOf(" /* COMPLETE HERE */");
}
bool isCreated() const { return !filePath.isEmpty(); }
bool hasValidCursorPosition() const { return cursorPosition >= 0; }
bool isCreatedAndHasValidCursorPosition() const
{ return isCreated() && hasValidCursorPosition(); }
QString filePath;
int cursorPosition = -1;
private:
TestDocument() = default;
void finish(const QByteArray &fileName, const QByteArray &contents,
CppEditor::Tests::TemporaryDir *temporaryDir = nullptr)
{
cursorPosition = findCursorMarkerPosition(contents);
if (!contents.isEmpty()) {
if (!temporaryDir) {
m_temporaryDir.reset(new CppEditor::Tests::TemporaryDir);
temporaryDir = m_temporaryDir.data();
}
filePath = temporaryDir->createFile(fileName, contents);
}
}
QSharedPointer<CppEditor::Tests::TemporaryDir> m_temporaryDir;
};
class OpenEditorAtCursorPosition
{
public:
OpenEditorAtCursorPosition(const TestDocument &testDocument);
~OpenEditorAtCursorPosition(); // Close editor
bool succeeded() const { return m_editor && m_backendIsNotified; }
bool waitUntilBackendIsNotified(int timeout = 10000);
bool waitUntilProjectPartChanged(const QString &newProjectPartId, int timeout = 10000);
bool waitUntil(const std::function<bool()> &condition, int timeout);
TextEditor::BaseTextEditor *editor() const { return m_editor; }
private:
TextEditor::BaseTextEditor *m_editor;
bool m_backendIsNotified = false;
};
OpenEditorAtCursorPosition::OpenEditorAtCursorPosition(const TestDocument &testDocument)
{
Core::IEditor *coreEditor = Core::EditorManager::openEditor(
Utils::FilePath::fromString(testDocument.filePath));
m_editor = qobject_cast<TextEditor::BaseTextEditor *>(coreEditor);
QTC_CHECK(m_editor);
if (m_editor) {
if (testDocument.hasValidCursorPosition())
m_editor->setCursorPosition(testDocument.cursorPosition);
m_backendIsNotified = waitUntilBackendIsNotified();
}
QTC_CHECK(m_backendIsNotified);
}
OpenEditorAtCursorPosition::~OpenEditorAtCursorPosition()
{
if (m_editor)
Core::EditorManager::closeEditors({m_editor}, /* askAboutModifiedEditors= */ false);
}
bool OpenEditorAtCursorPosition::waitUntilBackendIsNotified(int timeout)
{
const QString filePath = m_editor->document()->filePath().toString();
auto condition = [filePath] () {
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
return processor && processor->projectPart();
};
return waitUntil(condition, timeout);
}
bool OpenEditorAtCursorPosition::waitUntilProjectPartChanged(const QString &newProjectPartId,
int timeout)
{
const QString filePath = m_editor->document()->filePath().toString();
auto condition = [newProjectPartId, filePath] () {
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
return processor
&& processor->projectPart()
&& processor->projectPart()->id() == newProjectPartId;
};
return waitUntil(condition, timeout);
}
bool OpenEditorAtCursorPosition::waitUntil(const std::function<bool ()> &condition, int timeout)
{
QElapsedTimer time;
time.start();
forever {
if (time.elapsed() > timeout)
return false;
if (condition())
return true;
QCoreApplication::processEvents();
QThread::msleep(20);
}
return false;
}
CppEditor::ProjectPart::ConstPtr createProjectPart(const Utils::FilePath &projectFilePath,
const QStringList &files,
const ProjectExplorer::Macros &macros)
{
using namespace CppEditor;
ProjectExplorer::RawProjectPart rpp;
rpp.setProjectFileLocation("myproject.project");
rpp.setQtVersion(Utils::QtMajorVersion::None);
rpp.setMacros(macros);
const auto projectFiles = Utils::transform<ProjectFiles>(files, [](const QString &f) {
return ProjectFile(f, ProjectFile::classify(f));
});
return ProjectPart::create(projectFilePath, rpp, {}, projectFiles);
}
CppEditor::ProjectInfo::ConstPtr createProjectInfo(ProjectExplorer::Project *project,
const QStringList &files,
const ProjectExplorer::Macros &macros)
{
using namespace CppEditor;
QTC_ASSERT(project, return {});
const CppEditor::ProjectPart::ConstPtr projectPart
= createProjectPart(project->projectFilePath(), files, macros);
const auto projectInfo = ProjectInfo::create(
{project, ProjectExplorer::KitInfo(nullptr), {}, {}}, {projectPart});
return projectInfo;
}
class ProjectLoader
{
public:
ProjectLoader(const QStringList &projectFiles,
const ProjectExplorer::Macros &projectMacros,
bool testOnlyForCleanedProjects = false)
: m_project(nullptr)
, m_projectFiles(projectFiles)
, m_projectMacros(projectMacros)
, m_helper(nullptr, testOnlyForCleanedProjects)
{
}
bool load()
{
m_project = m_helper.createProject(QLatin1String("testProject"));
const CppEditor::ProjectInfo::ConstPtr projectInfo = createProjectInfo(m_project,
m_projectFiles,
m_projectMacros);
const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo);
return m_projectFiles.size() == filesIndexedAfterLoading.size();
}
bool updateProject(const ProjectExplorer::Macros &updatedProjectMacros)
{
QTC_ASSERT(m_project, return false);
const CppEditor::ProjectInfo::ConstPtr updatedProjectInfo
= createProjectInfo(m_project, m_projectFiles, updatedProjectMacros);
return updateProjectInfo(updatedProjectInfo);
}
private:
bool updateProjectInfo(const CppEditor::ProjectInfo::ConstPtr &projectInfo)
{
const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo);
return m_projectFiles.size() == filesIndexedAfterLoading.size();
}
ProjectExplorer::Project *m_project;
QStringList m_projectFiles;
ProjectExplorer::Macros m_projectMacros;
CppEditor::Tests::ModelManagerTestHelper m_helper;
};
class ProjectLessCompletionTest
{
public:
ProjectLessCompletionTest(const QByteArray &testFileName,
const QString &textToInsert = QString(),
const QStringList &includePaths = QStringList(),
const QByteArray &contents = {})
{
CppEditor::Tests::TestCase garbageCollectionGlobalSnapshot;
QVERIFY(garbageCollectionGlobalSnapshot.succeededSoFar());
const auto testDocument = contents.isEmpty()
? TestDocument(testFileName, globalTemporaryDir())
: TestDocument::fromString(testFileName, contents, globalTemporaryDir());
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
OpenEditorAtCursorPosition openEditor(testDocument);
QVERIFY(openEditor.succeeded());
if (!textToInsert.isEmpty())
openEditor.editor()->insert(textToInsert);
proposal = completionResults(openEditor.editor(), includePaths, timeOutInMs());
}
TextEditor::ProposalModelPtr proposal;
};
int indexOfItemWithText(TextEditor::ProposalModelPtr model, const QByteArray &text)
{
if (!model)
return -1;
for (int i = 0, size = model->size(); i < size; ++i) {
const QString itemText = model->text(i);
if (itemText == QString::fromUtf8(text))
return i;
}
return -1;
}
bool hasItem(TextEditor::ProposalModelPtr model, const QByteArray &text)
{
return indexOfItemWithText(model, text) != -1;
}
int itemsWithText(TextEditor::ProposalModelPtr model, const QByteArray &text)
{
if (!model)
return 0;
int amount = 0;
for (int i = 0, size = model->size(); i < size; ++i) {
if (model->text(i) == QString::fromUtf8(text))
++amount;
}
return amount;
}
bool hasItem(TextEditor::ProposalModelPtr model, const QByteArray &text, const QByteArray &detail)
{
const int index = indexOfItemWithText(model, text);
if (index != -1 && index < model->size()) {
TextEditor::IAssistProposalModel *imodel = model.data();
const auto genericModel = static_cast<TextEditor::GenericProposalModel *>(imodel);
const auto itemDetail = genericModel->detail(index);
return itemDetail == QString::fromUtf8(detail);
}
return false;
}
bool hasSnippet(TextEditor::ProposalModelPtr model, const QByteArray &text)
{
if (!model)
return false;
// Snippets seem to end with a whitespace
const QString snippetText = QString::fromUtf8(text) + QLatin1Char(' ');
auto *genericModel = static_cast<TextEditor::GenericProposalModel *>(model.data());
for (int i = 0, size = genericModel->size(); i < size; ++i) {
TextEditor::AssistProposalItemInterface *item = genericModel->proposalItem(i);
QTC_ASSERT(item, continue);
if (item->text() == snippetText)
return item->isSnippet();
}
return false;
}
class MonitorGeneratedUiFile : public QObject
{
public:
MonitorGeneratedUiFile();
bool waitUntilGenerated(int timeout = 10000) const;
private:
void onUiFileGenerated() { m_isGenerated = true; }
bool m_isGenerated = false;
};
MonitorGeneratedUiFile::MonitorGeneratedUiFile()
{
connect(CppEditor::CppModelManager::instance(),
&CppEditor::CppModelManager::abstractEditorSupportContentsUpdated,
this, &MonitorGeneratedUiFile::onUiFileGenerated);
}
bool MonitorGeneratedUiFile::waitUntilGenerated(int timeout) const
{
if (m_isGenerated)
return true;
QElapsedTimer time;
time.start();
forever {
if (m_isGenerated)
return true;
if (time.elapsed() > timeout)
return false;
QCoreApplication::processEvents();
QThread::msleep(20);
}
return false;
}
class WriteFileAndWaitForReloadedDocument : public QObject
{
public:
WriteFileAndWaitForReloadedDocument(const QString &filePath,
const QByteArray &fileContents,
Core::IDocument *document)
: m_filePath(filePath)
, m_fileContents(fileContents)
{
QTC_CHECK(document);
connect(document, &Core::IDocument::reloadFinished,
this, &WriteFileAndWaitForReloadedDocument::onReloadFinished);
}
void onReloadFinished()
{
m_onReloadFinished = true;
}
bool wait() const
{
QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false);
QElapsedTimer totalTime;
totalTime.start();
QElapsedTimer writeFileAgainTime;
writeFileAgainTime.start();
forever {
if (m_onReloadFinished)
return true;
if (totalTime.elapsed() > 10000)
return false;
if (writeFileAgainTime.elapsed() > 3000) {
// The timestamp did not change, try again now.
QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false);
writeFileAgainTime.restart();
}
QCoreApplication::processEvents();
QThread::msleep(20);
}
}
private:
bool m_onReloadFinished = false;
QString m_filePath;
QByteArray m_fileContents;
};
} // anonymous namespace
namespace ClangCodeModel {
namespace Internal {
namespace Tests {
void ClangCodeCompletionTest::initTestCase()
{
CppEditor::ClangdSettings::setUseClangd(false);
for (LanguageClient::Client * const c : LanguageClient::LanguageClientManager::clients())
LanguageClient::LanguageClientManager::shutdownClient(c);
}
void ClangCodeCompletionTest::testCompleteDoxygenKeywords()
{
ProjectLessCompletionTest t("doxygenKeywordsCompletion.cpp");
QVERIFY(hasItem(t.proposal, "brief"));
QVERIFY(hasItem(t.proposal, "param"));
QVERIFY(hasItem(t.proposal, "return"));
QVERIFY(!hasSnippet(t.proposal, "class"));
}
void ClangCodeCompletionTest::testCompletePreprocessorKeywords()
{
ProjectLessCompletionTest t("preprocessorKeywordsCompletion.cpp");
QVERIFY(hasItem(t.proposal, "ifdef"));
QVERIFY(hasItem(t.proposal, "endif"));
QVERIFY(!hasSnippet(t.proposal, "class"));
}
void ClangCodeCompletionTest::testCompleteIncludeDirective()
{
CppEditor::Tests::TemporaryCopiedDir testDir(qrcPath("completion/exampleIncludeDir"));
ProjectLessCompletionTest t("includeDirectiveCompletion.cpp",
QString(),
QStringList(testDir.path()));
QVERIFY(hasItem(t.proposal, "file.h"));
QVERIFY(hasItem(t.proposal, "otherFile.h"));
QVERIFY(hasItem(t.proposal, "mylib/"));
QVERIFY(!hasSnippet(t.proposal, "class"));
}
void ClangCodeCompletionTest::testCompleteGlobals()
{
ProjectLessCompletionTest t("globalCompletion.cpp");
QVERIFY(hasItem(t.proposal, "globalVariable", "int globalVariable"));
QVERIFY(hasItem(t.proposal, "globalFunction", "void globalFunction()"));
QVERIFY(hasItem(t.proposal, "GlobalClass", "GlobalClass"));
QVERIFY(hasItem(t.proposal, "class", "class")); // Keyword
QVERIFY(hasSnippet(t.proposal, "class")); // Snippet
}
void ClangCodeCompletionTest::testCompleteMembers()
{
ProjectLessCompletionTest t("memberCompletion.cpp");
QVERIFY(hasItem(t.proposal, "member"));
QVERIFY(!hasItem(t.proposal, "globalVariable"));
QVERIFY(!hasItem(t.proposal, "class")); // Keyword
QVERIFY(!hasSnippet(t.proposal, "class")); // Snippet
}
void ClangCodeCompletionTest::testCompleteMembersFromInside()
{
ProjectLessCompletionTest t("membercompletion-inside.cpp");
QVERIFY(hasItem(t.proposal, "publicFunc"));
QVERIFY(hasItem(t.proposal, "privateFunc"));
}
void ClangCodeCompletionTest::testCompleteMembersFromOutside()
{
ProjectLessCompletionTest t("membercompletion-outside.cpp");
QVERIFY(hasItem(t.proposal, "publicFunc"));
QVERIFY(!hasItem(t.proposal, "privateFunc"));
}
void ClangCodeCompletionTest::testCompleteMembersFromFriend()
{
ProjectLessCompletionTest t("membercompletion-friend.cpp");
QVERIFY(hasItem(t.proposal, "publicFunc"));
QVERIFY(hasItem(t.proposal, "privateFunc"));
}
void ClangCodeCompletionTest::testCompleteFunctions()
{
ProjectLessCompletionTest t("functionCompletion.cpp");
QVERIFY(hasItem(t.proposal, "void f()"));
QVERIFY(hasItem(t.proposal, "void f(int a)"));
QVERIFY(hasItem(t.proposal, "void f(const QString &amp;s)"));
QVERIFY(hasItem(t.proposal, "void f(char c<i>, int optional = 3</i>)"));
QVERIFY(hasItem(t.proposal, "void f(char c<i>, int optional1 = 3, int optional2 = 3</i>)"));
QVERIFY(hasItem(t.proposal, "void f(const TType&lt;QString&gt; *t)"));
QVERIFY(hasItem(t.proposal, "TType&lt;QString&gt; f(bool)"));
}
void ClangCodeCompletionTest::testCompleteOverloads()
{
ProjectLessCompletionTest t("functionCompletionFiltered.cpp");
QCOMPARE(t.proposal->size(), 1);
QVERIFY(hasItem(t.proposal, "void func(int i, int j)"));
ProjectLessCompletionTest t2("functionCompletionFiltered2.cpp");
QCOMPARE(t2.proposal->size(), 2);
QVERIFY(hasItem(t2.proposal, "void func(const S &amp;s, int j)"));
QVERIFY(hasItem(t2.proposal, "void func(const S &amp;s, int j, int k)"));
}
void ClangCodeCompletionTest::testCompleteConstructor()
{
ProjectLessCompletionTest t("constructorCompletion.cpp");
QVERIFY(!hasItem(t.proposal, "globalVariable"));
QVERIFY(!hasItem(t.proposal, "class"));
QVERIFY(hasItem(t.proposal, "Foo(int)"));
QVERIFY(hasItem(t.proposal, "Foo(int, double)"));
}
void ClangCodeCompletionTest::testCompleteClassAndConstructor()
{
ProjectLessCompletionTest t("classAndConstructorCompletion.cpp");
QCOMPARE(itemsWithText(t.proposal, "Foo"), 2);
}
// Explicitly Inserting The Dot
// ----------------------------
// Inserting the dot for is important since it will send the editor
// content to the backend and thus generate an unsaved file on the backend
// side. The unsaved file enables us to do the dot to arrow correction.
void ClangCodeCompletionTest::testCompleteWithDotToArrowCorrection()
{
ProjectLessCompletionTest t("dotToArrowCorrection.cpp",
QStringLiteral(".")); // See above "Explicitly Inserting The Dot"
QVERIFY(hasItem(t.proposal, "member"));
}
void ClangCodeCompletionTest::testDontCompleteWithDotToArrowCorrectionForFloats()
{
ProjectLessCompletionTest t("noDotToArrowCorrectionForFloats.cpp",
QStringLiteral(".")); // See above "Explicitly Inserting The Dot"
QCOMPARE(t.proposal->size(), 0);
}
void ClangCodeCompletionTest::testCompleteProjectDependingCode()
{
const TestDocument testDocument("completionWithProject.cpp");
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
ProjectLoader projectLoader(QStringList(testDocument.filePath), {{"PROJECT_CONFIGURATION_1"}});
QVERIFY(projectLoader.load());
OpenEditorAtCursorPosition openEditor(testDocument);
QVERIFY(openEditor.succeeded());
TextEditor::ProposalModelPtr proposal = completionResults(openEditor.editor(), {},
timeOutInMs());
QVERIFY(hasItem(proposal, "projectConfiguration1"));
}
void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProject()
{
const TestDocument testDocument("completionWithProject.cpp");
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
OpenEditorAtCursorPosition openEditor(testDocument);
QVERIFY(openEditor.succeeded());
// Check completion without project
TextEditor::ProposalModelPtr proposal = completionResults(openEditor.editor(), {},
timeOutInMs());
QVERIFY(hasItem(proposal, "noProjectConfigurationDetected"));
{
// Check completion with project configuration 1
ProjectLoader projectLoader(QStringList(testDocument.filePath),
{{"PROJECT_CONFIGURATION_1"}},
/* testOnlyForCleanedProjects= */ true);
QVERIFY(projectLoader.load());
openEditor.waitUntilProjectPartChanged(QLatin1String("myproject.project"));
proposal = completionResults(openEditor.editor(), {}, timeOutInMs());
QVERIFY(hasItem(proposal, "projectConfiguration1"));
QVERIFY(!hasItem(proposal, "projectConfiguration2"));
// Check completion with project configuration 2
QVERIFY(projectLoader.updateProject({{"PROJECT_CONFIGURATION_2"}}));
openEditor.waitUntilBackendIsNotified();
proposal = completionResults(openEditor.editor(), {}, timeOutInMs());
QVERIFY(!hasItem(proposal, "projectConfiguration1"));
QVERIFY(hasItem(proposal, "projectConfiguration2"));
} // Project closed
// Check again completion without project
openEditor.waitUntilProjectPartChanged(QLatin1String(""));
proposal = completionResults(openEditor.editor(), {}, timeOutInMs());
QVERIFY(hasItem(proposal, "noProjectConfigurationDetected"));
}
void ClangCodeCompletionTest::testCompleteProjectDependingCodeInGeneratedUiFile()
{
CppEditor::Tests::TemporaryCopiedDir testDir(qrcPath("qt-widgets-app"));
QVERIFY(testDir.isValid());
MonitorGeneratedUiFile monitorGeneratedUiFile;
// Open project
const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro");
CppEditor::Tests::ProjectOpenerAndCloser projectManager;
QVERIFY(projectManager.open(projectFilePath, true));
QVERIFY(monitorGeneratedUiFile.waitUntilGenerated());
// Open file with ui object
const QString completionFile = testDir.absolutePath("mainwindow.cpp");
const TestDocument testDocument = TestDocument::fromExistingFile(completionFile);
QVERIFY(testDocument.isCreatedAndHasValidCursorPosition());
OpenEditorAtCursorPosition openSource(testDocument);
QVERIFY(openSource.succeeded());
// ...and check comletions
TextEditor::ProposalModelPtr proposal = completionResults(openSource.editor(), {},
timeOutInMs());
QVERIFY(hasItem(proposal, "menuBar"));
QVERIFY(hasItem(proposal, "statusBar"));
QVERIFY(hasItem(proposal, "centralWidget"));
QVERIFY(hasItem(proposal, "setupUi"));
}
static QByteArray makeSignalCompletionContents(const QByteArray &customContents)
{
static const QByteArray definitions = R"(
class QObject {
public:
void aSignal() __attribute__((annotate("qt_signal")));
void anotherSignal() __attribute__((annotate("qt_signal")));
void notASignal();
static void connect();
static void disconnect();
};
class DerivedFromQObject : public QObject {
public:
void myOwnSignal() __attribute__((annotate("qt_signal")));
void alsoNotASignal();
};
class NotAQObject {
public:
void notASignal();
void alsoNotASignal();
static void connect();
};)";
return definitions + customContents + " /* COMPLETE HERE */";
}
void ClangCodeCompletionTest::testSignalCompletion_data()
{
QTest::addColumn<QByteArray>("customContents");
QTest::addColumn<QByteArrayList>("hits");
// libclang mis-reports CXCursor_ClassDecl instead of CXCursor_Constructor, so the lists
// below include the class name.
QTest::addRow("positive: connect() on QObject class")
<< QByteArray("int main() { QObject::connect(dummy, QObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "QObject"};
QTest::addRow("positive: connect() on QObject object")
<< QByteArray("int main() { QObject o; o.connect(dummy, QObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "QObject"};
QTest::addRow("positive: connect() on QObject pointer")
<< QByteArray("int main() { QObject *o; o->connect(dummy, QObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "QObject"};
QTest::addRow("positive: connect() on QObject rvalue")
<< QByteArray("int main() { QObject().connect(dummy, QObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "QObject"};
QTest::addRow("positive: connect() on QObject pointer rvalue")
<< QByteArray("int main() { (new QObject)->connect(dummy, QObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "QObject"};
QTest::addRow("positive: disconnect() on QObject")
<< QByteArray("int main() { QObject::disconnect(dummy, QObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "QObject"};
QTest::addRow("positive: connect() in member function of derived class")
<< QByteArray("void DerivedFromQObject::alsoNotASignal() { connect(this, DerivedFromQObject::")
<< QByteArrayList{"aSignal", "anotherSignal", "myOwnSignal", "QObject", "DerivedFromQObject"};
const QByteArrayList allQObjectFunctions{"aSignal", "anotherSignal", "notASignal", "connect",
"disconnect", "QObject", "~QObject", "operator="};
QTest::addRow("negative: different function name")
<< QByteArray("int main() { QObject::notASignal(dummy, QObject::")
<< allQObjectFunctions;
QTest::addRow("negative: connect function from other class")
<< QByteArray("int main() { NotAQObject::connect(dummy, QObject::")
<< allQObjectFunctions;
QTest::addRow("negative: first argument")
<< QByteArray("int main() { QObject::connect(QObject::")
<< allQObjectFunctions;
QTest::addRow("negative: third argument")
<< QByteArray("int main() { QObject::connect(dummy1, dummy2, QObject::")
<< allQObjectFunctions;
QTest::addRow("negative: not a QObject")
<< QByteArray("int main() { QObject::connect(dummy, NotAQObject::")
<< QByteArrayList{"notASignal", "alsoNotASignal", "connect", "NotAQObject",
"~NotAQObject", "operator="};
}
void ClangCodeCompletionTest::testSignalCompletion()
{
QFETCH(QByteArray, customContents);
QFETCH(QByteArrayList, hits);
const QByteArray contents = makeSignalCompletionContents(customContents);
const ProjectLessCompletionTest t("signalcompletion.cpp", {}, {}, contents);
QVERIFY(t.proposal);
QCOMPARE(t.proposal->size(), hits.size());
for (const QByteArray &hit : qAsConst(hits))
QVERIFY(hasItem(t.proposal, hit));
}
} // namespace Tests
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -1,70 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#pragma once
#include <QObject>
namespace ClangCodeModel {
namespace Internal {
namespace Tests {
class ActivateClangModelManagerSupport;
class ClangCodeCompletionTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void testCompleteDoxygenKeywords();
void testCompletePreprocessorKeywords();
void testCompleteIncludeDirective();
void testCompleteGlobals();
void testCompleteMembers();
void testCompleteMembersFromInside();
void testCompleteMembersFromOutside();
void testCompleteMembersFromFriend();
void testCompleteFunctions();
void testCompleteOverloads();
void testCompleteConstructor();
void testCompleteClassAndConstructor();
void testCompleteWithDotToArrowCorrection();
void testDontCompleteWithDotToArrowCorrectionForFloats();
void testCompleteProjectDependingCode();
void testCompleteProjectDependingCodeAfterChangingProject();
void testCompleteProjectDependingCodeInGeneratedUiFile();
void testSignalCompletion_data();
void testSignalCompletion();
};
} // namespace Tests
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -25,7 +25,6 @@
#include "clangdtests.h" #include "clangdtests.h"
#include "clangautomationutils.h"
#include "clangbatchfileprocessor.h" #include "clangbatchfileprocessor.h"
#include "../clangdclient.h" #include "../clangdclient.h"
#include "../clangmodelmanagersupport.h" #include "../clangmodelmanagersupport.h"
@@ -83,6 +82,11 @@ namespace ClangCodeModel {
namespace Internal { namespace Internal {
namespace Tests { namespace Tests {
static QString qrcPath(const QByteArray &relativeFilePath)
{
return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath);
}
ClangdTest::~ClangdTest() ClangdTest::~ClangdTest()
{ {
EditorManager::closeAllEditors(false); EditorManager::closeAllEditors(false);

View File

@@ -1653,12 +1653,12 @@ void CppModelManager::activateClangCodeModel(
CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const
{ {
return d->m_activeModelManagerSupport->completionAssistProvider(); return d->m_builtinModelManagerSupport->completionAssistProvider();
} }
CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const
{ {
return d->m_activeModelManagerSupport->functionHintAssistProvider(); return d->m_builtinModelManagerSupport->functionHintAssistProvider();
} }
TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const

View File

@@ -174,14 +174,12 @@ extend_qtc_test(unittest
DEFINES CLANG_UNIT_TESTS DEFINES CLANG_UNIT_TESTS
DEPENDS libclang DEPENDS libclang
SOURCES SOURCES
activationsequencecontextprocessor-test.cpp
activationsequenceprocessor-test.cpp activationsequenceprocessor-test.cpp
chunksreportedmonitor.cpp chunksreportedmonitor.cpp
clangasyncjob-base.cpp clangasyncjob-base.cpp
clangcodecompleteresults-test.cpp clangcodecompleteresults-test.cpp
clangcodemodelserver-test.cpp clangcodemodelserver-test.cpp
clangcompletecodejob-test.cpp clangcompletecodejob-test.cpp
clangcompletioncontextanalyzer-test.cpp
clangdocumentprocessors-test.cpp clangdocumentprocessors-test.cpp
clangdocumentprocessor-test.cpp clangdocumentprocessor-test.cpp
clangdocuments-test.cpp clangdocuments-test.cpp
@@ -205,7 +203,6 @@ extend_qtc_test(unittest
clangupdateannotationsjob-test.cpp clangupdateannotationsjob-test.cpp
codecompleter-test.cpp codecompleter-test.cpp
codecompletionsextractor-test.cpp codecompletionsextractor-test.cpp
completionchunkstotextconverter-test.cpp
cursor-test.cpp cursor-test.cpp
diagnosticset-test.cpp diagnosticset-test.cpp
diagnostic-test.cpp diagnostic-test.cpp
@@ -415,10 +412,7 @@ extend_qtc_test(unittest DEPENDS Utils CPlusPlus ClangSupport)
extend_qtc_test(unittest extend_qtc_test(unittest
SOURCES_PREFIX ../../../src/plugins/clangcodemodel SOURCES_PREFIX ../../../src/plugins/clangcodemodel
SOURCES SOURCES
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
clangcompletionchunkstotextconverter.cpp clangcompletionchunkstotextconverter.h
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
clangfixitoperation.cpp clangfixitoperation.h clangfixitoperation.cpp clangfixitoperation.h
clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h
) )

View File

@@ -1,221 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "googletest.h"
#include <clangactivationsequencecontextprocessor.h>
#include <clangcodemodel/clangcompletionassistinterface.h>
#include <cplusplus/Token.h>
#include <QTextCursor>
#include <QTextDocument>
namespace {
using ContextProcessor = ClangCodeModel::Internal::ActivationSequenceContextProcessor;
using TextEditor::AssistInterface;
using ClangCodeModel::Internal::ClangCompletionAssistInterface;
TEST(ActivationSequenceContextProcessorSlowTest, TextCursorPosition)
{
ClangCompletionAssistInterface interface("foobar", 4);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.textCursor_forTestOnly().position(), 0);
}
TEST(ActivationSequenceContextProcessor, StringLiteral)
{
ClangCompletionAssistInterface interface("auto foo = \"bar\"", 12);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, EndOfStringLiteral)
{
ClangCompletionAssistInterface interface("auto foo = \"bar\"", 16);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, FunctionCallComma)
{
ClangCompletionAssistInterface interface("f(x, ", 4);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_COMMA);
}
TEST(ActivationSequenceContextProcessor, NonFunctionCallComma)
{
ClangCompletionAssistInterface interface("int x, ", 6);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, DoxygenComment)
{
ClangCompletionAssistInterface interface("//! @", 5);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_DOXY_COMMENT);
}
TEST(ActivationSequenceContextProcessor, NonDoxygenComment)
{
ClangCompletionAssistInterface interface("// @", 4);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, Comment)
{
ClangCompletionAssistInterface interface("//", 2);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, InsideALiteral)
{
ClangCompletionAssistInterface interface("\"foo\"", 2);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, ShlashInsideAString)
{
ClangCompletionAssistInterface interface("\"foo/bar\"", 5);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, ShlashOutsideAString)
{
ClangCompletionAssistInterface interface("foo/bar", 4);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, FunctionLeftParen)
{
ClangCompletionAssistInterface interface("foo(", 4);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_LPAREN);
}
TEST(ActivationSequenceContextProcessor, TemplateFunctionLeftParen)
{
ClangCompletionAssistInterface interface("foo<X>(", 7);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_LPAREN);
}
TEST(ActivationSequenceContextProcessor, TemplateFunctionSecondParameter)
{
ClangCompletionAssistInterface interface("foo<X>(", 7);
int startOfname = ContextProcessor::findStartOfName(interface.textDocument(),
6,
ContextProcessor::NameCategory::Function);
ASSERT_THAT(startOfname, 0);
}
TEST(ActivationSequenceContextProcessor, ExpressionLeftParen)
{
ClangCompletionAssistInterface interface("x * (", 5);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, AngleInclude)
{
ClangCompletionAssistInterface interface("#include <foo/bar>", 10);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_ANGLE_STRING_LITERAL);
}
TEST(ActivationSequenceContextProcessor, SlashInclude)
{
ClangCompletionAssistInterface interface("#include <foo/bar>", 14);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_SLASH);
}
TEST(ActivationSequenceContextProcessor, QuoteInclude)
{
ClangCompletionAssistInterface interface("#include \"foo/bar\"", 10);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_STRING_LITERAL);
}
TEST(ActivationSequenceContextProcessor, SlashInExlude)
{
ClangCompletionAssistInterface interface("#exclude <foo/bar>", 14);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, QuoteExclude)
{
ClangCompletionAssistInterface interface("#exclude \"foo/bar\"", 10);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_EOF_SYMBOL);
}
TEST(ActivationSequenceContextProcessor, SkipeWhiteSpacesBeforeCursor)
{
ClangCompletionAssistInterface interface("x-> ", 7);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_ARROW);
}
TEST(ActivationSequenceContextProcessor, SkipIdentifier)
{
ClangCompletionAssistInterface interface("x->foo_", 7);
ContextProcessor processor{&interface};
ASSERT_THAT(processor.completionKind(), CPlusPlus::T_ARROW);
}
}

View File

@@ -1,589 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "googletest.h"
#include <clangcodemodel/clangcompletioncontextanalyzer.h>
#include <clangcodemodel/clangcompletionassistinterface.h>
#include <utils/qtcassert.h>
#include <QTextDocument>
namespace ClangCodeModel {
namespace Internal {
void PrintTo(const ClangCompletionContextAnalyzer::CompletionAction &completionAction,
::std::ostream* os)
{
using CCA = ClangCompletionContextAnalyzer;
switch (completionAction) {
case CCA::PassThroughToLibClang: *os << "PassThroughToLibClang"; break;
case CCA::PassThroughToLibClangAfterLeftParen: *os << "PassThroughToLibClangAfterLeftParen"; break;
case CCA::CompleteDoxygenKeyword: *os << "CompleteDoxygenKeyword"; break;
case CCA::CompleteIncludePath: *os << "CompleteIncludePath"; break;
case CCA::CompletePreprocessorDirective: *os << "CompletePreprocessorDirective"; break;
case CCA::CompleteSignal: *os << "CompleteSignal"; break;
case CCA::CompleteSlot: *os << "CompleteSlot"; break;
case CCA::CompleteNone: *os << "CompleteNone"; break;
}
}
} // Internal
} // ClangCodeModel
namespace {
using ::testing::PrintToString;
using ClangCodeModel::Internal::ClangCompletionAssistInterface;
using CCA = ClangCodeModel::Internal::ClangCompletionContextAnalyzer;
class TestDocument
{
public:
TestDocument(const QByteArray &theSource)
: source(theSource),
position(theSource.lastIndexOf('@')) // Use 'lastIndexOf' due to doxygen: "//! @keyword"
{
source.remove(position, 1);
}
QByteArray source;
int position;
};
bool isPassThrough(CCA::CompletionAction completionAction)
{
return completionAction != CCA::PassThroughToLibClang
&& completionAction != CCA::PassThroughToLibClangAfterLeftParen;
}
MATCHER(IsPassThroughToClang, std::string(negation ? "isn't" : "is") + " passed through to Clang")
{
const auto completionAction = arg.completionAction();
if (isPassThrough(completionAction)) {
*result_listener << "completion action is " << PrintToString(completionAction);
return false;
}
return true;
}
// Offsets are relative to positionInText
MATCHER_P5(HasResult,
completionAction,
positionForClangOffset,
positionForProposalOffset,
positionInText,
addSnippets,
std::string(negation ? "hasn't" : "has")
+ " result of completion action " + PrintToString(completionAction)
+ " and offset for clang " + PrintToString(positionForClangOffset)
+ " and offset for proprosal " + PrintToString(positionForProposalOffset)
+ " and addSnippets " + PrintToString(addSnippets))
{
const int actualPositionForClangOffset = arg.positionForClang() - positionInText;
const int actualPositionForProposalOffset = arg.positionForProposal() - positionInText;
if (arg.completionAction() != completionAction
|| actualPositionForClangOffset != positionForClangOffset
|| actualPositionForProposalOffset != positionForProposalOffset
|| addSnippets != arg.addSnippets()) {
*result_listener << "completion action is " << PrintToString(arg.completionAction())
<< " and offset for clang is " << PrintToString(actualPositionForClangOffset)
<< " and offset for proprosal is " << PrintToString(actualPositionForProposalOffset)
<< " and addSnippets is " << PrintToString(arg.addSnippets());
return false;
}
return true;
}
// Offsets are relative to positionInText
MATCHER_P5(HasResultWithoutClangDifference,
completionAction,
positionForClangOffset,
positionForProposalOffset,
positionInText,
addSnippets,
std::string(negation ? "hasn't" : "has")
+ " result of completion action " + PrintToString(completionAction)
+ " and offset for clang " + PrintToString(positionForClangOffset)
+ " and offset for proprosal " + PrintToString(positionForProposalOffset)
+ " and addSnippets " + PrintToString(addSnippets))
{
const int actualPositionForProposalOffset = arg.positionForProposal() - positionInText;
if (arg.completionAction() != completionAction
|| arg.positionForClang() != positionForClangOffset
|| actualPositionForProposalOffset != positionForProposalOffset
|| addSnippets != arg.addSnippets()) {
*result_listener << "completion action is " << PrintToString(arg.completionAction())
<< " and offset for clang is " << PrintToString(arg.positionForClang())
<< " and offset for proprosal is " << PrintToString(actualPositionForProposalOffset)
<< " and addSnippets is " << PrintToString(arg.addSnippets());
return false;
}
return true;
}
class ClangCompletionContextAnalyzer : public ::testing::Test
{
protected:
CCA runAnalyzer(const char *text);
protected:
int positionInText = 0;
};
CCA ClangCompletionContextAnalyzer::runAnalyzer(const char *text)
{
const TestDocument testDocument(text);
ClangCompletionAssistInterface assistInterface(testDocument.source, testDocument.position);
CCA analyzer(&assistInterface, CPlusPlus::LanguageFeatures::defaultFeatures());
positionInText = testDocument.position;
analyzer.analyze();
return analyzer;
}
TEST_F(ClangCompletionContextAnalyzer, WordsBeforeCursor)
{
auto analyzer = runAnalyzer("foo bar@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterSpace)
{
auto analyzer = runAnalyzer("foo @");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterQualification)
{
auto analyzer = runAnalyzer(" Foo::@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfDotMember)
{
auto analyzer = runAnalyzer("o.mem@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfDotMemberWithSpaceInside)
{
auto analyzer = runAnalyzer("o. mem@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtBeginOfDotMember)
{
auto analyzer = runAnalyzer("o.@mem");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtBeginOfDotMemberWithSpaceInside)
{
auto analyzer = runAnalyzer("o. @mem");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfArrow)
{
auto analyzer = runAnalyzer("o->mem@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfArrowWithSpaceInside)
{
auto analyzer = runAnalyzer("o-> mem@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtBeginOfArrow)
{
auto analyzer = runAnalyzer("o->@mem");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AtBeginOfArrowWithSpaceInside)
{
auto analyzer = runAnalyzer("o-> @mem");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentOneAtCall)
{
auto analyzer = runAnalyzer("f(@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentTwoAtCall)
{
auto analyzer = runAnalyzer("f(1,@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, -2, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentTwoWithSpaceAtCall)
{
auto analyzer = runAnalyzer("f(1, @");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, WhitespaceAfterFunctionName)
{
auto analyzer = runAnalyzer("foo (@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ConstructorCallWithBraceInitializer)
{
auto analyzer = runAnalyzer("f{@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentTwoWithSpaceAtConstructorCallWithBraceInitializer)
{
auto analyzer = runAnalyzer("f{1, @");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, WhitespaceBeforeConstructorCallWithBraceInitializer)
{
auto analyzer = runAnalyzer("foo {@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, OpenFunctionScopeNotAConstructor)
{
auto analyzer = runAnalyzer("foo() {@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterOpeningParenthesis)
{
auto analyzer = runAnalyzer("(@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterOpeningBraceAndIdentifierOnNewLine)
{
auto analyzer = runAnalyzer("if (1) {\n"
"cla@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, -3, -3, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentOneAtSignal)
{
auto analyzer = runAnalyzer("SIGNAL(@");
ASSERT_THAT(analyzer, HasResult(CCA::CompleteSignal, 0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentOneWithLettersAtSignal)
{
auto analyzer = runAnalyzer("SIGNAL(foo@");
ASSERT_THAT(analyzer, HasResult(CCA::CompleteSignal, -3, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentOneAtSlot)
{
auto analyzer = runAnalyzer("SLOT(@");
ASSERT_THAT(analyzer, HasResult(CCA::CompleteSlot, -0, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, ArgumentOneWithLettersAtSlot)
{
auto analyzer = runAnalyzer("SLOT(foo@");
ASSERT_THAT(analyzer, HasResult(CCA::CompleteSlot, -3, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, DoxygenWithBackslash)
{
auto analyzer = runAnalyzer("//! \\@");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompleteDoxygenKeyword, -1, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, DoxygenWithAt)
{
auto analyzer = runAnalyzer("//! @@");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompleteDoxygenKeyword, -1, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, DoxygenWithParameter)
{
auto analyzer = runAnalyzer("//! \\par@");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompleteDoxygenKeyword, -1, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, Preprocessor)
{
auto analyzer = runAnalyzer("#@");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompletePreprocessorDirective, -1, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, PreprocessorIf)
{
auto analyzer = runAnalyzer("#if@");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompletePreprocessorDirective, -1, -2, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, LocalInclude)
{
auto analyzer = runAnalyzer("#include \"foo@\"");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompleteIncludePath, -1, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, GlobalInclude)
{
auto analyzer = runAnalyzer("#include <foo@>");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompleteIncludePath, -1, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, GlocalIncludeWithDirectory)
{
auto analyzer = runAnalyzer("#include <foo/@>");
ASSERT_THAT(analyzer, HasResultWithoutClangDifference(CCA::CompleteIncludePath, -1, 0, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, AfterQuote)
{
auto analyzer = runAnalyzer("\"@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, AfterSpaceQuote)
{
auto analyzer = runAnalyzer(" \"@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, AfterQuotedText)
{
auto analyzer = runAnalyzer("\"text\"@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, InQuotedText)
{
auto analyzer = runAnalyzer("\"hello cruel@ world\"");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, SingleQuote)
{
auto analyzer = runAnalyzer("'@'");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, AfterLetterInSingleQuoted)
{
auto analyzer = runAnalyzer("'a@'");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, CommaOperator)
{
auto analyzer = runAnalyzer("a = b,@\"");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, DoxygenMarkerInNonDoxygenComment)
{
auto analyzer = runAnalyzer("@@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, DoxygenMarkerInNonDoxygenComment2)
{
auto analyzer = runAnalyzer("\\@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, AtEndOfOneLineComment)
{
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, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterEmptyOneLineComment)
{
auto analyzer = runAnalyzer("//\n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterOneLineDoxygenComment1)
{
auto analyzer = runAnalyzer("/// comment\n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, AfterOneLineDoxygenComment2)
{
auto analyzer = runAnalyzer("//! comment \n"
"@");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText, true));
}
TEST_F(ClangCompletionContextAnalyzer, BeginEndComment)
{
auto analyzer = runAnalyzer("/* text@ */");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, Slash)
{
auto analyzer = runAnalyzer("5 /@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, LeftParen)
{
auto analyzer = runAnalyzer("(@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, TwoLeftParen)
{
auto analyzer = runAnalyzer("((@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, AsteriskLeftParen)
{
auto analyzer = runAnalyzer("*(@");
ASSERT_THAT(analyzer, IsPassThroughToClang());
}
TEST_F(ClangCompletionContextAnalyzer, TemplatedFunctionSecondArgument)
{
auto analyzer = runAnalyzer("f < decltype(bar -> member) > (1, @");
ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClangAfterLeftParen, 0, -3, positionInText, false));
}
TEST_F(ClangCompletionContextAnalyzer, FunctionNameStartPosition)
{
auto analyzer = runAnalyzer(" f<Bar>(1, @");
int functionNameStartPosition = analyzer.functionNameStart();
ASSERT_THAT(functionNameStartPosition, 1);
}
TEST_F(ClangCompletionContextAnalyzer, QualifiedFunctionNameStartPosition)
{
auto analyzer = runAnalyzer(" Namespace::f<Bar>(1, @");
int functionNameStartPosition = analyzer.functionNameStart();
ASSERT_THAT(functionNameStartPosition, 1);
}
TEST_F(ClangCompletionContextAnalyzer, SnippetsAfterOpeningBrace)
{
auto analyzer = runAnalyzer("{@");
ASSERT_TRUE(analyzer.addSnippets());
}
TEST_F(ClangCompletionContextAnalyzer, NoSnippetsAfterFunctionCallLike_OpeningBrace)
{
auto analyzer = runAnalyzer("foo{@");
ASSERT_FALSE(analyzer.addSnippets());
}
TEST_F(ClangCompletionContextAnalyzer, NoSnippetsAfterFunctionCallLike_OpeningParen)
{
auto analyzer = runAnalyzer("foo(@");
ASSERT_FALSE(analyzer.addSnippets());
}
} // namespace

View File

@@ -1,379 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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
** 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.
**
** 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.
**
****************************************************************************/
#include "googletest.h"
#include <codecompletionchunk.h>
#include <clangcompletionchunkstotextconverter.h>
namespace {
using ClangBackEnd::CodeCompletionChunk;
using ClangBackEnd::CodeCompletionChunks;
using Converter = ClangCodeModel::Internal::CompletionChunksToTextConverter;
class CompletionChunksToTextConverter : public ::testing::Test
{
protected:
Converter converter;
CodeCompletionChunk integerResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("int")};
CodeCompletionChunk templateResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("Foo<int>")};
CodeCompletionChunk enumerationResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("Enumeration")};
CodeCompletionChunk functionName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Function")};
CodeCompletionChunk namespaceName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Namespace")};
CodeCompletionChunk variableName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Variable")};
CodeCompletionChunk enumeratorName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumerator")};
CodeCompletionChunk enumerationName{CodeCompletionChunk::TypedText, Utf8StringLiteral("Enumeration")};
CodeCompletionChunk className{CodeCompletionChunk::TypedText, Utf8StringLiteral("Class")};
CodeCompletionChunk leftParen{CodeCompletionChunk::LeftParen, Utf8StringLiteral("(")};
CodeCompletionChunk rightParen{CodeCompletionChunk::RightParen, Utf8StringLiteral(")")};
CodeCompletionChunk comma{CodeCompletionChunk::Comma, Utf8StringLiteral(", ")};
CodeCompletionChunk semicolon{CodeCompletionChunk::SemiColon, Utf8StringLiteral(";")};
CodeCompletionChunk colonColonText{CodeCompletionChunk::Text, Utf8StringLiteral("::")};
CodeCompletionChunk functionArgumentX{CodeCompletionChunk::Placeholder, Utf8StringLiteral("char x")};
CodeCompletionChunk functionArgumentXAsCurrentParameter{CodeCompletionChunk::CurrentParameter, Utf8StringLiteral("char x")};
CodeCompletionChunk functionArgumentY{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int y")};
CodeCompletionChunk functionArgumentYAsCurrentParamter{CodeCompletionChunk::CurrentParameter, Utf8StringLiteral("int y")};
CodeCompletionChunk functionArgumentZ{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int z")};
CodeCompletionChunk functionArgumentTemplate{CodeCompletionChunk::Placeholder, Utf8StringLiteral("const Foo<int> &foo")};
CodeCompletionChunk switchName{CodeCompletionChunk::TypedText, Utf8StringLiteral("switch")};
CodeCompletionChunk condition{CodeCompletionChunk::Placeholder, Utf8StringLiteral("condition")};
CodeCompletionChunk leftBrace{CodeCompletionChunk::LeftBrace, Utf8StringLiteral("{")};
CodeCompletionChunk rightBrace{CodeCompletionChunk::RightBrace, Utf8StringLiteral("}")};
CodeCompletionChunk verticalSpace{CodeCompletionChunk::VerticalSpace, Utf8StringLiteral("\n")};
CodeCompletionChunk throwName{CodeCompletionChunk::TypedText, Utf8StringLiteral("throw")};
CodeCompletionChunk voidResultType{CodeCompletionChunk::ResultType, Utf8StringLiteral("void")};
CodeCompletionChunk forName{CodeCompletionChunk::TypedText, Utf8StringLiteral("for")};
CodeCompletionChunk initStatement{CodeCompletionChunk::Placeholder, Utf8StringLiteral("init-statement")};
CodeCompletionChunk initExpression{CodeCompletionChunk::Placeholder, Utf8StringLiteral("init-expression")};
CodeCompletionChunk statements{CodeCompletionChunk::Placeholder, Utf8StringLiteral("statements")};
CodeCompletionChunk constCastName{CodeCompletionChunk::TypedText, Utf8StringLiteral("const_cast")};
CodeCompletionChunk leftAngle{CodeCompletionChunk::LeftAngle, Utf8StringLiteral("<")};
CodeCompletionChunk rightAngle{CodeCompletionChunk::RightAngle, Utf8StringLiteral(">")};
CodeCompletionChunk elseName{CodeCompletionChunk::TypedText, Utf8StringLiteral("else")};
CodeCompletionChunk ifName{CodeCompletionChunk::TypedText, Utf8StringLiteral("if")};
CodeCompletionChunk horizontalSpace{CodeCompletionChunk::HorizontalSpace, Utf8StringLiteral(" ")};
CodeCompletionChunk enableIfT{CodeCompletionChunk::TypedText, Utf8StringLiteral("enable_if_t")};
CodeCompletionChunk enableIfTCondition{CodeCompletionChunk::Placeholder, Utf8StringLiteral("_Cond")};
CodeCompletionChunk optionalEnableIfTType{CodeCompletionChunk::Placeholder, Utf8StringLiteral("_Tp"), true};
CodeCompletionChunk optionalComma{CodeCompletionChunk::Comma, Utf8StringLiteral(", "), true};
CodeCompletionChunk optionalFunctionArgumentY{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int y"), true};
CodeCompletionChunk optionalFunctionArgumentYAsCurrentParameter{CodeCompletionChunk::CurrentParameter, Utf8StringLiteral("int y"), true};
CodeCompletionChunk optionalFunctionArgumentZ{CodeCompletionChunk::Placeholder, Utf8StringLiteral("int z"), true};
};
TEST_F(CompletionChunksToTextConverter, ParseIsClearingText)
{
CodeCompletionChunks completionChunks({integerResultType, functionName, leftParen, rightParen});
converter.setAddResultType(true);
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("int Function()"));
}
TEST_F(CompletionChunksToTextConverter, ConvertFunction)
{
CodeCompletionChunks completionChunks({integerResultType, functionName, leftParen, rightParen});
converter.setAddResultType(true);
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("int Function()"));
}
TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithParameters)
{
CodeCompletionChunks completionChunks({integerResultType, functionName, leftParen, functionArgumentX,rightParen});
converter.setAddResultType(true);
converter.setAddPlaceHolderText(true);
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("int Function(char x)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertToFunctionSignatureWithOneArgument)
{
CodeCompletionChunks completionChunks({integerResultType,
functionName,
leftParen,
functionArgumentXAsCurrentParameter,
rightParen});
ASSERT_THAT(converter.convertToFunctionSignatureWithHtml(
completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind),
QStringLiteral("int Function(char x)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertToFunctionSignatureWithOneParameterThatIsActive)
{
CodeCompletionChunks completionChunks({integerResultType,
functionName,
leftParen,
functionArgumentXAsCurrentParameter,
rightParen});
ASSERT_THAT(converter.convertToFunctionSignatureWithHtml(
completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind,
1),
QStringLiteral("int Function(<b>char x</b>)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertToFunctionSignatureWithTwoParametersWhereOneIsActive)
{
CodeCompletionChunks completionChunks({integerResultType,
functionName,
leftParen,
functionArgumentX,
comma,
functionArgumentYAsCurrentParamter,
rightParen});
ASSERT_THAT(converter.convertToFunctionSignatureWithHtml(
completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind,
2),
QStringLiteral("int Function(char x, <b>int y</b>)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertToFunctionSignatureWithTwoParametersWhereOneIsOptionalAndActive)
{
CodeCompletionChunks completionChunks({integerResultType,
functionName,
leftParen,
functionArgumentX,
optionalComma,
optionalFunctionArgumentYAsCurrentParameter,
rightParen});
ASSERT_THAT(converter.convertToFunctionSignatureWithHtml(
completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind,
2),
QStringLiteral("int Function(char x<i>, <b>int y</b></i>)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertToFunctionSignatureWithTemplateReturnType)
{
CodeCompletionChunks completionChunks({templateResultType,
functionName,
leftParen,
functionArgumentX,
rightParen});
using ClangCodeModel::Internal::CompletionChunksToTextConverter;
ASSERT_THAT(CompletionChunksToTextConverter::convertToFunctionSignatureWithHtml(
completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind),
QStringLiteral("Foo&lt;int&gt; Function(char x)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertToFunctionSignatureWithTemplateArgument)
{
CodeCompletionChunks completionChunks({integerResultType,
functionName,
leftParen,
functionArgumentTemplate,
rightParen});
using ClangCodeModel::Internal::CompletionChunksToTextConverter;
ASSERT_THAT(CompletionChunksToTextConverter::convertToFunctionSignatureWithHtml(
completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind),
QStringLiteral("int Function(const Foo&lt;int&gt; &amp;foo)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertFunctionWithOptionalParameter)
{
CodeCompletionChunks completionChunks({integerResultType,
functionName,
leftParen,
functionArgumentX,
optionalComma,
optionalFunctionArgumentY,
optionalComma,
optionalFunctionArgumentZ,
rightParen});
ASSERT_THAT(Converter::convertToToolTipWithHtml(completionChunks,
ClangBackEnd::CodeCompletion::FunctionCompletionKind),
QStringLiteral("int Function(char x<i>, int y, int z</i>)"));
}
TEST_F(CompletionChunksToTextConverter, ConvertVariable)
{
CodeCompletionChunks completionChunks({integerResultType, variableName});
converter.setAddResultType(true);
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("int Variable"));
}
TEST_F(CompletionChunksToTextConverter, Enumerator)
{
CodeCompletionChunks completionChunks({enumerationResultType, enumeratorName});
converter.setAddResultType(true);
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("Enumeration Enumerator"));
}
TEST_F(CompletionChunksToTextConverter, Enumeration)
{
CodeCompletionChunks completionChunks({className});
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("Class"));
}
TEST_F(CompletionChunksToTextConverter, SwitchAsKeyword)
{
CodeCompletionChunks completionChunks({switchName,
leftParen,
condition,
rightParen,
leftBrace,
verticalSpace,
rightBrace});
converter.setupForKeywords();
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("switch () {\n\n}"));
ASSERT_THAT(converter.placeholderPositions().at(0), 8);
}
TEST_F(CompletionChunksToTextConverter, SwitchAsName)
{
CodeCompletionChunks completionChunks({switchName,
leftParen,
condition,
rightParen,
leftBrace,
verticalSpace,
rightBrace});
const QString text = ClangCodeModel::Internal::CompletionChunksToTextConverter::convertToName(
completionChunks);
ASSERT_THAT(text, QStringLiteral("switch(){}"));
}
TEST_F(CompletionChunksToTextConverter, For)
{
CodeCompletionChunks completionChunks({forName,
leftParen,
initStatement,
semicolon,
initExpression,
semicolon,
condition,
rightParen,
leftBrace,
verticalSpace,
statements,
verticalSpace,
rightBrace});
converter.setupForKeywords();
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("for (;;) {\n\n}"));
}
TEST_F(CompletionChunksToTextConverter, const_cast)
{
CodeCompletionChunks completionChunks({constCastName,
leftAngle,
rightAngle,
leftParen,
rightParen});
converter.setupForKeywords();
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("const_cast<>()"));
}
TEST_F(CompletionChunksToTextConverter, Throw)
{
CodeCompletionChunks completionChunks({voidResultType, throwName});
auto completionName = Converter::convertToName(completionChunks);
ASSERT_THAT(completionName, QStringLiteral("throw"));
}
TEST_F(CompletionChunksToTextConverter, ElseIf)
{
CodeCompletionChunks completionChunks({elseName,
horizontalSpace,
ifName,
horizontalSpace,
leftBrace,
verticalSpace,
statements,
verticalSpace,
rightBrace});
converter.setupForKeywords();
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("else if {\n\n}"));
}
TEST_F(CompletionChunksToTextConverter, EnableIfT)
{
CodeCompletionChunks completionChunks({enableIfT,
leftAngle,
enableIfTCondition,
optionalComma,
optionalEnableIfTType,
rightAngle});
converter.setupForKeywords();
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("enable_if_t<>"));
}
TEST_F(CompletionChunksToTextConverter, Namespace)
{
CodeCompletionChunks completionChunks({namespaceName, colonColonText});
converter.parseChunks(completionChunks);
ASSERT_THAT(converter.text(), QStringLiteral("Namespace::"));
}
}

View File

@@ -193,7 +193,6 @@ Project {
name: "libclang tests" name: "libclang tests"
condition: libclang.present && (!qbs.targetOS.contains("windows") || libclang.llvmBuildModeMatches) condition: libclang.present && (!qbs.targetOS.contains("windows") || libclang.llvmBuildModeMatches)
files: [ files: [
"activationsequencecontextprocessor-test.cpp",
"activationsequenceprocessor-test.cpp", "activationsequenceprocessor-test.cpp",
"chunksreportedmonitor.cpp", "chunksreportedmonitor.cpp",
"chunksreportedmonitor.h", "chunksreportedmonitor.h",
@@ -203,7 +202,6 @@ Project {
"clangcodemodelserver-test.cpp", "clangcodemodelserver-test.cpp",
"clangcompareoperators.h", "clangcompareoperators.h",
"clangcompletecodejob-test.cpp", "clangcompletecodejob-test.cpp",
"clangcompletioncontextanalyzer-test.cpp",
"clangdocument-test.cpp", "clangdocument-test.cpp",
"clangdocumentprocessor-test.cpp", "clangdocumentprocessor-test.cpp",
"clangdocumentprocessors-test.cpp", "clangdocumentprocessors-test.cpp",
@@ -225,7 +223,6 @@ Project {
"clangupdateannotationsjob-test.cpp", "clangupdateannotationsjob-test.cpp",
"codecompleter-test.cpp", "codecompleter-test.cpp",
"codecompletionsextractor-test.cpp", "codecompletionsextractor-test.cpp",
"completionchunkstotextconverter-test.cpp",
"cursor-test.cpp", "cursor-test.cpp",
"diagnostic-test.cpp", "diagnostic-test.cpp",
"diagnosticcontainer-matcher.h", "diagnosticcontainer-matcher.h",
@@ -392,14 +389,8 @@ Project {
name: "sources from clangcodemodel" name: "sources from clangcodemodel"
prefix: "../../../src/plugins/clangcodemodel/" prefix: "../../../src/plugins/clangcodemodel/"
files: [ files: [
"clangactivationsequencecontextprocessor.cpp",
"clangactivationsequencecontextprocessor.h",
"clangactivationsequenceprocessor.cpp", "clangactivationsequenceprocessor.cpp",
"clangactivationsequenceprocessor.h", "clangactivationsequenceprocessor.h",
"clangcompletionchunkstotextconverter.cpp",
"clangcompletionchunkstotextconverter.h",
"clangcompletioncontextanalyzer.cpp",
"clangcompletioncontextanalyzer.h",
"clangfixitoperation.cpp", "clangfixitoperation.cpp",
"clangfixitoperation.h", "clangfixitoperation.h",
"clanguiheaderondiskmanager.cpp", "clanguiheaderondiskmanager.cpp",