diff --git a/codecompletion/context.h b/codecompletion/context.h --- a/codecompletion/context.h +++ b/codecompletion/context.h @@ -54,6 +54,8 @@ KDevelop::DeclarationPointer lastDeclaration(const QString& expression); + QList getImportableDeclarations(KDevelop::Declaration *sourceDeclaration); + QList importAndMemberCompletion(); QList normalCompletion(); diff --git a/codecompletion/context.cpp b/codecompletion/context.cpp --- a/codecompletion/context.cpp +++ b/codecompletion/context.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "parser/golexer.h" #include "parser/goparser.h" @@ -149,80 +150,69 @@ return items; } +QList CodeCompletionContext::getImportableDeclarations(Declaration *sourceDeclaration) +{ + QList items; + auto declarations = getDeclarations(sourceDeclaration->qualifiedIdentifier(), m_duContext.data()); + for(Declaration* declaration : declarations) + { + DUContext* context = declaration->internalContext(); + if(!context) continue; + auto innerDeclarations = context->allDeclarations(CursorInRevision::invalid(), sourceDeclaration->topContext(), false); + for(const QPair innerDeclaration : innerDeclarations) + { + if(innerDeclaration.first == declaration) + continue; + QualifiedIdentifier fullname = innerDeclaration.first->qualifiedIdentifier(); + Identifier identifier = fullname.last(); + if(identifier.toString().size() <= 0) + continue; + //import only declarations that start with capital letter(Go language rule) + if(m_duContext->topContext() != declaration->topContext()) + if(!identifier.toString().at(0).isLetter() || (identifier.toString().at(0) != identifier.toString().at(0).toUpper())) + continue; + items << itemForDeclaration(innerDeclaration); + } + } + return items; +} QList< CompletionTreeItemPointer > CodeCompletionContext::importAndMemberCompletion() { QList items; - AbstractType::Ptr lasttype = lastType(m_text.left(m_text.size()-1)); - if(lasttype) + AbstractType::Ptr type = lastType(m_text.left(m_text.size()-1)); + + if(type) { - //evaluate pointers - if(fastCast(lasttype.constData())) - { - DUChainReadLocker lock; - PointerType* ptype = fastCast(lasttype.constData()); - if(ptype->baseType()) - lasttype = ptype->baseType(); - } - if(fastCast(lasttype.constData())) - {//we have to look for namespace declarations - //TODO handle namespace aliases - DUChainReadLocker lock; - Declaration* lastdeclaration = fastCast(lasttype.constData())->declaration(m_duContext->topContext()); - //if(lastdeclaration->kind() == Declaration::Namespace) - //{ - //namespace could be splitted into multiple contexts - //auto decls = m_duContext->findDeclarations(lastdeclaration->qualifiedIdentifier()); - auto decls = getDeclarations(lastdeclaration->qualifiedIdentifier(), m_duContext.data()); - for(Declaration* declaration : decls) - { - DUContext* context = declaration->internalContext(); - if(!context) continue; - auto declarations = context->allDeclarations(CursorInRevision::invalid(), declaration->topContext(), false); - for(const QPair decl : declarations) - { - if(decl.first == declaration) - continue; - QualifiedIdentifier fullname = decl.first->qualifiedIdentifier(); - Identifier ident = fullname.last(); - if(ident.toString().size() <= 0) - continue; - //import only declarations that start with capital letter(Go language rule) - if(m_duContext->topContext() != declaration->topContext()) - if(!ident.toString().at(0).isLetter() || (ident.toString().at(0) != ident.toString().at(0).toUpper())) - continue; - items << itemForDeclaration(decl); - } - } - // } - } - //this construction will descend through type hierarchy till it hits basic types - //e.g. type mystruct struct{}; type mystruct2 mystruct; ... - int count = 0; - do { - count++; - GoStructureType* structure = fastCast(lasttype.constData()); - if(structure) - {//get members - DUContext* context = structure->context(); - DUChainReadLocker lock; - //auto declarations = context->findDeclarations(identifierForNode(node->selector)); - auto declarations = context->allDeclarations(CursorInRevision::invalid(), m_duContext->topContext(), false); - lock.unlock(); - for(const QPair &decl : declarations) - { - items << itemForDeclaration(decl); - } - } - StructureType* identType = fastCast(lasttype.constData()); - if(identType) - { - DUChainReadLocker lock; - lasttype = identType->declaration(m_duContext->topContext())->abstractType(); - }else - break; - - }while(lasttype && count<100); + if(auto ptype = fastCast(type.constData())) + { + DUChainReadLocker lock; + if(ptype->baseType()) + { + type = ptype->baseType(); + } + } + if(auto structure = fastCast(type.constData())) + { + DUChainReadLocker lock; + Declaration* declaration = structure->declaration(m_duContext->topContext()); + if(declaration) + { + items << getImportableDeclarations(declaration); + } + } + if(auto structure = fastCast(type.constData())) + { + DUContext* context = structure->context(); + DUChainReadLocker lock; + + auto declarations = context->allDeclarations(CursorInRevision::invalid(), m_duContext->topContext(), false); + lock.unlock(); + for(const QPair &decl : declarations) + { + items << itemForDeclaration(decl); + } + } } return items; } diff --git a/codecompletion/tests/testcompletion.cpp b/codecompletion/tests/testcompletion.cpp --- a/codecompletion/tests/testcompletion.cpp +++ b/codecompletion/tests/testcompletion.cpp @@ -171,7 +171,9 @@ QTest::newRow("var") << "var myvar func(a int);" << "myvar(%CURSOR)" << " myvar (int)" << 1 << 4; QTest::newRow("var 2") << "func functest(t int) rune;" << "myvar := functest; myvar(%CURSOR)" << "rune myvar (int)" << 1 << 5; QTest::newRow("struct") << "type mytype struct { f func(t int) rune; };" << "var myvar mytype; myvar.f(%CURSOR)" << "rune f (int)" << 1 << 5; - QTest::newRow("method") << "type mytype int; func (m mytype) myfunc(c rune) {};" << "var myvar mytype; myvar.myfunc(%CURSOR)" << " myfunc (rune c)" << 1 << 6; + QTest::newRow("method") << "type mytype int; func (m mytype) myfunc(c rune) {};" << "var myvar mytype; myvar.myfunc(%CURSOR)" << " myfunc (rune c)" << 1 << 5; + QTest::newRow("method of embedded struct declared after method") << "type mytype struct {}; func (m mytype) myfunc(c rune) {}; type mytype2 struct {*mytype};" << "var myvar mytype2; myvar.myfunc(%CURSOR)" << " myfunc (rune c)" << 1 << 6; + QTest::newRow("method of embedded struct declared before method") << "type mytype struct {}; func (m mytype) myfunc(c rune) {}; type mytype2 struct {*mytype};" << "var myvar mytype2; myvar.myfunc(%CURSOR)" << " myfunc (rune c)" << 1 << 6; QTest::newRow("paren") << "func myfunc(a int) {};" << "(myfunc)(%CURSOR)" << " myfunc (int a)" << 1 << 4; QTest::newRow("nested") << "func myfunc(a int) {};" << "f := myfunc; myfunc(f(%CURSOR))" << " myfunc (int a)" << 2 << 6; QTest::newRow("nested 2") << "func myfunc(a int) {};" << "f := myfunc; myfunc(f(%CURSOR))" << " f (int)" << 1 << 6; diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -248,6 +248,8 @@ void DeclarationBuilder::visitMethodDeclaration(go::MethodDeclarationAst* node) { Declaration* declaration=0; + bool openedDeclaration = false; + if(node->methodRecv) { go::IdentifierAst* actualtype=0; @@ -258,10 +260,29 @@ else actualtype = node->methodRecv->nameOrType; DUChainWriteLocker lock; - declaration = openDeclaration(identifierForNode(actualtype), editorFindRange(actualtype, 0)); - declaration->setKind(Declaration::Namespace); - openContext(node, editorFindRange(node, 0), DUContext::Namespace, identifierForNode(actualtype)); - declaration->setInternalContext(currentContext()); + + QList declarations = currentContext()->findLocalDeclarations(identifierForNode(actualtype).last(), CursorInRevision::invalid(), this->topContext()); + + if(declarations.size() >= 1) + { + declaration = declarations.at(0); + } + else + { + openedDeclaration = true; + declaration = openDeclaration(identifierForNode(actualtype), editorFindRange(actualtype, 0)); + declaration->setKind(Declaration::Type); + } + + if(declaration->internalContext()) + { + openContext(declaration->internalContext()); + } + else + { + openContext(node, editorFindRange(node, 0), DUContext::Namespace, identifierForNode(actualtype)); + declaration->setInternalContext(currentContext()); + } } auto functionDeclaration = buildFunction(node->signature, node->body, node->methodName, m_session->commentBeforeToken(node->startToken-1)); @@ -284,7 +305,10 @@ } closeContext(); //namespace - closeDeclaration(); //namespace declaration + if(openedDeclaration) + { + closeDeclaration(); + } } void DeclarationBuilder::visitTypeSpec(go::TypeSpecAst* node) @@ -295,10 +319,10 @@ if(comment.size() == 0) comment = m_lastTypeComment; setComment(comment); - Declaration* decl; + ClassDeclaration* decl; { DUChainWriteLocker lock; - decl = openDeclaration(identifierForNode(node->name), editorFindRange(node->name, 0)); + decl = openDeclaration(identifierForNode(node->name), editorFindRange(node->name, 0)); //decl->setKind(Declaration::Namespace); decl->setKind(Declaration::Type); //force direct here because otherwise DeclarationId will mess up actual type declaration and method declarations @@ -312,6 +336,29 @@ decl->setType(lastType()); decl->setIsTypeAlias(true); + decl->setInternalContext(lastContext()); + if(node->type && node->type->complexType && node->type->complexType->structType && node->type->complexType->structType->fieldDeclSequence) + { + auto sequence = node->type->complexType->structType->fieldDeclSequence; + for(int i = 0; icount(); ++i) + { + auto anonymousField = sequence->at(i)->element->anonFieldStar; + if(anonymousField) + { + visitTypeName(anonymousField->typeName); + + StructureType::Ptr baseClassType = lastType().cast(); + if(baseClassType) + { + auto baseClassDeclaration = baseClassType->declaration(topContext()); + if(baseClassDeclaration && baseClassDeclaration->internalContext()) + { + decl->internalContext()->addImportedParentContext(baseClassType->declaration(topContext())->internalContext()); + } + } + } + } + } closeDeclaration(); //qCDebug(DUCHAIN) << "Type" << identifierForNode(node->name) << " exit"; }