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
|
SOURCES
|
||||||
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
|
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
|
||||||
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
|
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
|
||||||
clangassistproposalitem.cpp clangassistproposalitem.h
|
|
||||||
clangassistproposalmodel.cpp clangassistproposalmodel.h
|
|
||||||
clangbackendcommunicator.cpp clangbackendcommunicator.h
|
clangbackendcommunicator.cpp clangbackendcommunicator.h
|
||||||
clangbackendlogging.cpp clangbackendlogging.h
|
clangbackendlogging.cpp clangbackendlogging.h
|
||||||
clangbackendreceiver.cpp clangbackendreceiver.h
|
clangbackendreceiver.cpp clangbackendreceiver.h
|
||||||
clangbackendsender.cpp clangbackendsender.h
|
clangbackendsender.cpp clangbackendsender.h
|
||||||
clangcodemodelplugin.cpp clangcodemodelplugin.h
|
clangcodemodelplugin.cpp clangcodemodelplugin.h
|
||||||
clangcompletionassistinterface.cpp clangcompletionassistinterface.h
|
|
||||||
clangcompletionassistprocessor.cpp clangcompletionassistprocessor.h
|
|
||||||
clangcompletionassistprovider.cpp clangcompletionassistprovider.h
|
|
||||||
clangcompletionchunkstotextconverter.cpp clangcompletionchunkstotextconverter.h
|
|
||||||
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
|
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
|
||||||
clangconstants.h
|
clangconstants.h
|
||||||
clangdclient.cpp clangdclient.h
|
clangdclient.cpp clangdclient.h
|
||||||
@@ -32,7 +26,6 @@ add_qtc_plugin(ClangCodeModel
|
|||||||
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
|
clangeditordocumentprocessor.cpp clangeditordocumentprocessor.h
|
||||||
clangfixitoperation.cpp clangfixitoperation.h
|
clangfixitoperation.cpp clangfixitoperation.h
|
||||||
clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h
|
clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h
|
||||||
clangfunctionhintmodel.cpp clangfunctionhintmodel.h
|
|
||||||
clangdlocatorfilters.cpp clangdlocatorfilters.h
|
clangdlocatorfilters.cpp clangdlocatorfilters.h
|
||||||
clangmodelmanagersupport.cpp clangmodelmanagersupport.h
|
clangmodelmanagersupport.cpp clangmodelmanagersupport.h
|
||||||
clangpreprocessorassistproposalitem.cpp clangpreprocessorassistproposalitem.h
|
clangpreprocessorassistproposalitem.cpp clangpreprocessorassistproposalitem.h
|
||||||
@@ -57,9 +50,7 @@ extend_qtc_plugin(ClangCodeModel
|
|||||||
extend_qtc_plugin(ClangCodeModel
|
extend_qtc_plugin(ClangCodeModel
|
||||||
CONDITION WITH_TESTS
|
CONDITION WITH_TESTS
|
||||||
SOURCES
|
SOURCES
|
||||||
test/clangautomationutils.cpp test/clangautomationutils.h
|
|
||||||
test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h
|
test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h
|
||||||
test/clangcodecompletion_test.cpp test/clangcodecompletion_test.h
|
|
||||||
test/clangdtests.cpp test/clangdtests.h
|
test/clangdtests.cpp test/clangdtests.h
|
||||||
test/data/clangtestdata.qrc
|
test/data/clangtestdata.qrc
|
||||||
)
|
)
|
||||||
|
@@ -52,13 +52,6 @@ ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
|
|||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
|
|
||||||
ActivationSequenceContextProcessor::ActivationSequenceContextProcessor(
|
|
||||||
const ClangCompletionAssistInterface *interface)
|
|
||||||
: ActivationSequenceContextProcessor(interface->textDocument(), interface->position(),
|
|
||||||
interface->languageFeatures())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const
|
CPlusPlus::Kind ActivationSequenceContextProcessor::completionKind() const
|
||||||
{
|
{
|
||||||
return m_completionKind;
|
return m_completionKind;
|
||||||
|
@@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <clangcodemodel/clangcompletionassistinterface.h>
|
|
||||||
|
|
||||||
#include <cplusplus/Token.h>
|
#include <cplusplus/Token.h>
|
||||||
|
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
@@ -43,7 +41,6 @@ class ActivationSequenceContextProcessor
|
|||||||
public:
|
public:
|
||||||
ActivationSequenceContextProcessor(QTextDocument *document, int position,
|
ActivationSequenceContextProcessor(QTextDocument *document, int position,
|
||||||
CPlusPlus::LanguageFeatures languageFeatures);
|
CPlusPlus::LanguageFeatures languageFeatures);
|
||||||
ActivationSequenceContextProcessor(const ClangCompletionAssistInterface *interface);
|
|
||||||
|
|
||||||
CPlusPlus::Kind completionKind() const;
|
CPlusPlus::Kind completionKind() const;
|
||||||
int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>"
|
int startOfNamePosition() const; // e.g. points to 'b' in "foo.bar<CURSOR>"
|
||||||
|
@@ -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 "clangbackendcommunicator.h"
|
||||||
|
|
||||||
#include "clangbackendlogging.h"
|
#include "clangbackendlogging.h"
|
||||||
#include "clangcompletionassistprocessor.h"
|
|
||||||
#include "clangmodelmanagersupport.h"
|
#include "clangmodelmanagersupport.h"
|
||||||
#include "clangutils.h"
|
#include "clangutils.h"
|
||||||
|
|
||||||
@@ -99,8 +98,6 @@ BackendCommunicator::BackendCommunicator()
|
|||||||
|
|
||||||
m_receiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); });
|
m_receiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); });
|
||||||
|
|
||||||
connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
|
|
||||||
this, &BackendCommunicator::onEditorAboutToClose);
|
|
||||||
connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
|
connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose,
|
||||||
this, &BackendCommunicator::setupDummySender);
|
this, &BackendCommunicator::setupDummySender);
|
||||||
auto globalFCB = GlobalFileChangeBlocker::instance();
|
auto globalFCB = GlobalFileChangeBlocker::instance();
|
||||||
@@ -192,11 +189,6 @@ void BackendCommunicator::documentVisibilityChanged()
|
|||||||
visibleCppEditorDocumentsFilePaths());
|
visibleCppEditorDocumentsFilePaths());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackendCommunicator::isNotWaitingForCompletion() const
|
|
||||||
{
|
|
||||||
return !m_receiver.isExpectingCompletionsMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackendCommunicator::setBackendJobsPostponed(bool postponed)
|
void BackendCommunicator::setBackendJobsPostponed(bool postponed)
|
||||||
{
|
{
|
||||||
if (postponed) {
|
if (postponed) {
|
||||||
@@ -349,12 +341,6 @@ void BackendCommunicator::onConnectedToBackend()
|
|||||||
initializeBackendWithCurrentData();
|
initializeBackendWithCurrentData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendCommunicator::onEditorAboutToClose(Core::IEditor *editor)
|
|
||||||
{
|
|
||||||
if (auto *textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor))
|
|
||||||
m_receiver.deleteProcessorsOfEditorWidget(textEditor->editorWidget());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackendCommunicator::setupDummySender()
|
void BackendCommunicator::setupDummySender()
|
||||||
{
|
{
|
||||||
m_sender.reset(new DummyBackendSender);
|
m_sender.reset(new DummyBackendSender);
|
||||||
@@ -443,26 +429,5 @@ void BackendCommunicator::unsavedFilesRemoved(const FileContainers &fileContaine
|
|||||||
m_sender->unsavedFilesRemoved(message);
|
m_sender->unsavedFilesRemoved(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendCommunicator::requestCompletions(ClangCompletionAssistProcessor *assistProcessor,
|
|
||||||
const QString &filePath,
|
|
||||||
quint32 line,
|
|
||||||
quint32 column,
|
|
||||||
qint32 funcNameStartLine,
|
|
||||||
qint32 funcNameStartColumn)
|
|
||||||
{
|
|
||||||
const RequestCompletionsMessage message(filePath,
|
|
||||||
line,
|
|
||||||
column,
|
|
||||||
funcNameStartLine,
|
|
||||||
funcNameStartColumn);
|
|
||||||
m_sender->requestCompletions(message);
|
|
||||||
m_receiver.addExpectedCompletionsMessage(message.ticketNumber, assistProcessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackendCommunicator::cancelCompletions(TextEditor::IAssistProcessor *processor)
|
|
||||||
{
|
|
||||||
m_receiver.cancelProcessor(processor);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangCodeModel
|
} // namespace ClangCodeModel
|
||||||
|
@@ -48,8 +48,6 @@ namespace TextEditor { class IAssistProcessor; }
|
|||||||
namespace ClangCodeModel {
|
namespace ClangCodeModel {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class ClangCompletionAssistProcessor;
|
|
||||||
|
|
||||||
class BackendCommunicator : public QObject
|
class BackendCommunicator : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -84,16 +82,7 @@ public:
|
|||||||
void unsavedFilesUpdatedFromCppEditorDocument(const QString &filePath);
|
void unsavedFilesUpdatedFromCppEditorDocument(const QString &filePath);
|
||||||
void unsavedFilesRemoved(const FileContainers &fileContainers);
|
void unsavedFilesRemoved(const FileContainers &fileContainers);
|
||||||
|
|
||||||
void requestCompletions(ClangCompletionAssistProcessor *assistProcessor,
|
|
||||||
const QString &filePath,
|
|
||||||
quint32 line,
|
|
||||||
quint32 column,
|
|
||||||
qint32 funcNameStartLine = -1,
|
|
||||||
qint32 funcNameStartColumn = -1);
|
|
||||||
void cancelCompletions(TextEditor::IAssistProcessor *processor);
|
|
||||||
|
|
||||||
void updateChangeContentStartPosition(const QString &filePath, int position);
|
void updateChangeContentStartPosition(const QString &filePath, int position);
|
||||||
bool isNotWaitingForCompletion() const;
|
|
||||||
|
|
||||||
void setBackendJobsPostponed(bool postponed);
|
void setBackendJobsPostponed(bool postponed);
|
||||||
|
|
||||||
@@ -107,7 +96,6 @@ private:
|
|||||||
void setupDummySender();
|
void setupDummySender();
|
||||||
|
|
||||||
void onConnectedToBackend();
|
void onConnectedToBackend();
|
||||||
void onEditorAboutToClose(Core::IEditor *editor);
|
|
||||||
|
|
||||||
void logExecutableDoesNotExist();
|
void logExecutableDoesNotExist();
|
||||||
void logRestartedDueToUnexpectedFinish();
|
void logRestartedDueToUnexpectedFinish();
|
||||||
|
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
#include "clangbackendlogging.h"
|
#include "clangbackendlogging.h"
|
||||||
|
|
||||||
#include "clangcompletionassistprocessor.h"
|
|
||||||
#include "clangeditordocumentprocessor.h"
|
#include "clangeditordocumentprocessor.h"
|
||||||
|
|
||||||
#include <clangsupport/clangcodemodelclientmessages.h>
|
#include <clangsupport/clangcodemodelclientmessages.h>
|
||||||
@@ -71,55 +70,8 @@ void BackendReceiver::setAliveHandler(const BackendReceiver::AliveHandler &handl
|
|||||||
m_aliveHandler = handler;
|
m_aliveHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendReceiver::addExpectedCompletionsMessage(
|
|
||||||
quint64 ticket,
|
|
||||||
ClangCompletionAssistProcessor *processor)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(processor, return);
|
|
||||||
QTC_CHECK(!m_assistProcessorsTable.contains(ticket));
|
|
||||||
m_assistProcessorsTable.insert(ticket, processor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackendReceiver::cancelProcessor(TextEditor::IAssistProcessor *processor)
|
|
||||||
{
|
|
||||||
for (auto it = m_assistProcessorsTable.cbegin(), end = m_assistProcessorsTable.cend();
|
|
||||||
it != end; ++it)
|
|
||||||
{
|
|
||||||
if (it.value() == processor) {
|
|
||||||
m_assistProcessorsTable.erase(it);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackendReceiver::deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *textEditorWidget)
|
|
||||||
{
|
|
||||||
QList<quint64> toRemove;
|
|
||||||
for (auto it = m_assistProcessorsTable.cbegin(), end = m_assistProcessorsTable.cend();
|
|
||||||
it != end; ++it)
|
|
||||||
{
|
|
||||||
ClangCompletionAssistProcessor *assistProcessor = it.value();
|
|
||||||
if (assistProcessor->textEditorWidget() == textEditorWidget) {
|
|
||||||
delete assistProcessor;
|
|
||||||
toRemove.append(it.key());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (quint64 item : toRemove)
|
|
||||||
m_assistProcessorsTable.remove(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BackendReceiver::isExpectingCompletionsMessage() const
|
|
||||||
{
|
|
||||||
return !m_assistProcessorsTable.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackendReceiver::reset()
|
void BackendReceiver::reset()
|
||||||
{
|
{
|
||||||
// Clean up waiting assist processors
|
|
||||||
for (ClangCompletionAssistProcessor *processor : m_assistProcessorsTable)
|
|
||||||
processor->setAsyncProposalAvailable(nullptr);
|
|
||||||
m_assistProcessorsTable.clear();
|
|
||||||
|
|
||||||
// Clean up futures for references; TODO: Remove duplication
|
// Clean up futures for references; TODO: Remove duplication
|
||||||
for (ReferencesEntry &entry : m_referencesTable) {
|
for (ReferencesEntry &entry : m_referencesTable) {
|
||||||
entry.futureInterface.cancel();
|
entry.futureInterface.cancel();
|
||||||
@@ -155,10 +107,6 @@ void BackendReceiver::completions(const ClangBackEnd::CompletionsMessage &messag
|
|||||||
{
|
{
|
||||||
qCDebugIpc() << "CompletionsMessage with" << message.codeCompletions.size()
|
qCDebugIpc() << "CompletionsMessage with" << message.codeCompletions.size()
|
||||||
<< "items";
|
<< "items";
|
||||||
|
|
||||||
const quint64 ticket = message.ticketNumber;
|
|
||||||
if (ClangCompletionAssistProcessor *processor = m_assistProcessorsTable.take(ticket))
|
|
||||||
processor->handleAvailableCompletions(message.codeCompletions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendReceiver::annotations(const ClangBackEnd::AnnotationsMessage &message)
|
void BackendReceiver::annotations(const ClangBackEnd::AnnotationsMessage &message)
|
||||||
|
@@ -43,8 +43,6 @@ class TextEditorWidget;
|
|||||||
namespace ClangCodeModel {
|
namespace ClangCodeModel {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class ClangCompletionAssistProcessor;
|
|
||||||
|
|
||||||
class BackendReceiver : public ClangBackEnd::ClangCodeModelClientInterface
|
class BackendReceiver : public ClangBackEnd::ClangCodeModelClientInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -54,12 +52,6 @@ public:
|
|||||||
using AliveHandler = std::function<void ()>;
|
using AliveHandler = std::function<void ()>;
|
||||||
void setAliveHandler(const AliveHandler &handler);
|
void setAliveHandler(const AliveHandler &handler);
|
||||||
|
|
||||||
void addExpectedCompletionsMessage(quint64 ticket, ClangCompletionAssistProcessor *processor);
|
|
||||||
void cancelProcessor(TextEditor::IAssistProcessor *processor);
|
|
||||||
void deleteProcessorsOfEditorWidget(TextEditor::TextEditorWidget *textEditorWidget);
|
|
||||||
|
|
||||||
bool isExpectingCompletionsMessage() const;
|
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -74,7 +66,6 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AliveHandler m_aliveHandler;
|
AliveHandler m_aliveHandler;
|
||||||
QHash<quint64, ClangCompletionAssistProcessor *> m_assistProcessorsTable;
|
|
||||||
|
|
||||||
struct ReferencesEntry {
|
struct ReferencesEntry {
|
||||||
ReferencesEntry() = default;
|
ReferencesEntry() = default;
|
||||||
|
@@ -29,10 +29,6 @@ QtcPlugin {
|
|||||||
"clangactivationsequencecontextprocessor.h",
|
"clangactivationsequencecontextprocessor.h",
|
||||||
"clangactivationsequenceprocessor.cpp",
|
"clangactivationsequenceprocessor.cpp",
|
||||||
"clangactivationsequenceprocessor.h",
|
"clangactivationsequenceprocessor.h",
|
||||||
"clangassistproposalitem.cpp",
|
|
||||||
"clangassistproposalitem.h",
|
|
||||||
"clangassistproposalmodel.cpp",
|
|
||||||
"clangassistproposalmodel.h",
|
|
||||||
"clangbackendcommunicator.cpp",
|
"clangbackendcommunicator.cpp",
|
||||||
"clangbackendcommunicator.h",
|
"clangbackendcommunicator.h",
|
||||||
"clangbackendlogging.cpp",
|
"clangbackendlogging.cpp",
|
||||||
@@ -43,14 +39,6 @@ QtcPlugin {
|
|||||||
"clangbackendsender.h",
|
"clangbackendsender.h",
|
||||||
"clangcodemodelplugin.cpp",
|
"clangcodemodelplugin.cpp",
|
||||||
"clangcodemodelplugin.h",
|
"clangcodemodelplugin.h",
|
||||||
"clangcompletionassistinterface.cpp",
|
|
||||||
"clangcompletionassistinterface.h",
|
|
||||||
"clangcompletionassistprocessor.cpp",
|
|
||||||
"clangcompletionassistprocessor.h",
|
|
||||||
"clangcompletionassistprovider.cpp",
|
|
||||||
"clangcompletionassistprovider.h",
|
|
||||||
"clangcompletionchunkstotextconverter.cpp",
|
|
||||||
"clangcompletionchunkstotextconverter.h",
|
|
||||||
"clangcompletioncontextanalyzer.cpp",
|
"clangcompletioncontextanalyzer.cpp",
|
||||||
"clangcompletioncontextanalyzer.h",
|
"clangcompletioncontextanalyzer.h",
|
||||||
"clangconstants.h",
|
"clangconstants.h",
|
||||||
@@ -72,8 +60,6 @@ QtcPlugin {
|
|||||||
"clangfixitoperation.h",
|
"clangfixitoperation.h",
|
||||||
"clangfixitoperationsextractor.cpp",
|
"clangfixitoperationsextractor.cpp",
|
||||||
"clangfixitoperationsextractor.h",
|
"clangfixitoperationsextractor.h",
|
||||||
"clangfunctionhintmodel.cpp",
|
|
||||||
"clangfunctionhintmodel.h",
|
|
||||||
"clangmodelmanagersupport.cpp",
|
"clangmodelmanagersupport.cpp",
|
||||||
"clangmodelmanagersupport.h",
|
"clangmodelmanagersupport.h",
|
||||||
"clangpreprocessorassistproposalitem.cpp",
|
"clangpreprocessorassistproposalitem.cpp",
|
||||||
@@ -121,12 +107,8 @@ QtcPlugin {
|
|||||||
condition: qtc.testsEnabled
|
condition: qtc.testsEnabled
|
||||||
prefix: "test/"
|
prefix: "test/"
|
||||||
files: [
|
files: [
|
||||||
"clangautomationutils.cpp",
|
|
||||||
"clangautomationutils.h",
|
|
||||||
"clangbatchfileprocessor.cpp",
|
"clangbatchfileprocessor.cpp",
|
||||||
"clangbatchfileprocessor.h",
|
"clangbatchfileprocessor.h",
|
||||||
"clangcodecompletion_test.cpp",
|
|
||||||
"clangcodecompletion_test.h",
|
|
||||||
"clangdtests.cpp",
|
"clangdtests.cpp",
|
||||||
"clangdtests.h",
|
"clangdtests.h",
|
||||||
"data/clangtestdata.qrc",
|
"data/clangtestdata.qrc",
|
||||||
|
@@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
# include "test/clangbatchfileprocessor.h"
|
# include "test/clangbatchfileprocessor.h"
|
||||||
# include "test/clangcodecompletion_test.h"
|
|
||||||
# include "test/clangdtests.h"
|
# include "test/clangdtests.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -209,7 +208,6 @@ void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const
|
|||||||
QVector<QObject *> ClangCodeModelPlugin::createTestObjects() const
|
QVector<QObject *> ClangCodeModelPlugin::createTestObjects() const
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
new Tests::ClangCodeCompletionTest,
|
|
||||||
new Tests::ClangdTestCompletion,
|
new Tests::ClangdTestCompletion,
|
||||||
new Tests::ClangdTestExternalChanges,
|
new Tests::ClangdTestExternalChanges,
|
||||||
new Tests::ClangdTestFindReferences,
|
new Tests::ClangdTestFindReferences,
|
||||||
|
@@ -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 <QDebug>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
using namespace CPlusPlus;
|
using namespace CPlusPlus;
|
||||||
|
|
||||||
@@ -65,15 +66,6 @@ bool isTokenForPassThrough(unsigned tokenKind)
|
|||||||
namespace ClangCodeModel {
|
namespace ClangCodeModel {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
|
|
||||||
const ClangCompletionAssistInterface *assistInterface,
|
|
||||||
CPlusPlus::LanguageFeatures languageFeatures)
|
|
||||||
: ClangCompletionContextAnalyzer(assistInterface->textDocument(), assistInterface->position(),
|
|
||||||
assistInterface->type() == CompletionType::FunctionHint,
|
|
||||||
languageFeatures)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
|
ClangCompletionContextAnalyzer::ClangCompletionContextAnalyzer(
|
||||||
QTextDocument *document, int position, bool isFunctionHint,
|
QTextDocument *document, int position, bool isFunctionHint,
|
||||||
CPlusPlus::LanguageFeatures languageFeatures)
|
CPlusPlus::LanguageFeatures languageFeatures)
|
||||||
|
@@ -38,14 +38,9 @@ namespace TextEditor { class AssistInterface; }
|
|||||||
namespace ClangCodeModel {
|
namespace ClangCodeModel {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class ClangCompletionAssistInterface;
|
|
||||||
|
|
||||||
class ClangCompletionContextAnalyzer
|
class ClangCompletionContextAnalyzer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClangCompletionContextAnalyzer() = delete;
|
|
||||||
ClangCompletionContextAnalyzer(const ClangCompletionAssistInterface *assistInterface,
|
|
||||||
CPlusPlus::LanguageFeatures languageFeatures);
|
|
||||||
ClangCompletionContextAnalyzer(QTextDocument *document, int position, bool isFunctionHint,
|
ClangCompletionContextAnalyzer(QTextDocument *document, int position, bool isFunctionHint,
|
||||||
CPlusPlus::LanguageFeatures languageFeatures);
|
CPlusPlus::LanguageFeatures languageFeatures);
|
||||||
void analyze();
|
void analyze();
|
||||||
|
@@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
#include "clangdclient.h"
|
#include "clangdclient.h"
|
||||||
|
|
||||||
#include "clangcompletionassistprocessor.h"
|
|
||||||
#include "clangcompletioncontextanalyzer.h"
|
#include "clangcompletioncontextanalyzer.h"
|
||||||
#include "clangconstants.h"
|
#include "clangconstants.h"
|
||||||
#include "clangdqpropertyhighlighter.h"
|
#include "clangdqpropertyhighlighter.h"
|
||||||
@@ -903,8 +902,7 @@ private:
|
|||||||
= projectPartForFile(interface->filePath().toString());
|
= projectPartForFile(interface->filePath().toString());
|
||||||
if (projectPart)
|
if (projectPart)
|
||||||
headerPaths = projectPart->headerPaths;
|
headerPaths = projectPart->headerPaths;
|
||||||
completions = ClangCompletionAssistProcessor::completeInclude(
|
completions = completeInclude(m_endPos, m_completionOperator, interface, headerPaths);
|
||||||
m_endPos, m_completionOperator, interface, headerPaths);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -927,6 +925,107 @@ private:
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates completion proposals for #include and given cursor
|
||||||
|
* @param position - cursor placed after opening bracked or quote
|
||||||
|
* @param completionOperator - the type of token
|
||||||
|
* @param interface - relevant document data
|
||||||
|
* @param headerPaths - the include paths
|
||||||
|
* @return the list of completion items
|
||||||
|
*/
|
||||||
|
static QList<AssistProposalItemInterface *> completeInclude(
|
||||||
|
int position, unsigned completionOperator, const TextEditor::AssistInterface *interface,
|
||||||
|
const ProjectExplorer::HeaderPaths &headerPaths)
|
||||||
|
{
|
||||||
|
QTextCursor cursor(interface->textDocument());
|
||||||
|
cursor.setPosition(position);
|
||||||
|
QString directoryPrefix;
|
||||||
|
if (completionOperator == T_SLASH) {
|
||||||
|
QTextCursor c = cursor;
|
||||||
|
c.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
|
||||||
|
QString sel = c.selectedText();
|
||||||
|
int startCharPos = sel.indexOf(QLatin1Char('"'));
|
||||||
|
if (startCharPos == -1) {
|
||||||
|
startCharPos = sel.indexOf(QLatin1Char('<'));
|
||||||
|
completionOperator = T_ANGLE_STRING_LITERAL;
|
||||||
|
} else {
|
||||||
|
completionOperator = T_STRING_LITERAL;
|
||||||
|
}
|
||||||
|
if (startCharPos != -1)
|
||||||
|
directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make completion for all relevant includes
|
||||||
|
ProjectExplorer::HeaderPaths allHeaderPaths = headerPaths;
|
||||||
|
const auto currentFilePath = ProjectExplorer::HeaderPath::makeUser(
|
||||||
|
interface->filePath().toFileInfo().path());
|
||||||
|
if (!allHeaderPaths.contains(currentFilePath))
|
||||||
|
allHeaderPaths.append(currentFilePath);
|
||||||
|
|
||||||
|
const ::Utils::MimeType mimeType = ::Utils::mimeTypeForName("text/x-c++hdr");
|
||||||
|
const QStringList suffixes = mimeType.suffixes();
|
||||||
|
|
||||||
|
QList<AssistProposalItemInterface *> completions;
|
||||||
|
foreach (const ProjectExplorer::HeaderPath &headerPath, allHeaderPaths) {
|
||||||
|
QString realPath = headerPath.path;
|
||||||
|
if (!directoryPrefix.isEmpty()) {
|
||||||
|
realPath += QLatin1Char('/');
|
||||||
|
realPath += directoryPrefix;
|
||||||
|
if (headerPath.type == ProjectExplorer::HeaderPathType::Framework)
|
||||||
|
realPath += QLatin1String(".framework/Headers");
|
||||||
|
}
|
||||||
|
completions << completeIncludePath(realPath, suffixes, completionOperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QPair<AssistProposalItemInterface *, QString>> completionsForSorting;
|
||||||
|
for (AssistProposalItemInterface * const item : qAsConst(completions)) {
|
||||||
|
QString s = item->text();
|
||||||
|
s.replace('/', QChar(0)); // The dir separator should compare less than anything else.
|
||||||
|
completionsForSorting << qMakePair(item, s);
|
||||||
|
}
|
||||||
|
Utils::sort(completionsForSorting, [](const auto &left, const auto &right) {
|
||||||
|
return left.second < right.second;
|
||||||
|
});
|
||||||
|
for (int i = 0; i < completionsForSorting.count(); ++i)
|
||||||
|
completions[i] = completionsForSorting[i].first;
|
||||||
|
|
||||||
|
return completions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds #include completion proposals using given include path
|
||||||
|
* @param realPath - one of directories where compiler searches includes
|
||||||
|
* @param suffixes - file suffixes for C/C++ header files
|
||||||
|
* @return a list of matching completion items
|
||||||
|
*/
|
||||||
|
static QList<AssistProposalItemInterface *> completeIncludePath(
|
||||||
|
const QString &realPath, const QStringList &suffixes, unsigned completionOperator)
|
||||||
|
{
|
||||||
|
QList<AssistProposalItemInterface *> completions;
|
||||||
|
QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
//: Parent folder for proposed #include completion
|
||||||
|
const QString hint = ClangdClient::tr("Location: %1")
|
||||||
|
.arg(QDir::toNativeSeparators(QDir::cleanPath(realPath)));
|
||||||
|
while (i.hasNext()) {
|
||||||
|
const QString fileName = i.next();
|
||||||
|
const QFileInfo fileInfo = i.fileInfo();
|
||||||
|
const QString suffix = fileInfo.suffix();
|
||||||
|
if (suffix.isEmpty() || suffixes.contains(suffix)) {
|
||||||
|
QString text = fileName.mid(realPath.length() + 1);
|
||||||
|
if (fileInfo.isDir())
|
||||||
|
text += QLatin1Char('/');
|
||||||
|
|
||||||
|
auto *item = new ClangPreprocessorAssistProposalItem;
|
||||||
|
item->setText(text);
|
||||||
|
item->setDetail(hint);
|
||||||
|
item->setIcon(CPlusPlus::Icons::keywordIcon());
|
||||||
|
item->setCompletionOperator(completionOperator);
|
||||||
|
completions.append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return completions;
|
||||||
|
}
|
||||||
|
|
||||||
ClangdClient * const m_client;
|
ClangdClient * const m_client;
|
||||||
const int m_position;
|
const int m_position;
|
||||||
const int m_endPos;
|
const int m_endPos;
|
||||||
|
@@ -153,7 +153,7 @@ CppEditor::SemanticInfo ClangEditorDocumentProcessor::recalculateSemanticInfo()
|
|||||||
|
|
||||||
CppEditor::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser()
|
CppEditor::BaseEditorDocumentParser::Ptr ClangEditorDocumentProcessor::parser()
|
||||||
{
|
{
|
||||||
return m_parser;
|
return m_builtinProcessor.parser();
|
||||||
}
|
}
|
||||||
|
|
||||||
CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot()
|
CPlusPlus::Snapshot ClangEditorDocumentProcessor::snapshot()
|
||||||
|
@@ -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);
|
return Utils::qobject_container_cast<TextEditor::TextDocument *>(documents);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangModelManagerSupport::ClangModelManagerSupport()
|
ClangModelManagerSupport::ClangModelManagerSupport() : m_refactoringEngine(new RefactoringEngine)
|
||||||
: m_completionAssistProvider(m_communicator, CompletionType::Other)
|
|
||||||
, m_functionHintAssistProvider(m_communicator, CompletionType::FunctionHint)
|
|
||||||
, m_refactoringEngine(new RefactoringEngine)
|
|
||||||
{
|
{
|
||||||
QTC_CHECK(!m_instance);
|
QTC_CHECK(!m_instance);
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
@@ -175,12 +172,12 @@ ClangModelManagerSupport::~ClangModelManagerSupport()
|
|||||||
|
|
||||||
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::completionAssistProvider()
|
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::completionAssistProvider()
|
||||||
{
|
{
|
||||||
return &m_completionAssistProvider;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAssistProvider()
|
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAssistProvider()
|
||||||
{
|
{
|
||||||
return &m_functionHintAssistProvider;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,
|
void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "clangcompletionassistprovider.h"
|
#include "clangbackendcommunicator.h"
|
||||||
#include "clanguiheaderondiskmanager.h"
|
#include "clanguiheaderondiskmanager.h"
|
||||||
|
|
||||||
#include <cppeditor/cppmodelmanagersupport.h>
|
#include <cppeditor/cppmodelmanagersupport.h>
|
||||||
@@ -143,8 +143,6 @@ private:
|
|||||||
|
|
||||||
UiHeaderOnDiskManager m_uiHeaderOnDiskManager;
|
UiHeaderOnDiskManager m_uiHeaderOnDiskManager;
|
||||||
BackendCommunicator m_communicator;
|
BackendCommunicator m_communicator;
|
||||||
ClangCompletionAssistProvider m_completionAssistProvider;
|
|
||||||
ClangCompletionAssistProvider m_functionHintAssistProvider;
|
|
||||||
std::unique_ptr<CppEditor::RefactoringEngineInterface> m_refactoringEngine;
|
std::unique_ptr<CppEditor::RefactoringEngineInterface> m_refactoringEngine;
|
||||||
|
|
||||||
QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings;
|
QHash<ProjectExplorer::Project *, ClangProjectSettings *> m_projectSettings;
|
||||||
|
@@ -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 "clangbatchfileprocessor.h"
|
||||||
|
|
||||||
#include "clangautomationutils.h"
|
|
||||||
|
|
||||||
#include <clangcodemodel/clangeditordocumentprocessor.h>
|
#include <clangcodemodel/clangeditordocumentprocessor.h>
|
||||||
|
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
@@ -442,45 +440,6 @@ Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments,
|
|||||||
return Command::Ptr(new InsertTextCommand(context, textToInsert));
|
return Command::Ptr(new InsertTextCommand(context, textToInsert));
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompleteCommand : public Command
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CompleteCommand(const CommandContext &context);
|
|
||||||
|
|
||||||
bool run() override;
|
|
||||||
|
|
||||||
static Command::Ptr parse(BatchFileLineTokenizer &arguments,
|
|
||||||
const CommandContext &context);
|
|
||||||
};
|
|
||||||
|
|
||||||
CompleteCommand::CompleteCommand(const CommandContext &context)
|
|
||||||
: Command(context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CompleteCommand::run()
|
|
||||||
{
|
|
||||||
qCDebug(debug) << "line" << context().lineNumber << "CompleteCommand";
|
|
||||||
|
|
||||||
TextEditor::BaseTextEditor *editor = currentTextEditor();
|
|
||||||
QTC_ASSERT(editor, return false);
|
|
||||||
|
|
||||||
const QString documentFilePath = editor->document()->filePath().toString();
|
|
||||||
auto *processor = ClangEditorDocumentProcessor::get(documentFilePath);
|
|
||||||
QTC_ASSERT(processor, return false);
|
|
||||||
|
|
||||||
return !completionResults(editor, QStringList(), timeOutInMs()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::Ptr CompleteCommand::parse(BatchFileLineTokenizer &arguments,
|
|
||||||
const CommandContext &context)
|
|
||||||
{
|
|
||||||
Q_UNUSED(arguments)
|
|
||||||
Q_UNUSED(context)
|
|
||||||
|
|
||||||
return Command::Ptr(new CompleteCommand(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
class SetCursorCommand : public Command
|
class SetCursorCommand : public Command
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -687,7 +646,6 @@ BatchFileParser::BatchFileParser(const QString &filePath,
|
|||||||
m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse);
|
m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse);
|
||||||
m_commandParsers.insert("setCursor", &SetCursorCommand::parse);
|
m_commandParsers.insert("setCursor", &SetCursorCommand::parse);
|
||||||
m_commandParsers.insert("insertText", &InsertTextCommand::parse);
|
m_commandParsers.insert("insertText", &InsertTextCommand::parse);
|
||||||
m_commandParsers.insert("complete", &CompleteCommand::parse);
|
|
||||||
m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse);
|
m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 "clangdtests.h"
|
||||||
|
|
||||||
#include "clangautomationutils.h"
|
|
||||||
#include "clangbatchfileprocessor.h"
|
#include "clangbatchfileprocessor.h"
|
||||||
#include "../clangdclient.h"
|
#include "../clangdclient.h"
|
||||||
#include "../clangmodelmanagersupport.h"
|
#include "../clangmodelmanagersupport.h"
|
||||||
@@ -83,6 +82,11 @@ namespace ClangCodeModel {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
namespace Tests {
|
namespace Tests {
|
||||||
|
|
||||||
|
static QString qrcPath(const QByteArray &relativeFilePath)
|
||||||
|
{
|
||||||
|
return QLatin1String(":/unittests/ClangCodeModel/") + QString::fromUtf8(relativeFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
ClangdTest::~ClangdTest()
|
ClangdTest::~ClangdTest()
|
||||||
{
|
{
|
||||||
EditorManager::closeAllEditors(false);
|
EditorManager::closeAllEditors(false);
|
||||||
|
@@ -1653,12 +1653,12 @@ void CppModelManager::activateClangCodeModel(
|
|||||||
|
|
||||||
CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const
|
CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const
|
||||||
{
|
{
|
||||||
return d->m_activeModelManagerSupport->completionAssistProvider();
|
return d->m_builtinModelManagerSupport->completionAssistProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const
|
CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const
|
||||||
{
|
{
|
||||||
return d->m_activeModelManagerSupport->functionHintAssistProvider();
|
return d->m_builtinModelManagerSupport->functionHintAssistProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
|
TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
|
||||||
|
@@ -174,14 +174,12 @@ extend_qtc_test(unittest
|
|||||||
DEFINES CLANG_UNIT_TESTS
|
DEFINES CLANG_UNIT_TESTS
|
||||||
DEPENDS libclang
|
DEPENDS libclang
|
||||||
SOURCES
|
SOURCES
|
||||||
activationsequencecontextprocessor-test.cpp
|
|
||||||
activationsequenceprocessor-test.cpp
|
activationsequenceprocessor-test.cpp
|
||||||
chunksreportedmonitor.cpp
|
chunksreportedmonitor.cpp
|
||||||
clangasyncjob-base.cpp
|
clangasyncjob-base.cpp
|
||||||
clangcodecompleteresults-test.cpp
|
clangcodecompleteresults-test.cpp
|
||||||
clangcodemodelserver-test.cpp
|
clangcodemodelserver-test.cpp
|
||||||
clangcompletecodejob-test.cpp
|
clangcompletecodejob-test.cpp
|
||||||
clangcompletioncontextanalyzer-test.cpp
|
|
||||||
clangdocumentprocessors-test.cpp
|
clangdocumentprocessors-test.cpp
|
||||||
clangdocumentprocessor-test.cpp
|
clangdocumentprocessor-test.cpp
|
||||||
clangdocuments-test.cpp
|
clangdocuments-test.cpp
|
||||||
@@ -205,7 +203,6 @@ extend_qtc_test(unittest
|
|||||||
clangupdateannotationsjob-test.cpp
|
clangupdateannotationsjob-test.cpp
|
||||||
codecompleter-test.cpp
|
codecompleter-test.cpp
|
||||||
codecompletionsextractor-test.cpp
|
codecompletionsextractor-test.cpp
|
||||||
completionchunkstotextconverter-test.cpp
|
|
||||||
cursor-test.cpp
|
cursor-test.cpp
|
||||||
diagnosticset-test.cpp
|
diagnosticset-test.cpp
|
||||||
diagnostic-test.cpp
|
diagnostic-test.cpp
|
||||||
@@ -415,10 +412,7 @@ extend_qtc_test(unittest DEPENDS Utils CPlusPlus ClangSupport)
|
|||||||
extend_qtc_test(unittest
|
extend_qtc_test(unittest
|
||||||
SOURCES_PREFIX ../../../src/plugins/clangcodemodel
|
SOURCES_PREFIX ../../../src/plugins/clangcodemodel
|
||||||
SOURCES
|
SOURCES
|
||||||
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
|
|
||||||
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
|
clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h
|
||||||
clangcompletionchunkstotextconverter.cpp clangcompletionchunkstotextconverter.h
|
|
||||||
clangcompletioncontextanalyzer.cpp clangcompletioncontextanalyzer.h
|
|
||||||
clangfixitoperation.cpp clangfixitoperation.h
|
clangfixitoperation.cpp clangfixitoperation.h
|
||||||
clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h
|
clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h
|
||||||
)
|
)
|
||||||
|
@@ -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"
|
name: "libclang tests"
|
||||||
condition: libclang.present && (!qbs.targetOS.contains("windows") || libclang.llvmBuildModeMatches)
|
condition: libclang.present && (!qbs.targetOS.contains("windows") || libclang.llvmBuildModeMatches)
|
||||||
files: [
|
files: [
|
||||||
"activationsequencecontextprocessor-test.cpp",
|
|
||||||
"activationsequenceprocessor-test.cpp",
|
"activationsequenceprocessor-test.cpp",
|
||||||
"chunksreportedmonitor.cpp",
|
"chunksreportedmonitor.cpp",
|
||||||
"chunksreportedmonitor.h",
|
"chunksreportedmonitor.h",
|
||||||
@@ -203,7 +202,6 @@ Project {
|
|||||||
"clangcodemodelserver-test.cpp",
|
"clangcodemodelserver-test.cpp",
|
||||||
"clangcompareoperators.h",
|
"clangcompareoperators.h",
|
||||||
"clangcompletecodejob-test.cpp",
|
"clangcompletecodejob-test.cpp",
|
||||||
"clangcompletioncontextanalyzer-test.cpp",
|
|
||||||
"clangdocument-test.cpp",
|
"clangdocument-test.cpp",
|
||||||
"clangdocumentprocessor-test.cpp",
|
"clangdocumentprocessor-test.cpp",
|
||||||
"clangdocumentprocessors-test.cpp",
|
"clangdocumentprocessors-test.cpp",
|
||||||
@@ -225,7 +223,6 @@ Project {
|
|||||||
"clangupdateannotationsjob-test.cpp",
|
"clangupdateannotationsjob-test.cpp",
|
||||||
"codecompleter-test.cpp",
|
"codecompleter-test.cpp",
|
||||||
"codecompletionsextractor-test.cpp",
|
"codecompletionsextractor-test.cpp",
|
||||||
"completionchunkstotextconverter-test.cpp",
|
|
||||||
"cursor-test.cpp",
|
"cursor-test.cpp",
|
||||||
"diagnostic-test.cpp",
|
"diagnostic-test.cpp",
|
||||||
"diagnosticcontainer-matcher.h",
|
"diagnosticcontainer-matcher.h",
|
||||||
@@ -392,14 +389,8 @@ Project {
|
|||||||
name: "sources from clangcodemodel"
|
name: "sources from clangcodemodel"
|
||||||
prefix: "../../../src/plugins/clangcodemodel/"
|
prefix: "../../../src/plugins/clangcodemodel/"
|
||||||
files: [
|
files: [
|
||||||
"clangactivationsequencecontextprocessor.cpp",
|
|
||||||
"clangactivationsequencecontextprocessor.h",
|
|
||||||
"clangactivationsequenceprocessor.cpp",
|
"clangactivationsequenceprocessor.cpp",
|
||||||
"clangactivationsequenceprocessor.h",
|
"clangactivationsequenceprocessor.h",
|
||||||
"clangcompletionchunkstotextconverter.cpp",
|
|
||||||
"clangcompletionchunkstotextconverter.h",
|
|
||||||
"clangcompletioncontextanalyzer.cpp",
|
|
||||||
"clangcompletioncontextanalyzer.h",
|
|
||||||
"clangfixitoperation.cpp",
|
"clangfixitoperation.cpp",
|
||||||
"clangfixitoperation.h",
|
"clangfixitoperation.h",
|
||||||
"clanguiheaderondiskmanager.cpp",
|
"clanguiheaderondiskmanager.cpp",
|
||||||
|
Reference in New Issue
Block a user