Commit 06063496 authored by Sergey Kalinichev's avatar Sergey Kalinichev

Added look-ahead matching code completion

Known shortcomings:

1)Auto type (and many other types) is not exposed through LibClang. So
we assign DelayedType to it instead of IdentifiedType, therefore no
declaration attached to the type, hence no look-ahead completion.

2)LibClang missing API to determine expected code completion type. To
workaround it, now we use types of declarations from the best mathes
group. Therefore if no declarations found with a high enough priority,
no items will be added to the look-ahead completion group.

As a result the look-ahead completion works only in the simpliest cases.

REVIEW: 124601
parent fcf58cd9
......@@ -44,6 +44,7 @@ namespace
const QString settingsGroup = QStringLiteral("Clang Settings");
const QString macros = QStringLiteral("macros");
const QString lookAhead = QStringLiteral("lookAhead");
const QString forwardDeclare = QStringLiteral("forwardDeclare");
......@@ -63,6 +64,7 @@ CodeCompletionSettings readCodeCompletionSettings(KConfig* cfg)
CodeCompletionSettings settings;
settings.macros = grp.readEntry(macros, true);
settings.lookAhead = grp.readEntry(lookAhead, false);
return settings;
}
......@@ -82,6 +84,13 @@ AssistantsSettings ClangSettingsManager::assistantsSettings() const
CodeCompletionSettings ClangSettingsManager::codeCompletionSettings() const
{
if (m_enableTesting) {
CodeCompletionSettings settings;
settings.lookAhead = true;
settings.macros = true;
return settings;
}
auto cfg = ICore::self()->activeSession()->config();
return readCodeCompletionSettings(cfg.data());
}
......
......@@ -47,6 +47,7 @@ Q_DECLARE_METATYPE(ParserSettings);
struct CodeCompletionSettings
{
bool macros = true;
bool lookAhead = false;
};
struct AssistantsSettings
......@@ -67,6 +68,9 @@ public:
private:
ClangSettingsManager();
bool m_enableTesting = false;
friend class TestCodeCompletion;
};
#endif // CLANGSETTINGSMANAGER_H
......@@ -8,6 +8,9 @@
<entry name="macros" key="macros" type="Bool">
<default>true</default>
</entry>
<entry name="lookAhead" key="lookAhead" type="Bool">
<default>false</default>
</entry>
<entry name="forwardDeclare" key="forwardDeclare" type="Bool">
<default>true</default>
......
......@@ -45,6 +45,19 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="kcfg_lookAhead">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Enable Look-ahead code-completion</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
......
......@@ -26,9 +26,13 @@
#include <language/duchain/topducontext.h>
#include <language/duchain/declaration.h>
#include <language/duchain/classmemberdeclaration.h>
#include <language/duchain/classdeclaration.h>
#include <language/duchain/duchainutils.h>
#include <language/duchain/types/integraltype.h>
#include <language/duchain/types/functiontype.h>
#include <language/duchain/types/pointertype.h>
#include <language/duchain/types/typealiastype.h>
#include <language/duchain/types/typeutils.h>
#include <language/interfaces/iastcontainer.h>
#include <language/codecompletion/codecompletionitem.h>
#include <language/codecompletion/codecompletionmodel.h>
......@@ -539,6 +543,119 @@ Declaration* classDeclarationForContext(const DUContextPointer& context, const C
return parent ? parent->owner() : nullptr;
}
class LookAheadItemMatcher
{
public:
LookAheadItemMatcher(const TopDUContextPointer& ctx)
: m_topContext(ctx)
, m_enabled(ClangSettingsManager::self()->codeCompletionSettings().lookAhead)
{}
/// Adds all local declarations for @p declaration into possible look-ahead items.
void addDeclarations(Declaration* declaration)
{
if (!m_enabled) {
return;
}
if (declaration->kind() != Declaration::Instance) {
return;
}
auto type = typeForDeclaration(declaration);
auto identifiedType = dynamic_cast<const IdentifiedType*>(type.data());
if (!identifiedType) {
return;
}
addDeclarationsForType(identifiedType, declaration);
}
/// Add type for matching. This type'll be used for filtering look-ahead items
/// Only items with @p type will be returned through @sa matchedItems
void addMatchedType(const IndexedType& type)
{
matchedTypes.insert(type);
}
/// @return look-ahead items that math given types. @sa addMatchedType
QList<CompletionTreeItemPointer> matchedItems()
{
QList<CompletionTreeItemPointer> lookAheadItems;
for (auto it = possibleLookAheadDeclarations.begin(); it != possibleLookAheadDeclarations.end(); it++) {
auto decl = it.key().first;
if (matchedTypes.contains(decl->indexedType())) {
QString text = it.value() + decl->identifier().toString();
auto item = new DeclarationItem(decl, text, {}, text);
item->setMatchQuality(8);
lookAheadItems.append(CompletionTreeItemPointer(item));
}
}
return lookAheadItems;
}
private:
AbstractType::Ptr typeForDeclaration(const Declaration* decl)
{
return TypeUtils::targetType(decl->abstractType(), m_topContext.data());
}
void addDeclarationsForType(const IdentifiedType* identifiedType, Declaration* declaration)
{
if (auto typeDecl = identifiedType->declaration(m_topContext.data())) {
if (dynamic_cast<ClassDeclaration*>(typeDecl->logicalDeclaration(m_topContext.data()))) {
if (!typeDecl->internalContext()) {
return;
}
for (auto localDecl : typeDecl->internalContext()->localDeclarations()) {
if(localDecl->identifier().isEmpty()){
continue;
}
if(auto classMember = dynamic_cast<ClassMemberDeclaration*>(localDecl)){
// TODO: Also add protected/private members if completion is inside this class context.
if(classMember->accessPolicy() != Declaration::Public){
continue;
}
}
if(!declaration->abstractType()){
continue;
}
if (declaration->abstractType()->whichType() == AbstractType::TypeIntegral) {
if (auto integralType = declaration->abstractType().cast<IntegralType>()) {
if (integralType->dataType() == IntegralType::TypeVoid) {
continue;
}
}
}
QString access = declaration->abstractType()->whichType() == AbstractType::TypePointer
? QStringLiteral("->") : QStringLiteral(".");
possibleLookAheadDeclarations.insert({localDecl, declaration}, declaration->identifier().toString() + access);
}
}
}
}
// Declaration and it's context
typedef QPair<Declaration*, Declaration*> DeclarationContext;
/// Types of declarations that look-ahead completion items can have
QSet<IndexedType> matchedTypes;
// List of declarations that can be added to the Look Ahead group
// QString represents context and access (e.g. "classInstance->")
QHash<DeclarationContext, QString> possibleLookAheadDeclarations;
TopDUContextPointer m_topContext;
bool m_enabled;
};
}
ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context,
......@@ -621,6 +738,8 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(boo
QSet<Declaration*> handled;
LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext()));
clangDebug() << "Clang found" << m_results->NumResults << "completion results";
for (uint i = 0; i < m_results->NumResults; ++i) {
......@@ -782,10 +901,14 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(boo
if (bestMatch && !found->indexedIdentifier().identifier().toString().startsWith(QLatin1String("__")) ) {
const int matchQuality = codeCompletionPriorityToMatchQuality(completionPriority);
declarationItem->setMatchQuality(matchQuality);
// TODO: LibClang missing API to determine expected code completion type.
lookAheadMatcher.addMatchedType(found->indexedType());
} else {
declarationItem->setInheritanceDepth(completionPriority);
}
lookAheadMatcher.addDeclarations(found);
}
item = declarationItem;
} else {
// still, let's trust that Clang found something useful and put it into the completion result list
......@@ -824,8 +947,9 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(boo
addOverwritableItems();
eventuallyAddGroup(i18n("Special"), 700, specialItems);
eventuallyAddGroup(i18n("Macros"), 900, macros);
eventuallyAddGroup(i18n("Builtin"), 800, builtin);
eventuallyAddGroup(i18n("Look-ahead Matches"), 800, lookAheadMatcher.matchedItems());
eventuallyAddGroup(i18n("Builtin"), 900, builtin);
eventuallyAddGroup(i18n("Macros"), 1000, macros);
return items;
}
......
......@@ -36,6 +36,7 @@
#include "codecompletion/completionhelper.h"
#include "codecompletion/context.h"
#include "codecompletion/includepathcompletioncontext.h"
#include "../clangsettings/clangsettingsmanager.h"
#include <KTextEditor/Editor>
#include <KTextEditor/Document>
......@@ -102,6 +103,8 @@ void TestCodeCompletion::initTestCase()
QVERIFY(qputenv("KDEV_DISABLE_PLUGINS", "kdevcppsupport"));
AutoTestShell::init();
TestCore::initialize(Core::NoUi);
ClangSettingsManager::self()->m_enableTesting = true;
}
void TestCodeCompletion::cleanupTestCase()
......@@ -148,6 +151,9 @@ void executeCompletionTest(const QString& code, const CompletionItems& expectedC
}
tester.names.sort();
QEXPECT_FAIL("look-ahead function primary type argument", "No API in LibClang to determine expected code completion type", Continue);
QEXPECT_FAIL("look-ahead template", "Templates not supported. Template type inaccessible through LibClang", Continue);
QEXPECT_FAIL("look-ahead auto item", "Auto type, like many other types, is not exposed through LibClang. We assign DelayedType to it instead of IdentifiedType", Continue);
QCOMPARE(tester.names, expectedCompletionItems.completions);
}
......@@ -325,6 +331,78 @@ void TestCodeCompletion::testClangCodeCompletion_data()
"Abc",
"f",
}};
QTest::newRow("look-ahead int")
<< "struct LookAhead { int intItem;}; int main() {LookAhead* pInstance; LookAhead instance; int i =\n }"
<< CompletionItems{{1, 0}, {
"LookAhead", "i", "instance",
"instance.intItem", "main",
"pInstance", "pInstance->intItem",
}};
QTest::newRow("look-ahead class")
<< "class Class{}; struct LookAhead {Class classItem;}; int main() {LookAhead* pInstance; LookAhead instance; Class cl =\n }"
<< CompletionItems{{1, 0}, {
"Class", "LookAhead", "cl",
"instance", "instance.classItem",
"main", "pInstance", "pInstance->classItem",
}};
QTest::newRow("look-ahead function argument")
<< "class Class{}; struct LookAhead {Class classItem;}; void function(Class cl);"
"int main() {LookAhead* pInstance; LookAhead instance; function(\n }"
<< CompletionItems{{1, 0}, {
"Class", "LookAhead", "function",
"instance", "instance.classItem",
"main", "pInstance", "pInstance->classItem",
}};
QTest::newRow("look-ahead function primary type argument")
<< "struct LookAhead {double doubleItem;}; void function(double double);"
"int main() {LookAhead* pInstance; LookAhead instance; function(\n }"
<< CompletionItems{{1, 0}, {
"LookAhead", "function", "instance",
"instance.doubleItem", "main",
"pInstance", "pInstance->doubleItem",
}};
QTest::newRow("look-ahead typedef")
<< "typedef double DOUBLE; struct LookAhead {DOUBLE doubleItem;};"
"int main() {LookAhead* pInstance; LookAhead instance; double i =\n "
<< CompletionItems{{1, 0}, {
"DOUBLE", "LookAhead", "i",
"instance", "instance.doubleItem",
"main", "pInstance", "pInstance->doubleItem",
}};
QTest::newRow("look-ahead pointer")
<< "struct LookAhead {int* pInt;};"
"int main() {LookAhead* pInstance; LookAhead instance; int* i =\n "
<< CompletionItems{{1, 0}, {
"LookAhead", "i", "instance",
"instance.pInt", "main",
"pInstance", "pInstance->pInt",
}};
QTest::newRow("look-ahead template")
<< "template <typename T> struct LookAhead {int intItem;};"
"int main() {LookAhead<int>* pInstance; LookAhead<int> instance; int i =\n "
<< CompletionItems{{1, 0}, {
"LookAhead", "i", "instance",
"instance.intItem", "main",
"pInstance", "pInstance->intItem",
}};
QTest::newRow("look-ahead item access")
<< "class Class { public: int publicInt; protected: int protectedInt; private: int privateInt;};"
"int main() {Class cl; int i =\n "
<< CompletionItems{{1, 0}, {
"Class", "cl",
"cl.publicInt",
"i", "main",
}};
QTest::newRow("look-ahead auto item")
<< "struct LookAhead { int intItem; };"
"int main() {auto instance = LookAhead(); int i = \n "
<< CompletionItems{{1, 0}, {
"LookAhead",
"i",
"instance",
"instance.intItem"
}};
}
void TestCodeCompletion::testVirtualOverride()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment