forked from qt-creator/qt-creator
ClangCodeModel: Remove libclang-based completion and function hints
Change-Id: I742fb14b1aba3ba1f35a5c80bf553d2a735cac48 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -11,17 +11,11 @@ add_qtc_plugin(ClangCodeModel
|
||||
SOURCES
|
||||
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
|
||||
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
|
||||
clangassistproposalitem.cpp clangassistproposalitem.h
|
||||
clangassistproposalmodel.cpp clangassistproposalmodel.h
|
||||
clangbackendcommunicator.cpp clangbackendcommunicator.h
|
||||
clangbackendlogging.cpp clangbackendlogging.h
|
||||
clangbackendreceiver.cpp clangbackendreceiver.h
|
||||
clangbackendsender.cpp clangbackendsender.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
|
||||
clangconstants.h
|
||||
clangdclient.cpp clangdclient.h
|
||||
@@ -32,7 +26,6 @@ add_qtc_plugin(ClangCodeModel
|
||||
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
|
||||
clangfixitoperation.cpp clangfixitoperation.h
|
||||
clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h
|
||||
clangfunctionhintmodel.cpp clangfunctionhintmodel.h
|
||||
clangdlocatorfilters.cpp clangdlocatorfilters.h
|
||||
clangmodelmanagersupport.cpp clangmodelmanagersupport.h
|
||||
clangpreprocessorassistproposalitem.cpp clangpreprocessorassistproposalitem.h
|
||||
@@ -57,9 +50,7 @@ extend_qtc_plugin(ClangCodeModel
|
||||
extend_qtc_plugin(ClangCodeModel
|
||||
CONDITION WITH_TESTS
|
||||
SOURCES
|
||||
test/clangautomationutils.cpp test/clangautomationutils.h
|
||||
test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h
|
||||
test/clangcodecompletion_test.cpp test/clangcodecompletion_test.h
|
||||
test/clangdtests.cpp test/clangdtests.h
|
||||
test/data/clangtestdata.qrc
|
||||
)
|
||||
|
@@ -52,13 +52,6 @@ ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
|
||||
process();
|
||||
}
|
||||
|
||||
ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
|
||||
const ClangCompletionAssistInterface *interface)
|
||||
: ActivationSequenceContextProcessor(interface->textDocument(), interface->position(),
|
||||
interface->languageFeatures())
|
||||
{
|
||||
}
|
||||
|
||||
CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const
|
||||
{
|
||||
return m_completionKind;
|
||||
|
@@ -25,8 +25,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <clangcodemodel/clangcompletionassistinterface.h>
|
||||
|
||||
#include <cplusplus/Token.h>
|
||||
|
||||
#include <QTextCursor>
|
||||
@@ -43,7 +41,6 @@ class ActivationSequenceContextProcessor
|
||||
public:
|
||||
ActivationSequenceContextProcessor(QTextDocument *document, int position,
|
||||
CPlusPlus::LanguageFeatures languageFeatures);
|
||||
ActivationSequenceContextProcessor(const ClangCompletionAssistInterface *interface);
|
||||
|
||||
CPlusPlus::Kind completionKind() const;
|
||||
int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>"
|
||||
|
@@ -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
|
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
@@ -26,7 +26,6 @@
|
||||
#include "clangbackendcommunicator.h"
|
||||
|
||||
#include "clangbackendlogging.h"
|
||||
#include "clangcompletionassistprocessor.h"
|
||||
#include "clangmodelmanagersupport.h"
|
||||
#include "clangutils.h"
|
||||
|
||||
@@ -99,8 +98,6 @@ BackendCommunicator::BackendCommunicator()
|
||||
|
||||
m_receiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); });
|
||||
|
||||
connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
|
||||
this, &BackendCommunicator::onEditorAboutToClose);
|
||||
connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
|
||||
this, &BackendCommunicator::setupDummySender);
|
||||
auto globalFCB = GlobalFileChangeBlocker::instance();
|
||||
@@ -192,11 +189,6 @@ void BackendCommunicator::documentVisibilityChanged()
|
||||
visibleCppEditorDocumentsFilePaths());
|
||||
}
|
||||
|
||||
bool BackendCommunicator::isNotWaitingForCompletion() const
|
||||
{
|
||||
return !m_receiver.isExpectingCompletionsMessage();
|
||||
}
|
||||
|
||||
void BackendCommunicator::setBackendJobsPostponed(bool postponed)
|
||||
{
|
||||
if (postponed) {
|
||||
@@ -349,12 +341,6 @@ void BackendCommunicator::onConnectedToBackend()
|
||||
initializeBackendWithCurrentData();
|
||||
}
|
||||
|
||||
void BackendCommunicator::onEditorAboutToClose(Core::IEditor *editor)
|
||||
{
|
||||
if (auto *textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor))
|
||||
m_receiver.deleteProcessorsOfEditorWidget(textEditor->editorWidget());
|
||||
}
|
||||
|
||||
void BackendCommunicator::setupDummySender()
|
||||
{
|
||||
m_sender.reset(new DummyBackendSender);
|
||||
@@ -443,26 +429,5 @@ void BackendCommunicator::unsavedFilesRemoved(const FileContainers &fileContaine
|
||||
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 ClangCodeModel
|
||||
|
@@ -48,8 +48,6 @@ namespace TextEditor { class IAssistProcessor; }
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
class ClangCompletionAssistProcessor;
|
||||
|
||||
class BackendCommunicator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -84,16 +82,7 @@ public:
|
||||
void unsavedFilesUpdatedFromCppEditorDocument(const QString &filePath);
|
||||
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);
|
||||
bool isNotWaitingForCompletion() const;
|
||||
|
||||
void setBackendJobsPostponed(bool postponed);
|
||||
|
||||
@@ -107,7 +96,6 @@ private:
|
||||
void setupDummySender();
|
||||
|
||||
void onConnectedToBackend();
|
||||
void onEditorAboutToClose(Core::IEditor *editor);
|
||||
|
||||
void logExecutableDoesNotExist();
|
||||
void logRestartedDueToUnexpectedFinish();
|
||||
|
@@ -27,7 +27,6 @@
|
||||
|
||||
#include "clangbackendlogging.h"
|
||||
|
||||
#include "clangcompletionassistprocessor.h"
|
||||
#include "clangeditordocumentprocessor.h"
|
||||
|
||||
#include <clangsupport/clangcodemodelclientmessages.h>
|
||||
@@ -71,55 +70,8 @@ void BackendReceiver::setAliveHandler(const BackendReceiver::AliveHandler &handl
|
||||
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()
|
||||
{
|
||||
// Clean up waiting assist processors
|
||||
for (ClangCompletionAssistProcessor *processor : m_assistProcessorsTable)
|
||||
processor->setAsyncProposalAvailable(nullptr);
|
||||
m_assistProcessorsTable.clear();
|
||||
|
||||
// Clean up futures for references; TODO: Remove duplication
|
||||
for (ReferencesEntry &entry : m_referencesTable) {
|
||||
entry.futureInterface.cancel();
|
||||
@@ -155,10 +107,6 @@ void BackendReceiver::completions(const ClangBackEnd::CompletionsMessage &messag
|
||||
{
|
||||
qCDebugIpc() << "CompletionsMessage with" << message.codeCompletions.size()
|
||||
<< "items";
|
||||
|
||||
const quint64 ticket = message.ticketNumber;
|
||||
if (ClangCompletionAssistProcessor *processor = m_assistProcessorsTable.take(ticket))
|
||||
processor->handleAvailableCompletions(message.codeCompletions);
|
||||
}
|
||||
|
||||
void BackendReceiver::annotations(const ClangBackEnd::AnnotationsMessage &message)
|
||||
|
@@ -43,8 +43,6 @@ class TextEditorWidget;
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
class ClangCompletionAssistProcessor;
|
||||
|
||||
class BackendReceiver : public ClangBackEnd::ClangCodeModelClientInterface
|
||||
{
|
||||
public:
|
||||
@@ -54,12 +52,6 @@ public:
|
||||
using AliveHandler = std::function<void ()>;
|
||||
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();
|
||||
|
||||
private:
|
||||
@@ -74,7 +66,6 @@ private:
|
||||
|
||||
private:
|
||||
AliveHandler m_aliveHandler;
|
||||
QHash<quint64, ClangCompletionAssistProcessor *> m_assistProcessorsTable;
|
||||
|
||||
struct ReferencesEntry {
|
||||
ReferencesEntry() = default;
|
||||
|
@@ -29,10 +29,6 @@ QtcPlugin {
|
||||
"clangactivationsequencecontextprocessor.h",
|
||||
"clangactivationsequenceprocessor.cpp",
|
||||
"clangactivationsequenceprocessor.h",
|
||||
"clangassistproposalitem.cpp",
|
||||
"clangassistproposalitem.h",
|
||||
"clangassistproposalmodel.cpp",
|
||||
"clangassistproposalmodel.h",
|
||||
"clangbackendcommunicator.cpp",
|
||||
"clangbackendcommunicator.h",
|
||||
"clangbackendlogging.cpp",
|
||||
@@ -43,14 +39,6 @@ QtcPlugin {
|
||||
"clangbackendsender.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",
|
||||
"clangconstants.h",
|
||||
@@ -72,8 +60,6 @@ QtcPlugin {
|
||||
"clangfixitoperation.h",
|
||||
"clangfixitoperationsextractor.cpp",
|
||||
"clangfixitoperationsextractor.h",
|
||||
"clangfunctionhintmodel.cpp",
|
||||
"clangfunctionhintmodel.h",
|
||||
"clangmodelmanagersupport.cpp",
|
||||
"clangmodelmanagersupport.h",
|
||||
"clangpreprocessorassistproposalitem.cpp",
|
||||
@@ -121,12 +107,8 @@ QtcPlugin {
|
||||
condition: qtc.testsEnabled
|
||||
prefix: "test/"
|
||||
files: [
|
||||
"clangautomationutils.cpp",
|
||||
"clangautomationutils.h",
|
||||
"clangbatchfileprocessor.cpp",
|
||||
"clangbatchfileprocessor.h",
|
||||
"clangcodecompletion_test.cpp",
|
||||
"clangcodecompletion_test.h",
|
||||
"clangdtests.cpp",
|
||||
"clangdtests.h",
|
||||
"data/clangtestdata.qrc",
|
||||
|
@@ -31,7 +31,6 @@
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
# include "test/clangbatchfileprocessor.h"
|
||||
# include "test/clangcodecompletion_test.h"
|
||||
# include "test/clangdtests.h"
|
||||
#endif
|
||||
|
||||
@@ -209,7 +208,6 @@ void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const
|
||||
QVector<QObject *> ClangCodeModelPlugin::createTestObjects() const
|
||||
{
|
||||
return {
|
||||
new Tests::ClangCodeCompletionTest,
|
||||
new Tests::ClangdTestCompletion,
|
||||
new Tests::ClangdTestExternalChanges,
|
||||
new Tests::ClangdTestFindReferences,
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
@@ -39,6 +39,7 @@
|
||||
#include <QDebug>
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocument>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
|
||||
@@ -65,15 +66,6 @@ bool isTokenForPassThrough(unsigned tokenKind)
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
|
||||
const ClangCompletionAssistInterface *assistInterface,
|
||||
CPlusPlus::LanguageFeatures languageFeatures)
|
||||
: ClangCompletionContextAnalyzer(assistInterface->textDocument(), assistInterface->position(),
|
||||
assistInterface->type() == CompletionType::FunctionHint,
|
||||
languageFeatures)
|
||||
{
|
||||
}
|
||||
|
||||
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
|
||||
QTextDocument *document, int position, bool isFunctionHint,
|
||||
CPlusPlus::LanguageFeatures languageFeatures)
|
||||
|
@@ -38,14 +38,9 @@ namespace TextEditor { class AssistInterface; }
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
class ClangCompletionAssistInterface;
|
||||
|
||||
class ClangCompletionContextAnalyzer
|
||||
{
|
||||
public:
|
||||
ClangCompletionContextAnalyzer() = delete;
|
||||
ClangCompletionContextAnalyzer(const ClangCompletionAssistInterface *assistInterface,
|
||||
CPlusPlus::LanguageFeatures languageFeatures);
|
||||
ClangCompletionContextAnalyzer(QTextDocument *document, int position, bool isFunctionHint,
|
||||
CPlusPlus::LanguageFeatures languageFeatures);
|
||||
void analyze();
|
||||
|
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "clangdclient.h"
|
||||
|
||||
#include "clangcompletionassistprocessor.h"
|
||||
#include "clangcompletioncontextanalyzer.h"
|
||||
#include "clangconstants.h"
|
||||
#include "clangdqpropertyhighlighter.h"
|
||||
@@ -903,8 +902,7 @@ private:
|
||||
= projectPartForFile(interface->filePath().toString());
|
||||
if (projectPart)
|
||||
headerPaths = projectPart->headerPaths;
|
||||
completions = ClangCompletionAssistProcessor::completeInclude(
|
||||
m_endPos, m_completionOperator, interface, headerPaths);
|
||||
completions = completeInclude(m_endPos, m_completionOperator, interface, headerPaths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -927,6 +925,107 @@ private:
|
||||
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;
|
||||
const int m_position;
|
||||
const int m_endPos;
|
||||
|
@@ -153,7 +153,7 @@ CppEditor::SemanticInfo ClangEditorDocumentProcessor::recalculateSemanticInfo()
|
||||
|
||||
CppEditor::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser()
|
||||
{
|
||||
return m_parser;
|
||||
return m_builtinProcessor.parser();
|
||||
}
|
||||
|
||||
CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot()
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -104,10 +104,7 @@ static const QList<TextEditor::TextDocument *> allCppDocuments()
|
||||
return Utils::qobject_container_cast<TextEditor::TextDocument *>(documents);
|
||||
}
|
||||
|
||||
ClangModelManagerSupport::ClangModelManagerSupport()
|
||||
: m_completionAssistProvider(m_communicator, CompletionType::Other)
|
||||
, m_functionHintAssistProvider(m_communicator, CompletionType::FunctionHint)
|
||||
, m_refactoringEngine(new RefactoringEngine)
|
||||
ClangModelManagerSupport::ClangModelManagerSupport() : m_refactoringEngine(new RefactoringEngine)
|
||||
{
|
||||
QTC_CHECK(!m_instance);
|
||||
m_instance = this;
|
||||
@@ -175,12 +172,12 @@ ClangModelManagerSupport::~ClangModelManagerSupport()
|
||||
|
||||
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::completionAssistProvider()
|
||||
{
|
||||
return &m_completionAssistProvider;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAssistProvider()
|
||||
{
|
||||
return &m_functionHintAssistProvider;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clangcompletionassistprovider.h"
|
||||
#include "clangbackendcommunicator.h"
|
||||
#include "clanguiheaderondiskmanager.h"
|
||||
|
||||
#include <cppeditor/cppmodelmanagersupport.h>
|
||||
@@ -143,8 +143,6 @@ private:
|
||||
|
||||
UiHeaderOnDiskManager m_uiHeaderOnDiskManager;
|
||||
BackendCommunicator m_communicator;
|
||||
ClangCompletionAssistProvider m_completionAssistProvider;
|
||||
ClangCompletionAssistProvider m_functionHintAssistProvider;
|
||||
std::unique_ptr<CppEditor::RefactoringEngineInterface> m_refactoringEngine;
|
||||
|
||||
QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings;
|
||||
|
@@ -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
|
@@ -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
|
@@ -25,8 +25,6 @@
|
||||
|
||||
#include "clangbatchfileprocessor.h"
|
||||
|
||||
#include "clangautomationutils.h"
|
||||
|
||||
#include <clangcodemodel/clangeditordocumentprocessor.h>
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
@@ -442,45 +440,6 @@ Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments,
|
||||
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
|
||||
{
|
||||
public:
|
||||
@@ -687,7 +646,6 @@ BatchFileParser::BatchFileParser(const QString &filePath,
|
||||
m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse);
|
||||
m_commandParsers.insert("setCursor", &SetCursorCommand::parse);
|
||||
m_commandParsers.insert("insertText", &InsertTextCommand::parse);
|
||||
m_commandParsers.insert("complete", &CompleteCommand::parse);
|
||||
m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse);
|
||||
}
|
||||
|
||||
|
@@ -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 ¯os)
|
||||
{
|
||||
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 ¯os)
|
||||
{
|
||||
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 &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<QString> *t)"));
|
||||
QVERIFY(hasItem(t.proposal, "TType<QString> 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 &s, int j)"));
|
||||
QVERIFY(hasItem(t2.proposal, "void func(const S &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
|
@@ -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
|
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "clangdtests.h"
|
||||
|
||||
#include "clangautomationutils.h"
|
||||
#include "clangbatchfileprocessor.h"
|
||||
#include "../clangdclient.h"
|
||||
#include "../clangmodelmanagersupport.h"
|
||||
@@ -83,6 +82,11 @@ namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
namespace Tests {
|
||||
|
||||
static QString qrcPath(const QByteArray &relativeFilePath)
|
||||
{
|
||||
return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath);
|
||||
}
|
||||
|
||||
ClangdTest::~ClangdTest()
|
||||
{
|
||||
EditorManager::closeAllEditors(false);
|
||||
|
@@ -1653,12 +1653,12 @@ void CppModelManager::activateClangCodeModel(
|
||||
|
||||
CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const
|
||||
{
|
||||
return d->m_activeModelManagerSupport->completionAssistProvider();
|
||||
return d->m_builtinModelManagerSupport->completionAssistProvider();
|
||||
}
|
||||
|
||||
CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const
|
||||
{
|
||||
return d->m_activeModelManagerSupport->functionHintAssistProvider();
|
||||
return d->m_builtinModelManagerSupport->functionHintAssistProvider();
|
||||
}
|
||||
|
||||
TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
|
||||
|
@@ -174,14 +174,12 @@ extend_qtc_test(unittest
|
||||
DEFINES CLANG_UNIT_TESTS
|
||||
DEPENDS libclang
|
||||
SOURCES
|
||||
activationsequencecontextprocessor-test.cpp
|
||||
activationsequenceprocessor-test.cpp
|
||||
chunksreportedmonitor.cpp
|
||||
clangasyncjob-base.cpp
|
||||
clangcodecompleteresults-test.cpp
|
||||
clangcodemodelserver-test.cpp
|
||||
clangcompletecodejob-test.cpp
|
||||
clangcompletioncontextanalyzer-test.cpp
|
||||
clangdocumentprocessors-test.cpp
|
||||
clangdocumentprocessor-test.cpp
|
||||
clangdocuments-test.cpp
|
||||
@@ -205,7 +203,6 @@ extend_qtc_test(unittest
|
||||
clangupdateannotationsjob-test.cpp
|
||||
codecompleter-test.cpp
|
||||
codecompletionsextractor-test.cpp
|
||||
completionchunkstotextconverter-test.cpp
|
||||
cursor-test.cpp
|
||||
diagnosticset-test.cpp
|
||||
diagnostic-test.cpp
|
||||
@@ -415,10 +412,7 @@ extend_qtc_test(unittest DEPENDS Utils CPlusPlus ClangSupport)
|
||||
extend_qtc_test(unittest
|
||||
SOURCES_PREFIX ../../../src/plugins/clangcodemodel
|
||||
SOURCES
|
||||
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
|
||||
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
|
||||
clangcompletionchunkstotextconverter.cpp clangcompletionchunkstotextconverter.h
|
||||
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
|
||||
clangfixitoperation.cpp clangfixitoperation.h
|
||||
clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h
|
||||
)
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
@@ -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<int> 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<int> &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::"));
|
||||
}
|
||||
|
||||
}
|
@@ -193,7 +193,6 @@ Project {
|
||||
name: "libclang tests"
|
||||
condition: libclang.present && (!qbs.targetOS.contains("windows") || libclang.llvmBuildModeMatches)
|
||||
files: [
|
||||
"activationsequencecontextprocessor-test.cpp",
|
||||
"activationsequenceprocessor-test.cpp",
|
||||
"chunksreportedmonitor.cpp",
|
||||
"chunksreportedmonitor.h",
|
||||
@@ -203,7 +202,6 @@ Project {
|
||||
"clangcodemodelserver-test.cpp",
|
||||
"clangcompareoperators.h",
|
||||
"clangcompletecodejob-test.cpp",
|
||||
"clangcompletioncontextanalyzer-test.cpp",
|
||||
"clangdocument-test.cpp",
|
||||
"clangdocumentprocessor-test.cpp",
|
||||
"clangdocumentprocessors-test.cpp",
|
||||
@@ -225,7 +223,6 @@ Project {
|
||||
"clangupdateannotationsjob-test.cpp",
|
||||
"codecompleter-test.cpp",
|
||||
"codecompletionsextractor-test.cpp",
|
||||
"completionchunkstotextconverter-test.cpp",
|
||||
"cursor-test.cpp",
|
||||
"diagnostic-test.cpp",
|
||||
"diagnosticcontainer-matcher.h",
|
||||
@@ -392,14 +389,8 @@ Project {
|
||||
name: "sources from clangcodemodel"
|
||||
prefix: "../../../src/plugins/clangcodemodel/"
|
||||
files: [
|
||||
"clangactivationsequencecontextprocessor.cpp",
|
||||
"clangactivationsequencecontextprocessor.h",
|
||||
"clangactivationsequenceprocessor.cpp",
|
||||
"clangactivationsequenceprocessor.h",
|
||||
"clangcompletionchunkstotextconverter.cpp",
|
||||
"clangcompletionchunkstotextconverter.h",
|
||||
"clangcompletioncontextanalyzer.cpp",
|
||||
"clangcompletioncontextanalyzer.h",
|
||||
"clangfixitoperation.cpp",
|
||||
"clangfixitoperation.h",
|
||||
"clanguiheaderondiskmanager.cpp",
|
||||
|
Reference in New Issue
Block a user