2013-12-10 14:37:32 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2013-12-10 14:37:32 +01:00
|
|
|
**
|
|
|
|
|
** 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
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2013-12-10 14:37:32 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2013-12-10 14:37:32 +01:00
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2015-07-06 12:05:02 +02:00
|
|
|
#include "clangassistproposalitem.h"
|
|
|
|
|
|
2015-11-30 09:43:50 +01:00
|
|
|
#include "clangactivationsequenceprocessor.h"
|
2015-07-06 12:05:02 +02:00
|
|
|
#include "clangassistproposalmodel.h"
|
|
|
|
|
#include "clangcompletionassistprocessor.h"
|
2015-05-08 15:48:17 +02:00
|
|
|
#include "clangcompletioncontextanalyzer.h"
|
2018-11-09 12:44:39 +01:00
|
|
|
#include "clangfixitoperation.h"
|
2015-07-06 12:05:02 +02:00
|
|
|
#include "clangfunctionhintmodel.h"
|
2015-11-30 09:43:50 +01:00
|
|
|
#include "clangcompletionchunkstotextconverter.h"
|
2016-01-28 16:39:05 +01:00
|
|
|
#include "clangpreprocessorassistproposalitem.h"
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-12-15 11:59:48 +01:00
|
|
|
#include <cpptools/cppdoxygen.h>
|
|
|
|
|
#include <cpptools/cppmodelmanager.h>
|
2015-12-30 12:14:40 +01:00
|
|
|
#include <cpptools/cpptoolsbridge.h>
|
2015-09-28 17:42:43 +02:00
|
|
|
#include <cpptools/editordocumenthandle.h>
|
|
|
|
|
|
2015-07-06 12:05:02 +02:00
|
|
|
#include <texteditor/codeassist/assistproposalitem.h>
|
|
|
|
|
#include <texteditor/codeassist/functionhintproposal.h>
|
2018-06-15 14:35:58 +02:00
|
|
|
#include <texteditor/codeassist/genericproposal.h>
|
2015-07-06 12:05:02 +02:00
|
|
|
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
|
2020-08-03 16:19:19 +02:00
|
|
|
#include <texteditor/texteditorsettings.h>
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-07-06 12:05:02 +02:00
|
|
|
#include <cplusplus/BackwardsScanner.h>
|
|
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
2016-04-06 10:08:01 +02:00
|
|
|
#include <cplusplus/Icons.h>
|
2015-07-06 12:05:02 +02:00
|
|
|
#include <cplusplus/SimpleLexer.h>
|
|
|
|
|
|
2017-08-24 12:28:01 +02:00
|
|
|
#include <clangsupport/filecontainer.h>
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2017-04-28 17:46:40 +02:00
|
|
|
#include <utils/algorithm.h>
|
2015-02-04 09:32:46 +01:00
|
|
|
#include <utils/mimetypes/mimedatabase.h>
|
2018-09-27 15:50:12 +02:00
|
|
|
#include <utils/optional.h>
|
2020-09-18 12:39:32 +02:00
|
|
|
#include <utils/porting.h>
|
2015-12-15 11:59:48 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2020-09-18 12:39:32 +02:00
|
|
|
#include <utils/textutils.h>
|
2015-12-15 11:59:48 +01:00
|
|
|
|
|
|
|
|
#include <QDirIterator>
|
2020-11-19 15:04:11 +01:00
|
|
|
#include <QPair>
|
2015-12-30 12:14:40 +01:00
|
|
|
#include <QTextDocument>
|
2014-06-20 15:44:56 +04:00
|
|
|
|
2015-07-06 12:05:02 +02:00
|
|
|
namespace ClangCodeModel {
|
|
|
|
|
namespace Internal {
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-07-22 17:33:27 +02:00
|
|
|
using ClangBackEnd::CodeCompletion;
|
2016-02-01 14:51:01 +01:00
|
|
|
using TextEditor::AssistProposalItemInterface;
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2018-07-31 11:44:07 +02:00
|
|
|
static void addAssistProposalItem(QList<AssistProposalItemInterface *> &items,
|
|
|
|
|
const CodeCompletion &codeCompletion,
|
|
|
|
|
const QString &name)
|
|
|
|
|
{
|
2018-11-04 21:44:21 +01:00
|
|
|
auto item = new ClangAssistProposalItem;
|
2018-07-31 11:44:07 +02:00
|
|
|
items.push_back(item);
|
|
|
|
|
|
|
|
|
|
item->setText(name);
|
|
|
|
|
item->setOrder(int(codeCompletion.priority));
|
|
|
|
|
item->appendCodeCompletion(codeCompletion);
|
|
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2018-09-27 15:50:12 +02:00
|
|
|
// Add the next CXXMethod or CXXConstructor which is the overload for another existing item.
|
2018-07-31 11:44:07 +02:00
|
|
|
static void addFunctionOverloadAssistProposalItem(QList<AssistProposalItemInterface *> &items,
|
2018-08-02 15:15:59 +02:00
|
|
|
AssistProposalItemInterface *sameItem,
|
|
|
|
|
const ClangCompletionAssistInterface *interface,
|
|
|
|
|
const CodeCompletion &codeCompletion,
|
|
|
|
|
const QString &name)
|
2018-07-31 11:44:07 +02:00
|
|
|
{
|
2018-08-02 15:15:59 +02:00
|
|
|
auto *item = static_cast<ClangAssistProposalItem *>(sameItem);
|
2019-02-21 11:07:03 +01:00
|
|
|
item->setHasOverloadsWithParameters(codeCompletion.hasParameters);
|
2018-09-27 15:50:12 +02:00
|
|
|
if (codeCompletion.completionKind == CodeCompletion::ConstructorCompletionKind) {
|
|
|
|
|
// It's the constructor, currently constructor definitions do not lead here.
|
2018-08-02 15:15:59 +02:00
|
|
|
// CLANG-UPGRADE-CHECK: Can we get here with constructor definition?
|
|
|
|
|
item->appendCodeCompletion(codeCompletion);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 11:44:07 +02:00
|
|
|
QTextCursor cursor = interface->textEditorWidget()->textCursor();
|
|
|
|
|
cursor.setPosition(interface->position());
|
|
|
|
|
cursor.movePosition(QTextCursor::StartOfWord);
|
|
|
|
|
|
2018-09-27 15:50:12 +02:00
|
|
|
const ClangBackEnd::CodeCompletionChunk resultType = codeCompletion.chunks.first();
|
2020-07-06 15:49:35 +02:00
|
|
|
if (matchPreviousWord(*interface->textEditorWidget(), cursor, resultType.text.toString())) {
|
2018-09-27 15:50:12 +02:00
|
|
|
// Function definition completion - do not merge completions together.
|
2018-07-31 11:44:07 +02:00
|
|
|
addAssistProposalItem(items, codeCompletion, name);
|
|
|
|
|
} else {
|
|
|
|
|
item->appendCodeCompletion(codeCompletion);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 15:50:12 +02:00
|
|
|
// Check if they are both CXXMethod or CXXConstructor.
|
|
|
|
|
static bool isTheSameFunctionOverload(const CodeCompletion &completion,
|
|
|
|
|
const QString &name,
|
|
|
|
|
ClangAssistProposalItem *lastItem)
|
|
|
|
|
{
|
2018-10-17 14:16:32 +02:00
|
|
|
return completion.completionKind == lastItem->firstCodeCompletion().completionKind
|
2018-09-27 15:50:12 +02:00
|
|
|
&& lastItem->text() == name;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-25 14:32:56 +02:00
|
|
|
QList<AssistProposalItemInterface *> ClangCompletionAssistProcessor::toAssistProposalItems(
|
|
|
|
|
const CodeCompletions &completions) const
|
2013-12-10 14:37:32 +01:00
|
|
|
{
|
2018-12-03 08:26:48 +01:00
|
|
|
// TODO: Handle Qt4's SIGNAL/SLOT
|
|
|
|
|
// Possibly check for m_completionOperator == T_SIGNAL
|
|
|
|
|
// Possibly check for codeCompletion.completionKind == CodeCompletion::SignalCompletionKind
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2018-07-31 11:44:07 +02:00
|
|
|
QList<AssistProposalItemInterface *> items;
|
|
|
|
|
items.reserve(completions.size());
|
2020-08-25 14:32:56 +02:00
|
|
|
|
|
|
|
|
// 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 = CppTools::CppModelManager::instance()
|
2020-09-02 12:29:23 +02:00
|
|
|
->positionRequiresSignal(m_interface->filePath().toString(), m_content, m_position);
|
2020-08-25 14:32:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-04-04 18:25:23 +02:00
|
|
|
for (const CodeCompletion &codeCompletion : completions) {
|
2020-08-25 14:32:56 +02:00
|
|
|
if (considerOnlySignals && codeCompletion.completionKind
|
|
|
|
|
!= CodeCompletion::SignalCompletionKind) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-09-27 15:50:12 +02:00
|
|
|
if (codeCompletion.text.isEmpty())
|
|
|
|
|
continue; // It's an OverloadCandidate which has text but no typedText.
|
|
|
|
|
|
2020-06-04 17:15:10 +02:00
|
|
|
// Don't offer symbols that are not accessible here.
|
|
|
|
|
if (codeCompletion.availability == CodeCompletion::NotAvailable
|
|
|
|
|
|| codeCompletion.availability == CodeCompletion::NotAccessible) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 11:44:07 +02:00
|
|
|
const QString name = codeCompletion.completionKind == CodeCompletion::KeywordCompletionKind
|
|
|
|
|
? CompletionChunksToTextConverter::convertToName(codeCompletion.chunks)
|
|
|
|
|
: codeCompletion.text.toString();
|
|
|
|
|
|
2018-09-27 15:50:12 +02:00
|
|
|
if (items.empty()) {
|
|
|
|
|
addAssistProposalItem(items, codeCompletion, name);
|
|
|
|
|
} else {
|
|
|
|
|
auto *lastItem = static_cast<ClangAssistProposalItem *>(items.last());
|
|
|
|
|
if (isTheSameFunctionOverload(codeCompletion, name, lastItem)) {
|
2020-08-25 14:32:56 +02:00
|
|
|
addFunctionOverloadAssistProposalItem(items, items.back(), m_interface.data(),
|
2018-08-02 15:15:59 +02:00
|
|
|
codeCompletion, name);
|
2018-09-27 15:50:12 +02:00
|
|
|
} else {
|
|
|
|
|
addAssistProposalItem(items, codeCompletion, name);
|
2018-08-02 15:15:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-31 11:44:07 +02:00
|
|
|
return items;
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-06 12:05:02 +02:00
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
using namespace TextEditor;
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2013-12-10 14:37:32 +01:00
|
|
|
ClangCompletionAssistProcessor::ClangCompletionAssistProcessor()
|
2016-07-27 08:48:46 +02:00
|
|
|
: CppCompletionAssistProcessor(100)
|
|
|
|
|
, m_completionOperator(T_EOF_SYMBOL)
|
2013-12-10 14:37:32 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 21:44:21 +01:00
|
|
|
ClangCompletionAssistProcessor::~ClangCompletionAssistProcessor() = default;
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2014-09-04 00:04:18 +02:00
|
|
|
IAssistProposal *ClangCompletionAssistProcessor::perform(const AssistInterface *interface)
|
2013-12-10 14:37:32 +01:00
|
|
|
{
|
|
|
|
|
m_interface.reset(static_cast<const ClangCompletionAssistInterface *>(interface));
|
|
|
|
|
|
2015-07-29 12:15:15 +02:00
|
|
|
if (interface->reason() != ExplicitlyInvoked && !accepts()) {
|
2018-03-01 12:33:48 +01:00
|
|
|
m_requestSent = false;
|
|
|
|
|
return nullptr;
|
2015-07-29 12:15:15 +02:00
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
return startCompletionHelper(); // == 0 if results are calculated asynchronously
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
2018-11-09 12:44:39 +01:00
|
|
|
// 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();
|
|
|
|
|
|
2019-07-24 18:40:10 +02:00
|
|
|
const int fixItLength = fixIt.range.end.column - fixIt.range.start.column;
|
2018-11-09 12:44:39 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 15:50:12 +02:00
|
|
|
void ClangCompletionAssistProcessor::handleAvailableCompletions(const CodeCompletions &completions)
|
2013-12-10 14:37:32 +01:00
|
|
|
{
|
2017-04-28 17:46:40 +02:00
|
|
|
QTC_CHECK(m_completions.isEmpty());
|
2015-07-22 13:09:44 +02:00
|
|
|
|
2018-10-05 14:43:30 +02:00
|
|
|
if (m_sentRequestType == FunctionHintCompletion && !completions.isEmpty()) {
|
2018-09-27 15:50:12 +02:00
|
|
|
const CodeCompletion &firstCompletion = completions.front();
|
|
|
|
|
if (firstCompletion.completionKind == CodeCompletion::FunctionOverloadCompletionKind) {
|
|
|
|
|
setAsyncProposalAvailable(createFunctionHintProposal(completions));
|
2018-08-20 13:15:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2019-03-11 12:01:02 +01:00
|
|
|
|
2020-02-26 08:50:43 +01:00
|
|
|
if (!m_fallbackToNormalCompletion) {
|
|
|
|
|
// We must report back to the code assistant under all circumstances
|
|
|
|
|
setAsyncProposalAvailable(nullptr);
|
2019-03-11 12:01:02 +01:00
|
|
|
return;
|
2020-02-26 08:50:43 +01:00
|
|
|
}
|
2018-08-20 13:15:13 +02:00
|
|
|
// 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.
|
2017-04-28 17:46:40 +02:00
|
|
|
}
|
2018-08-20 13:15:13 +02:00
|
|
|
|
|
|
|
|
//m_sentRequestType == NormalCompletion or function signatures were empty
|
2018-11-09 12:44:39 +01:00
|
|
|
|
|
|
|
|
// 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())
|
2020-08-25 14:32:56 +02:00
|
|
|
m_completions = toAssistProposalItems(applyCompletionFixIt(completions));
|
2018-11-09 12:44:39 +01:00
|
|
|
else
|
2020-08-25 14:32:56 +02:00
|
|
|
m_completions = toAssistProposalItems(completions);
|
2018-08-20 13:15:13 +02:00
|
|
|
|
|
|
|
|
if (m_addSnippets && !m_completions.isEmpty())
|
|
|
|
|
addSnippets();
|
|
|
|
|
|
|
|
|
|
setAsyncProposalAvailable(createProposal());
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
const TextEditorWidget *ClangCompletionAssistProcessor::textEditorWidget() const
|
|
|
|
|
{
|
|
|
|
|
return m_interface->textEditorWidget();
|
|
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
/// 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;
|
|
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
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;
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
--comma;
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
if (comma < 0)
|
|
|
|
|
return QByteArray();
|
|
|
|
|
const int openBrace = findOpenParen(doc, comma);
|
|
|
|
|
if (openBrace < 0)
|
|
|
|
|
return QByteArray();
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2019-03-11 12:01:02 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper()
|
|
|
|
|
{
|
|
|
|
|
ClangCompletionContextAnalyzer analyzer(m_interface.data(), m_interface->languageFeatures());
|
|
|
|
|
analyzer.analyze();
|
|
|
|
|
m_completionOperator = analyzer.completionOperator();
|
|
|
|
|
m_positionForProposal = analyzer.positionForProposal();
|
2018-10-24 10:05:53 +02:00
|
|
|
m_addSnippets = analyzer.addSnippets();
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
QByteArray modifiedFileContent;
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
const ClangCompletionContextAnalyzer::CompletionAction action = analyzer.completionAction();
|
|
|
|
|
switch (action) {
|
|
|
|
|
case ClangCompletionContextAnalyzer::CompleteDoxygenKeyword:
|
|
|
|
|
if (completeDoxygenKeywords())
|
|
|
|
|
return createProposal();
|
|
|
|
|
break;
|
|
|
|
|
case ClangCompletionContextAnalyzer::CompleteIncludePath:
|
|
|
|
|
if (completeInclude(analyzer.positionEndOfExpression()))
|
|
|
|
|
return createProposal();
|
|
|
|
|
break;
|
|
|
|
|
case ClangCompletionContextAnalyzer::CompletePreprocessorDirective:
|
|
|
|
|
if (completePreprocessorDirectives())
|
|
|
|
|
return createProposal();
|
|
|
|
|
break;
|
|
|
|
|
case ClangCompletionContextAnalyzer::CompleteSignal:
|
|
|
|
|
case ClangCompletionContextAnalyzer::CompleteSlot:
|
|
|
|
|
modifiedFileContent = modifyInput(m_interface->textDocument(),
|
|
|
|
|
analyzer.positionEndOfExpression());
|
2018-01-10 16:51:58 +01:00
|
|
|
Q_FALLTHROUGH();
|
2015-05-08 15:48:17 +02:00
|
|
|
case ClangCompletionContextAnalyzer::PassThroughToLibClang: {
|
|
|
|
|
m_sentRequestType = NormalCompletion;
|
2018-03-01 12:33:48 +01:00
|
|
|
m_requestSent = sendCompletionRequest(analyzer.positionForClang(),
|
|
|
|
|
modifiedFileContent);
|
2015-05-08 15:48:17 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: {
|
|
|
|
|
m_sentRequestType = FunctionHintCompletion;
|
2019-03-11 12:01:02 +01:00
|
|
|
if (lastPrecedingNonWhitespaceChar(m_interface.data()) == ',')
|
|
|
|
|
m_fallbackToNormalCompletion = false;
|
2018-03-01 12:33:48 +01:00
|
|
|
m_requestSent = sendCompletionRequest(analyzer.positionForClang(), QByteArray(),
|
|
|
|
|
analyzer.functionNameStart());
|
2015-05-08 15:48:17 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2020-07-31 16:50:03 +02:00
|
|
|
case ClangCompletionContextAnalyzer::CompleteNone:
|
2015-05-08 15:48:17 +02:00
|
|
|
default:
|
|
|
|
|
break;
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
2018-11-04 21:44:21 +01:00
|
|
|
return nullptr;
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-08 14:07:13 +02:00
|
|
|
int ClangCompletionAssistProcessor::startOfOperator(int positionInDocument,
|
2013-12-10 14:37:32 +01:00
|
|
|
unsigned *kind,
|
|
|
|
|
bool wantFunctionCall) const
|
|
|
|
|
{
|
2015-07-08 14:07:13 +02:00
|
|
|
auto activationSequence = m_interface->textAt(positionInDocument - 3, 3);
|
|
|
|
|
ActivationSequenceProcessor activationSequenceProcessor(activationSequence,
|
|
|
|
|
positionInDocument,
|
|
|
|
|
wantFunctionCall);
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2015-07-08 14:07:13 +02:00
|
|
|
*kind = activationSequenceProcessor.completionKind();
|
2015-07-23 16:06:48 +02:00
|
|
|
int start = activationSequenceProcessor.operatorStartPosition();
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2016-03-29 16:26:39 +02:00
|
|
|
CppCompletionAssistProcessor::startOfOperator(m_interface->textDocument(),
|
|
|
|
|
positionInDocument,
|
|
|
|
|
kind,
|
|
|
|
|
start,
|
|
|
|
|
m_interface->languageFeatures());
|
2013-12-10 14:37:32 +01:00
|
|
|
|
|
|
|
|
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 {
|
2020-08-03 16:19:19 +02:00
|
|
|
// Trigger completion after n characters of a name have been typed, when not editing an existing name
|
2013-12-10 14:37:32 +01:00
|
|
|
QChar characterUnderCursor = m_interface->characterAt(pos);
|
|
|
|
|
if (!characterUnderCursor.isLetterOrNumber() && characterUnderCursor != QLatin1Char('_')) {
|
|
|
|
|
const int startOfName = findStartOfName(pos);
|
2020-08-03 16:19:19 +02:00
|
|
|
if (pos - startOfName >= TextEditorSettings::completionSettings().m_characterThreshold) {
|
2013-12-10 14:37:32 +01:00
|
|
|
const QChar firstCharacter = m_interface->characterAt(startOfName);
|
|
|
|
|
if (firstCharacter.isLetter() || firstCharacter == QLatin1Char('_')) {
|
|
|
|
|
// Finally check that we're not inside a comment or string (code copied from startOfOperator)
|
|
|
|
|
QTextCursor tc(m_interface->textDocument());
|
|
|
|
|
tc.setPosition(pos);
|
|
|
|
|
|
|
|
|
|
SimpleLexer tokenize;
|
|
|
|
|
LanguageFeatures lf = tokenize.languageFeatures();
|
|
|
|
|
lf.qtMocRunEnabled = true;
|
|
|
|
|
lf.objCEnabled = true;
|
|
|
|
|
tokenize.setLanguageFeatures(lf);
|
|
|
|
|
tokenize.setSkipComments(false);
|
2014-11-06 09:35:29 +01:00
|
|
|
const Tokens &tokens = tokenize(tc.block().text(), BackwardsScanner::previousBlockState(tc.block()));
|
2013-12-10 14:37:32 +01:00
|
|
|
const int tokenIdx = SimpleLexer::tokenBefore(tokens, qMax(0, tc.positionInBlock() - 1));
|
2015-02-04 17:01:07 +02:00
|
|
|
const Token tk = (tokenIdx == -1) ? Token() : tokens.at(tokenIdx);
|
2013-12-10 14:37:32 +01:00
|
|
|
|
|
|
|
|
if (!tk.isComment() && !tk.isLiteral()) {
|
|
|
|
|
return true;
|
|
|
|
|
} else if (tk.isLiteral()
|
|
|
|
|
&& tokens.size() == 3
|
|
|
|
|
&& tokens.at(0).kind() == T_POUND
|
|
|
|
|
&& tokens.at(1).kind() == T_IDENTIFIER) {
|
|
|
|
|
const QString &line = tc.block().text();
|
2015-02-04 17:01:07 +02:00
|
|
|
const Token &idToken = tokens.at(1);
|
2020-09-18 12:39:32 +02:00
|
|
|
const QStringView &identifier = Utils::midView(line,
|
|
|
|
|
idToken.utf16charsBegin(),
|
|
|
|
|
idToken.utf16chars());
|
2013-12-10 14:37:32 +01:00
|
|
|
if (identifier == QLatin1String("include")
|
|
|
|
|
|| identifier == QLatin1String("include_next")
|
|
|
|
|
|| (m_interface->objcEnabled() && identifier == QLatin1String("import"))) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Creates completion proposals for #include and given cursor
|
|
|
|
|
* @param cursor - cursor placed after opening bracked or quote
|
|
|
|
|
* @return false if completions list is empty
|
|
|
|
|
*/
|
|
|
|
|
bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
QString directoryPrefix;
|
2015-05-08 15:48:17 +02:00
|
|
|
if (m_completionOperator == T_SLASH) {
|
2013-12-10 14:37:32 +01:00
|
|
|
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('<'));
|
2015-05-08 15:48:17 +02:00
|
|
|
m_completionOperator = T_ANGLE_STRING_LITERAL;
|
2013-12-10 14:37:32 +01:00
|
|
|
} else {
|
2015-05-08 15:48:17 +02:00
|
|
|
m_completionOperator = T_STRING_LITERAL;
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
if (startCharPos != -1)
|
|
|
|
|
directoryPrefix = sel.mid(startCharPos + 1, sel.length() - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make completion for all relevant includes
|
2018-09-03 16:10:43 +02:00
|
|
|
ProjectExplorer::HeaderPaths headerPaths = m_interface->headerPaths();
|
2020-09-02 12:29:23 +02:00
|
|
|
const ProjectExplorer::HeaderPath currentFilePath(m_interface->filePath().toFileInfo().path(),
|
|
|
|
|
ProjectExplorer::HeaderPathType::User);
|
2014-06-25 17:23:19 +02:00
|
|
|
if (!headerPaths.contains(currentFilePath))
|
|
|
|
|
headerPaths.append(currentFilePath);
|
2013-12-10 14:37:32 +01:00
|
|
|
|
2017-03-02 12:07:11 +01:00
|
|
|
const ::Utils::MimeType mimeType = ::Utils::mimeTypeForName("text/x-c++hdr");
|
2013-12-10 14:37:32 +01:00
|
|
|
const QStringList suffixes = mimeType.suffixes();
|
|
|
|
|
|
2018-09-03 16:10:43 +02:00
|
|
|
foreach (const ProjectExplorer::HeaderPath &headerPath, headerPaths) {
|
2014-06-25 17:23:19 +02:00
|
|
|
QString realPath = headerPath.path;
|
2013-12-10 14:37:32 +01:00
|
|
|
if (!directoryPrefix.isEmpty()) {
|
|
|
|
|
realPath += QLatin1Char('/');
|
|
|
|
|
realPath += directoryPrefix;
|
2018-09-13 12:58:38 +02:00
|
|
|
if (headerPath.type == ProjectExplorer::HeaderPathType::Framework)
|
2014-06-25 17:23:19 +02:00
|
|
|
realPath += QLatin1String(".framework/Headers");
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
completeIncludePath(realPath, suffixes);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-19 15:04:11 +01:00
|
|
|
QList<QPair<AssistProposalItemInterface *, QString>> completionsForSorting;
|
|
|
|
|
for (AssistProposalItemInterface * const item : qAsConst(m_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)
|
|
|
|
|
m_completions[i] = completionsForSorting[i].first;
|
2018-10-16 14:54:26 +02:00
|
|
|
|
2013-12-10 14:37:32 +01:00
|
|
|
return !m_completions.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
bool ClangCompletionAssistProcessor::completeInclude(int position)
|
|
|
|
|
{
|
|
|
|
|
QTextCursor textCursor(m_interface->textDocument()); // TODO: Simplify, move into function
|
|
|
|
|
textCursor.setPosition(position);
|
|
|
|
|
return completeInclude(textCursor);
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-10 14:37:32 +01:00
|
|
|
/**
|
|
|
|
|
* @brief Adds #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
|
|
|
|
|
*/
|
|
|
|
|
void ClangCompletionAssistProcessor::completeIncludePath(const QString &realPath,
|
|
|
|
|
const QStringList &suffixes)
|
|
|
|
|
{
|
|
|
|
|
QDirIterator i(realPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
2014-03-03 15:53:34 +01:00
|
|
|
//: Parent folder for proposed #include completion
|
|
|
|
|
const QString hint = tr("Location: %1").arg(QDir::toNativeSeparators(QDir::cleanPath(realPath)));
|
2013-12-10 14:37:32 +01:00
|
|
|
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('/');
|
|
|
|
|
|
2016-01-28 16:39:05 +01:00
|
|
|
auto *item = new ClangPreprocessorAssistProposalItem;
|
2013-12-10 14:37:32 +01:00
|
|
|
item->setText(text);
|
2016-01-28 16:39:05 +01:00
|
|
|
item->setDetail(hint);
|
2020-07-06 15:49:35 +02:00
|
|
|
item->setIcon(CPlusPlus::Icons::keywordIcon());
|
2016-01-28 16:39:05 +01:00
|
|
|
item->setCompletionOperator(m_completionOperator);
|
2013-12-10 14:37:32 +01:00
|
|
|
m_completions.append(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
bool ClangCompletionAssistProcessor::completePreprocessorDirectives()
|
2013-12-10 14:37:32 +01:00
|
|
|
{
|
|
|
|
|
foreach (const QString &preprocessorCompletion, m_preprocessorCompletions)
|
|
|
|
|
addCompletionItem(preprocessorCompletion,
|
2020-07-06 15:49:35 +02:00
|
|
|
Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro));
|
2013-12-10 14:37:32 +01:00
|
|
|
|
|
|
|
|
if (m_interface->objcEnabled())
|
|
|
|
|
addCompletionItem(QLatin1String("import"),
|
2020-07-06 15:49:35 +02:00
|
|
|
Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Macro));
|
2015-05-08 15:48:17 +02:00
|
|
|
|
|
|
|
|
return !m_completions.isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ClangCompletionAssistProcessor::completeDoxygenKeywords()
|
|
|
|
|
{
|
2015-07-06 12:05:02 +02:00
|
|
|
for (int i = 1; i < CppTools::T_DOXY_LAST_TAG; ++i)
|
2020-07-06 15:49:35 +02:00
|
|
|
addCompletionItem(QString::fromLatin1(CppTools::doxygenTagSpell(i)), CPlusPlus::Icons::keywordIcon());
|
2015-05-08 15:48:17 +02:00
|
|
|
return !m_completions.isEmpty();
|
2013-12-10 14:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ClangCompletionAssistProcessor::addCompletionItem(const QString &text,
|
|
|
|
|
const QIcon &icon,
|
2016-01-28 15:35:18 +01:00
|
|
|
int order)
|
2013-12-10 14:37:32 +01:00
|
|
|
{
|
2016-01-28 16:39:05 +01:00
|
|
|
auto *item = new ClangPreprocessorAssistProposalItem;
|
2013-12-10 14:37:32 +01:00
|
|
|
item->setText(text);
|
2016-01-28 16:39:05 +01:00
|
|
|
item->setIcon(icon);
|
2013-12-10 14:37:32 +01:00
|
|
|
item->setOrder(order);
|
2016-01-28 16:39:05 +01:00
|
|
|
item->setCompletionOperator(m_completionOperator);
|
2013-12-10 14:37:32 +01:00
|
|
|
m_completions.append(item);
|
|
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2015-07-20 13:24:06 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-13 15:56:41 +02:00
|
|
|
void ClangCompletionAssistProcessor::sendFileContent(const QByteArray &customFileContent)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2015-07-14 16:40:17 +02:00
|
|
|
// TODO: Revert custom modification after the completions
|
2015-07-20 13:24:06 +02:00
|
|
|
const UnsavedFileContentInfo info = unsavedFileContent(customFileContent);
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2017-09-25 15:10:48 +02:00
|
|
|
BackendCommunicator &communicator = m_interface->communicator();
|
2020-09-02 12:29:23 +02:00
|
|
|
communicator.documentsChanged({{m_interface->filePath().toString(),
|
2018-05-31 15:21:53 +02:00
|
|
|
Utf8String::fromByteArray(info.unsavedContent),
|
|
|
|
|
info.isDocumentModified,
|
|
|
|
|
uint(m_interface->textDocument()->revision())}});
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
2015-09-28 17:42:43 +02:00
|
|
|
namespace {
|
|
|
|
|
bool shouldSendDocumentForCompletion(const QString &filePath,
|
|
|
|
|
int completionPosition)
|
|
|
|
|
{
|
2020-07-06 15:49:35 +02:00
|
|
|
CppTools::CppEditorDocumentHandle *document = cppDocument(filePath);
|
2015-09-28 17:42:43 +02:00
|
|
|
|
|
|
|
|
if (document) {
|
2015-10-13 15:56:41 +02:00
|
|
|
auto &sendTracker = document->sendTracker();
|
|
|
|
|
return sendTracker.shouldSendRevisionWithCompletionPosition(int(document->revision()),
|
2015-09-28 17:42:43 +02:00
|
|
|
completionPosition);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-01 13:21:02 +01:00
|
|
|
bool shouldSendCodeCompletion(const QString &filePath,
|
|
|
|
|
int completionPosition)
|
2015-09-28 17:42:43 +02:00
|
|
|
{
|
2020-07-06 15:49:35 +02:00
|
|
|
CppTools::CppEditorDocumentHandle *document = cppDocument(filePath);
|
2015-09-28 17:42:43 +02:00
|
|
|
|
|
|
|
|
if (document) {
|
2015-12-01 13:21:02 +01:00
|
|
|
auto &sendTracker = document->sendTracker();
|
|
|
|
|
return sendTracker.shouldSendCompletion(completionPosition);
|
2015-09-28 17:42:43 +02:00
|
|
|
}
|
2015-12-01 13:21:02 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setLastDocumentRevision(const QString &filePath)
|
|
|
|
|
{
|
2020-07-06 15:49:35 +02:00
|
|
|
CppTools::CppEditorDocumentHandle *document = cppDocument(filePath);
|
2015-12-01 13:21:02 +01:00
|
|
|
|
|
|
|
|
if (document)
|
|
|
|
|
document->sendTracker().setLastSentRevision(int(document->revision()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setLastCompletionPosition(const QString &filePath,
|
|
|
|
|
int completionPosition)
|
|
|
|
|
{
|
2020-07-06 15:49:35 +02:00
|
|
|
CppTools::CppEditorDocumentHandle *document = cppDocument(filePath);
|
2015-12-01 13:21:02 +01:00
|
|
|
|
|
|
|
|
if (document)
|
|
|
|
|
document->sendTracker().setLastCompletionPosition(completionPosition);
|
2015-09-28 17:42:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2017-08-10 10:24:54 +02:00
|
|
|
ClangCompletionAssistProcessor::Position
|
|
|
|
|
ClangCompletionAssistProcessor::extractLineColumn(int position)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2017-08-10 10:24:54 +02:00
|
|
|
if (position < 0)
|
|
|
|
|
return {-1, -1};
|
|
|
|
|
|
|
|
|
|
int line = -1, column = -1;
|
2020-07-06 15:49:35 +02:00
|
|
|
Utils::Text::convertPosition(m_interface->textDocument(), position, &line, &column);
|
2018-01-26 17:04:38 +01:00
|
|
|
|
2020-07-06 15:49:35 +02:00
|
|
|
column = clangColumn(m_interface->textDocument()->findBlock(position), column);
|
2017-08-10 10:24:54 +02:00
|
|
|
return {line, column};
|
|
|
|
|
}
|
2015-05-08 15:48:17 +02:00
|
|
|
|
2017-08-10 10:24:54 +02:00
|
|
|
bool ClangCompletionAssistProcessor::sendCompletionRequest(int position,
|
|
|
|
|
const QByteArray &customFileContent,
|
|
|
|
|
int functionNameStartPosition)
|
|
|
|
|
{
|
2020-09-02 12:29:23 +02:00
|
|
|
const QString filePath = m_interface->filePath().toString();
|
2015-09-28 17:42:43 +02:00
|
|
|
|
2017-09-25 15:10:48 +02:00
|
|
|
auto &communicator = m_interface->communicator();
|
2015-12-01 13:21:02 +01:00
|
|
|
|
2017-09-25 15:10:48 +02:00
|
|
|
if (shouldSendCodeCompletion(filePath, position) || communicator.isNotWaitingForCompletion()) {
|
2015-12-01 13:21:02 +01:00
|
|
|
if (shouldSendDocumentForCompletion(filePath, position)) {
|
|
|
|
|
sendFileContent(customFileContent);
|
|
|
|
|
setLastDocumentRevision(filePath);
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-10 10:24:54 +02:00
|
|
|
const Position cursorPosition = extractLineColumn(position);
|
|
|
|
|
const Position functionNameStart = extractLineColumn(functionNameStartPosition);
|
2018-05-31 15:21:53 +02:00
|
|
|
communicator.requestCompletions(this,
|
|
|
|
|
filePath,
|
|
|
|
|
uint(cursorPosition.line),
|
|
|
|
|
uint(cursorPosition.column),
|
|
|
|
|
functionNameStart.line,
|
|
|
|
|
functionNameStart.column);
|
2015-12-01 13:21:02 +01:00
|
|
|
setLastCompletionPosition(filePath, position);
|
2020-08-25 14:32:56 +02:00
|
|
|
if (m_sentRequestType == NormalCompletion) {
|
|
|
|
|
if (!customFileContent.isEmpty())
|
|
|
|
|
m_content = customFileContent;
|
|
|
|
|
else if (const CppTools::CppEditorDocumentHandle * const doc = cppDocument(filePath))
|
|
|
|
|
m_content = doc->contents();
|
|
|
|
|
m_position = position;
|
|
|
|
|
}
|
2015-12-01 13:21:02 +01:00
|
|
|
return true;
|
2015-09-28 17:42:43 +02:00
|
|
|
}
|
|
|
|
|
|
2015-12-01 13:21:02 +01:00
|
|
|
return false;
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-15 14:35:58 +02:00
|
|
|
TextEditor::IAssistProposal *ClangCompletionAssistProcessor::createProposal()
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2018-03-01 12:33:48 +01:00
|
|
|
m_requestSent = false;
|
2018-06-15 14:35:58 +02:00
|
|
|
TextEditor::GenericProposalModelPtr model(new ClangAssistProposalModel());
|
2015-05-08 15:48:17 +02:00
|
|
|
model->loadContent(m_completions);
|
2018-06-15 14:35:58 +02:00
|
|
|
return new GenericProposal(m_positionForProposal, model);
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-28 17:46:40 +02:00
|
|
|
IAssistProposal *ClangCompletionAssistProcessor::createFunctionHintProposal(
|
2018-03-01 12:33:48 +01:00
|
|
|
const ClangBackEnd::CodeCompletions &completions)
|
2015-05-08 15:48:17 +02:00
|
|
|
{
|
2018-03-01 12:33:48 +01:00
|
|
|
m_requestSent = false;
|
2018-02-14 14:32:51 +01:00
|
|
|
TextEditor::FunctionHintProposalModelPtr model(new ClangFunctionHintModel(completions));
|
|
|
|
|
return new FunctionHintProposal(m_positionForProposal, model);
|
2015-05-08 15:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-19 07:44:46 +02:00
|
|
|
void ClangCompletionAssistProcessor::cancel()
|
|
|
|
|
{
|
|
|
|
|
m_interface->communicator().cancelCompletions(this);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 15:48:17 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace ClangCodeModel
|