diff --git a/language/duchain/ducontext.cpp b/language/duchain/ducontext.cpp
index 8945957456..5d4b1e1d36 100644
--- a/language/duchain/ducontext.cpp
+++ b/language/duchain/ducontext.cpp
@@ -1,1706 +1,1708 @@
/* This is part of KDevelop
Copyright 2006 Hamish Rodda
Copyright 2007-2009 David Nolden
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "ducontext.h"
#include
#include
#include
#include
#include
#include "ducontextdata.h"
#include "declaration.h"
#include "duchain.h"
#include "duchainlock.h"
#include "use.h"
#include "identifier.h"
#include "topducontext.h"
#include "persistentsymboltable.h"
#include "aliasdeclaration.h"
#include "namespacealiasdeclaration.h"
#include "abstractfunctiondeclaration.h"
#include "duchainregister.h"
#include "topducontextdynamicdata.h"
#include "importers.h"
#include "uses.h"
#include "navigation/abstractdeclarationnavigationcontext.h"
#include "navigation/abstractnavigationwidget.h"
#include "ducontextdynamicdata.h"
#include "util/debug.h"
// maximum depth for DUContext::findDeclarationsInternal searches
const uint maxParentDepth = 20;
using namespace KTextEditor;
#ifndef NDEBUG
#define ENSURE_CAN_WRITE_(x) {if(x->inDUChain()) { ENSURE_CHAIN_WRITE_LOCKED }}
#define ENSURE_CAN_READ_(x) {if(x->inDUChain()) { ENSURE_CHAIN_READ_LOCKED }}
#else
#define ENSURE_CAN_WRITE_(x)
#define ENSURE_CAN_READ_(x)
#endif
QDebug operator<<(QDebug dbg, const KDevelop::DUContext::Import& import)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "Import(" << import.indexedContext().data() << ')';
return dbg;
}
namespace KDevelop
{
DEFINE_LIST_MEMBER_HASH(DUContextData, m_childContexts, LocalIndexedDUContext)
DEFINE_LIST_MEMBER_HASH(DUContextData, m_importers, IndexedDUContext)
DEFINE_LIST_MEMBER_HASH(DUContextData, m_importedContexts, DUContext::Import)
DEFINE_LIST_MEMBER_HASH(DUContextData, m_localDeclarations, LocalIndexedDeclaration)
DEFINE_LIST_MEMBER_HASH(DUContextData, m_uses, Use)
REGISTER_DUCHAIN_ITEM(DUContext);
DUChainVisitor::~DUChainVisitor()
{
}
/**
* We leak here, to prevent a possible crash during destruction, as the destructor
* of Identifier is not safe to be called after the duchain has been destroyed
*/
const Identifier& globalImportIdentifier() {
static const Identifier globalImportIdentifierObject(QStringLiteral("{...import...}"));
return globalImportIdentifierObject;
}
const Identifier& globalAliasIdentifier() {
static const Identifier globalAliasIdentifierObject(QStringLiteral("{...alias...}"));
return globalAliasIdentifierObject;
}
const IndexedIdentifier& globalIndexedImportIdentifier()
{
static const IndexedIdentifier id(globalImportIdentifier());
return id;
}
const IndexedIdentifier& globalIndexedAliasIdentifier()
{
static const IndexedIdentifier id(globalAliasIdentifier());
return id;
}
void DUContext::rebuildDynamicData(DUContext* parent, uint ownIndex) {
Q_ASSERT(!parent || ownIndex);
m_dynamicData->m_topContext = parent ? parent->topContext() : static_cast(this);
m_dynamicData->m_indexInTopContext = ownIndex;
m_dynamicData->m_parentContext = DUContextPointer(parent);
m_dynamicData->m_context = this;
m_dynamicData->m_childContexts.clear();
m_dynamicData->m_childContexts.reserve(d_func()->m_childContextsSize());
FOREACH_FUNCTION(const LocalIndexedDUContext& ctx, d_func()->m_childContexts) {
m_dynamicData->m_childContexts << ctx.data(m_dynamicData->m_topContext);
}
m_dynamicData->m_localDeclarations.clear();
m_dynamicData->m_localDeclarations.reserve(d_func()->m_localDeclarationsSize());
FOREACH_FUNCTION(const LocalIndexedDeclaration& idx, d_func()->m_localDeclarations) {
auto declaration = idx.data(m_dynamicData->m_topContext);
if (!declaration) {
qCWarning(LANGUAGE) << "child declaration number" << idx.localIndex() << "of" << d_func_dynamic()->m_localDeclarationsSize() << "is invalid";
continue;
}
m_dynamicData->m_localDeclarations << declaration;
}
DUChainBase::rebuildDynamicData(parent, ownIndex);
}
DUContextData::DUContextData()
: m_inSymbolTable(false)
, m_anonymousInParent(false)
, m_propagateDeclarations(false)
{
initializeAppendedLists();
}
DUContextData::~DUContextData() {
freeAppendedLists();
}
DUContextData::DUContextData(const DUContextData& rhs)
: DUChainBaseData(rhs)
, m_inSymbolTable(rhs.m_inSymbolTable)
, m_anonymousInParent(rhs.m_anonymousInParent)
, m_propagateDeclarations(rhs.m_propagateDeclarations)
{
initializeAppendedLists();
copyListsFrom(rhs);
m_scopeIdentifier = rhs.m_scopeIdentifier;
m_contextType = rhs.m_contextType;
m_owner = rhs.m_owner;
}
DUContextDynamicData::DUContextDynamicData(DUContext* d)
: m_topContext(0)
, m_indexInTopContext(0)
, m_context(d)
{
}
void DUContextDynamicData::scopeIdentifier(bool includeClasses, QualifiedIdentifier& target) const {
if (m_parentContext)
m_parentContext->m_dynamicData->scopeIdentifier(includeClasses, target);
if (includeClasses || d_func()->m_contextType != DUContext::Class)
target += d_func()->m_scopeIdentifier;
}
bool DUContextDynamicData::imports(const DUContext* context, const TopDUContext* source,
QSet* recursionGuard) const
{
if( this == context->m_dynamicData )
return true;
if (recursionGuard->contains(this)) {
return false;
}
recursionGuard->insert(this);
FOREACH_FUNCTION( const DUContext::Import& ctx, d_func()->m_importedContexts ) {
DUContext* import = ctx.context(source);
if(import == context || (import && import->m_dynamicData->imports(context, source, recursionGuard)))
return true;
}
return false;
}
inline bool isContextTemporary(uint index) {
return index > (0xffffffff/2);
}
void DUContextDynamicData::addDeclaration( Declaration * newDeclaration )
{
// The definition may not have its identifier set when it's assigned...
// allow dupes here, TODO catch the error elsewhere
//If this context is temporary, added declarations should be as well, and viceversa
Q_ASSERT(isContextTemporary(m_indexInTopContext) == isContextTemporary(newDeclaration->ownIndex()));
CursorInRevision start = newDeclaration->range().start;
bool inserted = false;
///@todo Do binary search to find the position
for (int i = m_localDeclarations.size() - 1; i >= 0; --i) {
Declaration* child = m_localDeclarations[i];
Q_ASSERT(d_func()->m_localDeclarations()[i].data(m_topContext) == child);
if(child == newDeclaration)
return;
//TODO: All declarations in a macro will have the same empty range, and just get appended
//that may not be Good Enough in complex cases.
if (start >= child->range().start) {
m_localDeclarations.insert(i + 1, newDeclaration);
d_func_dynamic()->m_localDeclarationsList().insert(i+1, newDeclaration);
Q_ASSERT(d_func()->m_localDeclarations()[i+1].data(m_topContext) == newDeclaration);
inserted = true;
break;
}
}
if (!inserted) {
// We haven't found any child that is before this one, so prepend it
m_localDeclarations.insert(0, newDeclaration);
d_func_dynamic()->m_localDeclarationsList().insert(0, newDeclaration);
Q_ASSERT(d_func()->m_localDeclarations()[0].data(m_topContext) == newDeclaration);
}
}
bool DUContextDynamicData::removeDeclaration(Declaration* declaration)
{
const int idx = m_localDeclarations.indexOf(declaration);
if (idx != -1) {
Q_ASSERT(d_func()->m_localDeclarations()[idx].data(m_topContext) == declaration);
m_localDeclarations.remove(idx);
d_func_dynamic()->m_localDeclarationsList().remove(idx);
return true;
} else {
Q_ASSERT(d_func_dynamic()->m_localDeclarationsList().indexOf(LocalIndexedDeclaration(declaration)) == -1);
return false;
}
}
void DUContextDynamicData::addChildContext( DUContext * context )
{
// Internal, don't need to assert a lock
Q_ASSERT(!context->m_dynamicData->m_parentContext
|| context->m_dynamicData->m_parentContext.data()->m_dynamicData == this );
LocalIndexedDUContext indexed(context->m_dynamicData->m_indexInTopContext);
//If this context is temporary, added declarations should be as well, and viceversa
Q_ASSERT(isContextTemporary(m_indexInTopContext) == isContextTemporary(indexed.localIndex()));
bool inserted = false;
int childCount = m_childContexts.size();
for (int i = childCount-1; i >= 0; --i) {///@todo Do binary search to find the position
DUContext* child = m_childContexts[i];
Q_ASSERT(d_func_dynamic()->m_childContexts()[i] == LocalIndexedDUContext(child));
if (context == child)
return;
if (context->range().start >= child->range().start) {
m_childContexts.insert(i+1, context);
d_func_dynamic()->m_childContextsList().insert(i+1, indexed);
context->m_dynamicData->m_parentContext = m_context;
inserted = true;
break;
}
}
if( !inserted ) {
m_childContexts.insert(0, context);
d_func_dynamic()->m_childContextsList().insert(0, indexed);
context->m_dynamicData->m_parentContext = m_context;
}
}
bool DUContextDynamicData::removeChildContext( DUContext* context ) {
// ENSURE_CAN_WRITE
const int idx = m_childContexts.indexOf(context);
if (idx != -1) {
m_childContexts.remove(idx);
Q_ASSERT(d_func()->m_childContexts()[idx] == LocalIndexedDUContext(context));
d_func_dynamic()->m_childContextsList().remove(idx);
return true;
} else {
Q_ASSERT(d_func_dynamic()->m_childContextsList().indexOf(LocalIndexedDUContext(context)) == -1);
return false;
}
}
void DUContextDynamicData::addImportedChildContext( DUContext * context )
{
// ENSURE_CAN_WRITE
DUContext::Import import(m_context, context);
if(import.isDirect()) {
//Direct importers are registered directly within the data
if(d_func_dynamic()->m_importersList().contains(IndexedDUContext(context))) {
qCDebug(LANGUAGE) << m_context->scopeIdentifier(true).toString() << "importer added multiple times:" << context->scopeIdentifier(true).toString();
return;
}
d_func_dynamic()->m_importersList().append(context);
}else{
//Indirect importers are registered separately
Importers::self().addImporter(import.indirectDeclarationId(), IndexedDUContext(context));
}
}
//Can also be called with a context that is not in the list
void DUContextDynamicData::removeImportedChildContext( DUContext * context )
{
// ENSURE_CAN_WRITE
DUContext::Import import(m_context, context);
if(import.isDirect()) {
d_func_dynamic()->m_importersList().removeOne(IndexedDUContext(context));
}else{
//Indirect importers are registered separately
Importers::self().removeImporter(import.indirectDeclarationId(), IndexedDUContext(context));
}
}
int DUContext::depth() const
{
{ if (!parentContext()) return 0; return parentContext()->depth() + 1; }
}
DUContext::DUContext(DUContextData& data)
: DUChainBase(data)
, m_dynamicData(new DUContextDynamicData(this))
{
}
DUContext::DUContext(const RangeInRevision& range, DUContext* parent, bool anonymous)
: DUChainBase(*new DUContextData(), range)
, m_dynamicData(new DUContextDynamicData(this))
{
d_func_dynamic()->setClassId(this);
if(parent)
m_dynamicData->m_topContext = parent->topContext();
else
m_dynamicData->m_topContext = static_cast(this);
d_func_dynamic()->setClassId(this);
DUCHAIN_D_DYNAMIC(DUContext);
d->m_contextType = Other;
m_dynamicData->m_parentContext = 0;
d->m_anonymousInParent = anonymous;
d->m_inSymbolTable = false;
if (parent) {
m_dynamicData->m_indexInTopContext = parent->topContext()->m_dynamicData->allocateContextIndex(this, parent->isAnonymous() || anonymous);
Q_ASSERT(m_dynamicData->m_indexInTopContext);
if( !anonymous )
parent->m_dynamicData->addChildContext(this);
else
m_dynamicData->m_parentContext = parent;
}
if(parent && !anonymous && parent->inSymbolTable())
setInSymbolTable(true);
}
bool DUContext::isAnonymous() const {
return d_func()->m_anonymousInParent || (m_dynamicData->m_parentContext && m_dynamicData->m_parentContext->isAnonymous());
}
DUContext::DUContext( DUContextData& dd, const RangeInRevision& range, DUContext * parent, bool anonymous )
: DUChainBase(dd, range)
, m_dynamicData(new DUContextDynamicData(this))
{
if(parent)
m_dynamicData->m_topContext = parent->topContext();
else
m_dynamicData->m_topContext = static_cast(this);
DUCHAIN_D_DYNAMIC(DUContext);
d->m_contextType = Other;
m_dynamicData->m_parentContext = 0;
d->m_inSymbolTable = false;
d->m_anonymousInParent = anonymous;
if (parent) {
m_dynamicData->m_indexInTopContext = parent->topContext()->m_dynamicData->allocateContextIndex(this, parent->isAnonymous() || anonymous);
if( !anonymous )
parent->m_dynamicData->addChildContext(this);
else
m_dynamicData->m_parentContext = parent;
}
}
DUContext::DUContext(DUContext& useDataFrom)
: DUChainBase(useDataFrom)
, m_dynamicData(useDataFrom.m_dynamicData)
{
}
DUContext::~DUContext( )
{
TopDUContext* top = topContext();
if(!top->deleting() || !top->isOnDisk()) {
DUCHAIN_D_DYNAMIC(DUContext);
if(d->m_owner.declaration())
d->m_owner.declaration()->setInternalContext(0);
while( d->m_importersSize() != 0 ) {
if(d->m_importers()[0].data())
d->m_importers()[0].data()->removeImportedParentContext(this);
else {
qCDebug(LANGUAGE) << "importer disappeared";
d->m_importersList().removeOne(d->m_importers()[0]);
}
}
clearImportedParentContexts();
}
deleteChildContextsRecursively();
if(!topContext()->deleting() || !topContext()->isOnDisk())
deleteUses();
deleteLocalDeclarations();
//If the top-context is being delete, we don't need to spend time rebuilding the inner structure.
//That's expensive, especially when the data is not dynamic.
if(!top->deleting() || !top->isOnDisk()) {
if (m_dynamicData->m_parentContext)
m_dynamicData->m_parentContext->m_dynamicData->removeChildContext(this);
}
top->m_dynamicData->clearContextIndex(this);
Q_ASSERT(d_func()->isDynamic() == (!top->deleting() || !top->isOnDisk() || top->m_dynamicData->isTemporaryContextIndex(m_dynamicData->m_indexInTopContext)));
delete m_dynamicData;
}
QVector< DUContext * > DUContext::childContexts( ) const
{
ENSURE_CAN_READ
return m_dynamicData->m_childContexts;
}
Declaration* DUContext::owner() const {
ENSURE_CAN_READ
return d_func()->m_owner.declaration();
}
void DUContext::setOwner(Declaration* owner) {
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
if( owner == d->m_owner.declaration() )
return;
Declaration* oldOwner = d->m_owner.declaration();
d->m_owner = owner;
//Q_ASSERT(!oldOwner || oldOwner->internalContext() == this);
if( oldOwner && oldOwner->internalContext() == this )
oldOwner->setInternalContext(0);
//The context set as internal context should always be the last opened context
if( owner )
owner->setInternalContext(this);
}
DUContext* DUContext::parentContext( ) const
{
//ENSURE_CAN_READ Commented out for performance reasons
return m_dynamicData->m_parentContext.data();
}
void DUContext::setPropagateDeclarations(bool propagate)
{
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
if(propagate == d->m_propagateDeclarations)
return;
d->m_propagateDeclarations = propagate;
}
bool DUContext::isPropagateDeclarations() const
{
return d_func()->m_propagateDeclarations;
}
QList DUContext::findLocalDeclarations( const IndexedIdentifier& identifier,
const CursorInRevision& position,
const TopDUContext* topContext,
const AbstractType::Ptr& dataType,
SearchFlags flags ) const
{
ENSURE_CAN_READ
DeclarationList ret;
findLocalDeclarationsInternal(identifier, position.isValid() ? position : range().end, dataType, ret, topContext ? topContext : this->topContext(), flags);
return ret;
}
QList DUContext::findLocalDeclarations( const Identifier& identifier,
const CursorInRevision& position,
const TopDUContext* topContext,
const AbstractType::Ptr& dataType,
SearchFlags flags ) const
{
return findLocalDeclarations(IndexedIdentifier(identifier), position, topContext, dataType, flags);
}
namespace {
bool contextIsChildOrEqual(const DUContext* childContext, const DUContext* context)
{
if(childContext == context)
return true;
if(childContext->parentContext())
return contextIsChildOrEqual(childContext->parentContext(), context);
else
return false;
}
struct Checker
{
Checker(DUContext::SearchFlags flags, const AbstractType::Ptr& dataType,
const CursorInRevision & position, DUContext::ContextType ownType)
: m_flags(flags)
, m_dataType(dataType)
, m_position(position)
, m_ownType(ownType)
{
}
Declaration* check(Declaration* declaration) const
{
///@todo This is C++-specific
if (m_ownType != DUContext::Class && m_ownType != DUContext::Template
&& m_position.isValid() && m_position <= declaration->range().start)
{
return nullptr;
}
if (declaration->kind() == Declaration::Alias && !(m_flags & DUContext::DontResolveAliases)) {
//Apply alias declarations
AliasDeclaration* alias = static_cast(declaration);
if (alias->aliasedDeclaration().isValid()) {
declaration = alias->aliasedDeclaration().declaration();
} else {
qCDebug(LANGUAGE) << "lost aliased declaration";
}
}
if (declaration->kind() == Declaration::NamespaceAlias && !(m_flags & DUContext::NoFiltering)) {
return nullptr;
}
if ((m_flags & DUContext::OnlyFunctions) && !declaration->isFunctionDeclaration()) {
return nullptr;
}
if (m_dataType && m_dataType->indexed() != declaration->indexedType()) {
return nullptr;
}
return declaration;
}
DUContext::SearchFlags m_flags;
const AbstractType::Ptr m_dataType;
const CursorInRevision m_position;
DUContext::ContextType m_ownType;
};
}
void DUContext::findLocalDeclarationsInternal(const Identifier& identifier, const CursorInRevision& position,
const AbstractType::Ptr& dataType, DeclarationList& ret,
const TopDUContext* source, SearchFlags flags) const
{
return findLocalDeclarationsInternal(IndexedIdentifier(identifier), position, dataType, ret, source, flags);
}
void DUContext::findLocalDeclarationsInternal( const IndexedIdentifier& identifier,
const CursorInRevision & position,
const AbstractType::Ptr& dataType,
DeclarationList& ret, const TopDUContext* /*source*/,
SearchFlags flags ) const
{
Checker checker(flags, dataType, position, type());
DUCHAIN_D(DUContext);
if (d->m_inSymbolTable && !d->m_scopeIdentifier.isEmpty() && !identifier.isEmpty()) {
//This context is in the symbol table, use the symbol-table to speed up the search
QualifiedIdentifier id(scopeIdentifier(true) + identifier);
TopDUContext* top = topContext();
uint count;
const IndexedDeclaration* declarations;
PersistentSymbolTable::self().declarations(id, count, declarations);
for (uint a = 0; a < count; ++a) {
///@todo Eventually do efficient iteration-free filtering
if (declarations[a].topContextIndex() == top->ownIndex()) {
Declaration* decl = declarations[a].declaration();
if (decl && contextIsChildOrEqual(decl->context(), this)) {
Declaration* checked = checker.check(decl);
if (checked) {
ret.append(checked);
}
}
}
}
} else {
//Iterate through all declarations
DUContextDynamicData::VisibleDeclarationIterator it(m_dynamicData);
while (it) {
Declaration* declaration = *it;
if (declaration && declaration->indexedIdentifier() == identifier) {
Declaration* checked = checker.check(declaration);
if (checked)
ret.append(checked);
}
++it;
}
}
}
bool DUContext::foundEnough( const DeclarationList& ret, SearchFlags flags ) const {
if( !ret.isEmpty() && !(flags & DUContext::NoFiltering))
return true;
else
return false;
}
bool DUContext::findDeclarationsInternal( const SearchItem::PtrList & baseIdentifiers,
const CursorInRevision & position,
const AbstractType::Ptr& dataType,
DeclarationList& ret, const TopDUContext* source,
SearchFlags flags, uint depth ) const
{
if (depth > maxParentDepth) {
qCDebug(LANGUAGE) << "maximum depth reached in" << scopeIdentifier(true);
return false;
}
DUCHAIN_D(DUContext);
if (d->m_contextType != Namespace) {
// If we're in a namespace, delay all the searching into the top-context, because only that has the overview to pick the correct declarations.
for (int a = 0; a < baseIdentifiers.size(); ++a) {
if (!baseIdentifiers[a]->isExplicitlyGlobal && baseIdentifiers[a]->next.isEmpty()) {
// It makes no sense searching locally for qualified identifiers
findLocalDeclarationsInternal(baseIdentifiers[a]->identifier, position, dataType, ret, source, flags);
}
}
if (foundEnough(ret, flags)) {
return true;
}
}
///Step 1: Apply namespace-aliases and -imports
SearchItem::PtrList aliasedIdentifiers;
//Because of namespace-imports and aliases, this identifier may need to be searched under multiple names
applyAliases(baseIdentifiers, aliasedIdentifiers, position, false, type() != DUContext::Namespace && type() != DUContext::Global);
if (d->m_importedContextsSize() != 0) {
///Step 2: Give identifiers that are not marked as explicitly-global to imported contexts(explicitly global ones are treatead in TopDUContext)
SearchItem::PtrList nonGlobalIdentifiers;
foreach (const SearchItem::Ptr& identifier, aliasedIdentifiers) {
if (!identifier->isExplicitlyGlobal) {
nonGlobalIdentifiers << identifier;
}
}
if (!nonGlobalIdentifiers.isEmpty()) {
const auto& url = this->url();
for(int import = d->m_importedContextsSize()-1; import >= 0; --import ) {
if (position.isValid() && d->m_importedContexts()[import].position.isValid() && position < d->m_importedContexts()[import].position) {
continue;
}
DUContext* context = d->m_importedContexts()[import].context(source);
if (!context) {
continue;
} else if (context == this) {
qCDebug(LANGUAGE) << "resolved self as import:" << scopeIdentifier(true);
continue;
}
if (!context->findDeclarationsInternal(nonGlobalIdentifiers, url == context->url() ? position : context->range().end,
dataType, ret, source, flags | InImportedParentContext, depth+1))
{
return false;
}
}
}
}
if (foundEnough(ret, flags)) {
return true;
}
///Step 3: Continue search in parent-context
if (!(flags & DontSearchInParent) && shouldSearchInParent(flags) && m_dynamicData->m_parentContext) {
applyUpwardsAliases(aliasedIdentifiers, source);
return m_dynamicData->m_parentContext->findDeclarationsInternal(aliasedIdentifiers,
url() == m_dynamicData->m_parentContext->url() ? position : m_dynamicData->m_parentContext->range().end,
dataType, ret, source, flags, depth);
}
return true;
}
QList< QualifiedIdentifier > DUContext::fullyApplyAliases(const QualifiedIdentifier& id,
const TopDUContext* source) const
{
ENSURE_CAN_READ
if(!source)
source = topContext();
SearchItem::PtrList identifiers;
identifiers << SearchItem::Ptr(new SearchItem(id));
const DUContext* current = this;
while(current) {
SearchItem::PtrList aliasedIdentifiers;
current->applyAliases(identifiers, aliasedIdentifiers, CursorInRevision::invalid(), true, false);
current->applyUpwardsAliases(identifiers, source);
current = current->parentContext();
}
QList ret;
foreach (const SearchItem::Ptr& item, identifiers)
ret += item->toList();
return ret;
}
QList DUContext::findDeclarations( const QualifiedIdentifier & identifier,
const CursorInRevision & position,
const AbstractType::Ptr& dataType,
const TopDUContext* topContext, SearchFlags flags) const
{
ENSURE_CAN_READ
DeclarationList ret;
SearchItem::PtrList identifiers;
// optimize: we don't want to allocate the top node always
// so create it on stack but ref it so its not deleted by the smart pointer
SearchItem item(identifier);
item.ref.ref();
identifiers << SearchItem::Ptr(&item);
findDeclarationsInternal(identifiers, position.isValid() ? position : range().end, dataType, ret, topContext ? topContext : this->topContext(), flags, 0);
return ret;
}
bool DUContext::imports(const DUContext* origin, const CursorInRevision& /*position*/ ) const
{
ENSURE_CAN_READ
QSet recursionGuard;
recursionGuard.reserve(8);
return m_dynamicData->imports(origin, topContext(), &recursionGuard);
}
bool DUContext::addIndirectImport(const DUContext::Import& import) {
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) {
if(d->m_importedContexts()[a] == import) {
d->m_importedContextsList()[a].position = import.position;
return true;
}
}
///Do not sort the imported contexts by their own line-number, it makes no sense.
///Contexts added first, aka template-contexts, should stay in first place, so they are searched first.
d->m_importedContextsList().append(import);
return false;
}
void DUContext::addImportedParentContext( DUContext * context, const CursorInRevision& position, bool anonymous, bool /*temporary*/ )
{
ENSURE_CAN_WRITE
if(context == this) {
qCDebug(LANGUAGE) << "Tried to import self";
return;
}
if(!context) {
qCDebug(LANGUAGE) << "Tried to import invalid context";
return;
}
Import import(context, this, position);
if(addIndirectImport(import))
return;
if( !anonymous ) {
ENSURE_CAN_WRITE_(context)
context->m_dynamicData->addImportedChildContext(this);
}
}
void DUContext::removeImportedParentContext( DUContext * context )
{
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
Import import(context, this, CursorInRevision::invalid());
for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) {
if(d->m_importedContexts()[a] == import) {
d->m_importedContextsList().remove(a);
break;
}
}
if( !context )
return;
context->m_dynamicData->removeImportedChildContext(this);
}
KDevVarLengthArray DUContext::indexedImporters() const
{
KDevVarLengthArray ret;
if(owner())
ret = Importers::self().importers(owner()->id()); //Add indirect importers to the list
FOREACH_FUNCTION(const IndexedDUContext& ctx, d_func()->m_importers)
ret.append(ctx);
return ret;
}
QVector DUContext::importers() const
{
ENSURE_CAN_READ
QVector ret;
FOREACH_FUNCTION(const IndexedDUContext& ctx, d_func()->m_importers)
ret << ctx.context();
if(owner()) {
//Add indirect importers to the list
KDevVarLengthArray indirect = Importers::self().importers(owner()->id());
foreach (const IndexedDUContext ctx, indirect) {
ret << ctx.context();
}
}
return ret;
}
DUContext * DUContext::findContext( const CursorInRevision& position, DUContext* parent) const
{
ENSURE_CAN_READ
if (!parent)
parent = const_cast(this);
foreach (DUContext* context, parent->m_dynamicData->m_childContexts) {
if (context->range().contains(position)) {
DUContext* ret = findContext(position, context);
if (!ret) {
ret = context;
}
return ret;
}
}
return 0;
}
bool DUContext::parentContextOf(DUContext* context) const
{
if (this == context)
return true;
foreach (DUContext* child, m_dynamicData->m_childContexts) {
if (child->parentContextOf(context)) {
return true;
}
}
return false;
}
QList< QPair > DUContext::allDeclarations(const CursorInRevision& position,
const TopDUContext* topContext,
bool searchInParents) const
{
ENSURE_CAN_READ
QList< QPair > ret;
QHash hadContexts;
// Iterate back up the chain
mergeDeclarationsInternal(ret, position, hadContexts, topContext ? topContext : this->topContext(), searchInParents);
return ret;
}
QVector DUContext::localDeclarations(const TopDUContext* source) const
{
ENSURE_CAN_READ
// TODO: remove this parameter once we kill old-cpp
Q_UNUSED(source);
return m_dynamicData->m_localDeclarations;
}
void DUContext::mergeDeclarationsInternal(QList< QPair >& definitions,
const CursorInRevision& position,
QHash& hadContexts,
const TopDUContext* source,
bool searchInParents, int currentDepth) const
{
ENSURE_CAN_READ
if((currentDepth > 300 && currentDepth < 1000) || currentDepth > 1300) {
qCDebug(LANGUAGE) << "too much depth";
return;
}
DUCHAIN_D(DUContext);
if(hadContexts.contains(this) && !searchInParents)
return;
if(!hadContexts.contains(this)) {
hadContexts[this] = true;
if( (type() == DUContext::Namespace || type() == DUContext::Global) && currentDepth < 1000 )
currentDepth += 1000;
{
DUContextDynamicData::VisibleDeclarationIterator it(m_dynamicData);
while(it) {
Declaration* decl = *it;
if ( decl && (!position.isValid() || decl->range().start <= position) )
definitions << qMakePair(decl, currentDepth);
++it;
}
}
for(int a = d->m_importedContextsSize()-1; a >= 0; --a) {
const Import* import(&d->m_importedContexts()[a]);
DUContext* context = import->context(source);
while( !context && a > 0 ) {
--a;
import = &d->m_importedContexts()[a];
context = import->context(source);
}
if( !context )
break;
if(context == this) {
qCDebug(LANGUAGE) << "resolved self as import:" << scopeIdentifier(true);
continue;
}
if( position.isValid() && import->position.isValid() && position < import->position )
continue;
context->mergeDeclarationsInternal(definitions, CursorInRevision::invalid(), hadContexts, source, searchInParents && context->shouldSearchInParent(InImportedParentContext) && context->parentContext()->type() == DUContext::Helper, currentDepth+1);
}
}
///Only respect the position if the parent-context is not a class(@todo this is language-dependent)
if (parentContext() && searchInParents )
parentContext()->mergeDeclarationsInternal(definitions, parentContext()->type() == DUContext::Class ? parentContext()->range().end : position, hadContexts, source, searchInParents, currentDepth+1);
}
void DUContext::deleteLocalDeclarations()
{
ENSURE_CAN_WRITE
// It may happen that the deletion of one declaration triggers the deletion of another one
// Therefore we copy the list of indexed declarations and work on those. Indexed declarations
// will return zero for already deleted declarations.
KDevVarLengthArray indexedLocal;
if (d_func()->m_localDeclarations()) {
indexedLocal.append(d_func()->m_localDeclarations(), d_func()->m_localDeclarationsSize());
}
foreach (const LocalIndexedDeclaration& indexed, m_dynamicData->m_localDeclarations) {
delete indexed.data(topContext());
}
m_dynamicData->m_localDeclarations.clear();
}
void DUContext::deleteChildContextsRecursively()
{
ENSURE_CAN_WRITE
// note: don't use qDeleteAll here because child ctx deletion changes m_dynamicData->m_childContexts
// also note: foreach iterates on a copy, so this is safe
foreach (DUContext* ctx, m_dynamicData->m_childContexts) {
delete ctx;
}
m_dynamicData->m_childContexts.clear();
}
QVector DUContext::clearLocalDeclarations( )
{
auto copy = m_dynamicData->m_localDeclarations;
foreach (Declaration* dec, copy) {
dec->setContext(0);
}
return copy;
}
QualifiedIdentifier DUContext::scopeIdentifier(bool includeClasses) const
{
ENSURE_CAN_READ
QualifiedIdentifier ret;
m_dynamicData->scopeIdentifier(includeClasses, ret);
return ret;
}
bool DUContext::equalScopeIdentifier(const DUContext* rhs) const
{
ENSURE_CAN_READ
const DUContext* left = this;
const DUContext* right = rhs;
while(left || right) {
if(!left || !right)
return false;
if(!(left->d_func()->m_scopeIdentifier == right->d_func()->m_scopeIdentifier))
return false;
left = left->parentContext();
right = right->parentContext();
}
return true;
}
void DUContext::setLocalScopeIdentifier(const QualifiedIdentifier & identifier)
{
ENSURE_CAN_WRITE
bool wasInSymbolTable = inSymbolTable();
setInSymbolTable(false);
d_func_dynamic()->m_scopeIdentifier = identifier;
setInSymbolTable(wasInSymbolTable);
}
QualifiedIdentifier DUContext::localScopeIdentifier() const
{
//ENSURE_CAN_READ Commented out for performance reasons
return d_func()->m_scopeIdentifier;
}
IndexedQualifiedIdentifier DUContext::indexedLocalScopeIdentifier() const {
return d_func()->m_scopeIdentifier;
}
DUContext::ContextType DUContext::type() const
{
//ENSURE_CAN_READ This is disabled, because type() is called very often while searching, and it costs us performance
return d_func()->m_contextType;
}
void DUContext::setType(ContextType type)
{
ENSURE_CAN_WRITE
d_func_dynamic()->m_contextType = type;
}
QList DUContext::findDeclarations(const Identifier& identifier, const CursorInRevision& position,
const TopDUContext* topContext, SearchFlags flags) const
{
return findDeclarations(IndexedIdentifier(identifier), position, topContext, flags);
}
QList DUContext::findDeclarations(const IndexedIdentifier& identifier, const CursorInRevision& position,
const TopDUContext* topContext, SearchFlags flags) const
{
ENSURE_CAN_READ
DeclarationList ret;
SearchItem::PtrList identifiers;
identifiers << SearchItem::Ptr(new SearchItem(false, identifier, SearchItem::PtrList()));
findDeclarationsInternal(identifiers, position.isValid() ? position : range().end, AbstractType::Ptr(), ret, topContext ? topContext : this->topContext(), flags, 0);
return ret;
}
void DUContext::deleteUse(int index)
{
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
d->m_usesList().remove(index);
}
void DUContext::deleteUses()
{
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
d->m_usesList().clear();
}
void DUContext::deleteUsesRecursively()
{
deleteUses();
foreach (DUContext* childContext, m_dynamicData->m_childContexts) {
childContext->deleteUsesRecursively();
}
}
bool DUContext::inDUChain() const {
if( d_func()->m_anonymousInParent || !m_dynamicData->m_parentContext)
return false;
TopDUContext* top = topContext();
return top && top->inDUChain();
}
DUContext* DUContext::specialize(const IndexedInstantiationInformation& /*specialization*/,
const TopDUContext* topContext, int /*upDistance*/)
{
if(!topContext)
return 0;
return this;
}
CursorInRevision DUContext::importPosition(const DUContext* target) const
{
ENSURE_CAN_READ
DUCHAIN_D(DUContext);
Import import(const_cast(target), this, CursorInRevision::invalid());
for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a)
if(d->m_importedContexts()[a] == import)
return d->m_importedContexts()[a].position;
return CursorInRevision::invalid();
}
QVector DUContext::importedParentContexts() const
{
ENSURE_CAN_READ
QVector ret;
ret.reserve(d_func()->m_importedContextsSize());
FOREACH_FUNCTION(const DUContext::Import& import, d_func()->m_importedContexts)
ret << import;
return ret;
}
void DUContext::applyAliases(const SearchItem::PtrList& baseIdentifiers, SearchItem::PtrList& identifiers,
const CursorInRevision& position, bool canBeNamespace, bool onlyImports) const {
DeclarationList imports;
findLocalDeclarationsInternal(globalIndexedImportIdentifier(), position, AbstractType::Ptr(), imports, topContext(), DUContext::NoFiltering);
if(imports.isEmpty() && onlyImports) {
identifiers = baseIdentifiers;
return;
}
for ( const SearchItem::Ptr& identifier : baseIdentifiers ) {
bool addUnmodified = true;
if( !identifier->isExplicitlyGlobal ) {
if( !imports.isEmpty() )
{
//We have namespace-imports.
foreach ( Declaration* importDecl, imports )
{
//Search for the identifier with the import-identifier prepended
if(dynamic_cast(importDecl))
{
NamespaceAliasDeclaration* alias = static_cast(importDecl);
identifiers.append( SearchItem::Ptr( new SearchItem( alias->importIdentifier(), identifier ) ) ) ;
}else{
qCDebug(LANGUAGE) << "Declaration with namespace alias identifier has the wrong type" << importDecl->url().str() << importDecl->range().castToSimpleRange();
}
}
}
if( !identifier->isEmpty() && (identifier->hasNext() || canBeNamespace) ) {
DeclarationList aliases;
findLocalDeclarationsInternal(identifier->identifier, position, AbstractType::Ptr(), imports, 0, DUContext::NoFiltering);
if(!aliases.isEmpty()) {
//The first part of the identifier has been found as a namespace-alias.
//In c++, we only need the first alias. However, just to be correct, follow them all for now.
foreach ( Declaration* aliasDecl, aliases )
{
if(!dynamic_cast(aliasDecl))
continue;
addUnmodified = false; //The un-modified identifier can be ignored, because it will be replaced with the resolved alias
NamespaceAliasDeclaration* alias = static_cast(aliasDecl);
//Create an identifier where namespace-alias part is replaced with the alias target
identifiers.append( SearchItem::Ptr( new SearchItem( alias->importIdentifier(), identifier->next ) ) ) ;
}
}
}
}
if( addUnmodified )
identifiers.append(identifier);
}
}
void DUContext::applyUpwardsAliases(SearchItem::PtrList& identifiers, const TopDUContext* /*source*/) const {
if(type() == Namespace) {
if(d_func()->m_scopeIdentifier.isEmpty())
return;
//Make sure we search for the items in all namespaces of the same name, by duplicating each one with the namespace-identifier prepended.
//We do this by prepending items to the current identifiers that equal the local scope identifier.
SearchItem::Ptr newItem( new SearchItem(d_func()->m_scopeIdentifier.identifier()) );
//This will exclude explictly global identifiers
newItem->addToEachNode( identifiers );
if(!newItem->next.isEmpty()) {
//Prepend the full scope before newItem
DUContext* parent = m_dynamicData->m_parentContext.data();
while(parent) {
newItem = SearchItem::Ptr( new SearchItem(parent->d_func()->m_scopeIdentifier, newItem) );
parent = parent->m_dynamicData->m_parentContext.data();
}
newItem->isExplicitlyGlobal = true;
identifiers.insert(0, newItem);
}
}
}
bool DUContext::shouldSearchInParent(SearchFlags flags) const
{
return (parentContext() && parentContext()->type() == DUContext::Helper && (flags & InImportedParentContext))
|| !(flags & InImportedParentContext);
}
const Use* DUContext::uses() const
{
ENSURE_CAN_READ
return d_func()->m_uses();
}
bool DUContext::declarationHasUses(Declaration* decl)
{
return DUChain::uses()->hasUses(decl->id());
}
int DUContext::usesCount() const
{
return d_func()->m_usesSize();
}
bool usesRangeLessThan(const Use& left, const Use& right)
{
return left.m_range.start < right.m_range.start;
}
int DUContext::createUse(int declarationIndex, const RangeInRevision& range, int insertBefore)
{
DUCHAIN_D_DYNAMIC(DUContext);
ENSURE_CAN_WRITE
Use use(range, declarationIndex);
if(insertBefore == -1) {
//Find position where to insert
const unsigned int size = d->m_usesSize();
const Use* uses = d->m_uses();
const Use* lowerBound = std::lower_bound(uses, uses + size, use, usesRangeLessThan);
insertBefore = lowerBound - uses;
// comment out to test this:
/*
unsigned int a = 0;
for(; a < size && range.start > uses[a].m_range.start; ++a) {
}
Q_ASSERT(a == insertBefore);
*/
}
d->m_usesList().insert(insertBefore, use);
return insertBefore;
}
void DUContext::changeUseRange(int useIndex, const RangeInRevision& range)
{
ENSURE_CAN_WRITE
d_func_dynamic()->m_usesList()[useIndex].m_range = range;
}
void DUContext::setUseDeclaration(int useNumber, int declarationIndex)
{
ENSURE_CAN_WRITE
d_func_dynamic()->m_usesList()[useNumber].m_declarationIndex = declarationIndex;
}
DUContext * DUContext::findContextAt(const CursorInRevision & position, bool includeRightBorder) const
{
ENSURE_CAN_READ
// qCDebug(LANGUAGE) << "searchign" << position << "in:" << scopeIdentifier(true).toString() << range() << includeRightBorder;
if (!range().contains(position) && (!includeRightBorder || range().end != position)) {
// qCDebug(LANGUAGE) << "mismatch";
return 0;
}
const auto childContexts = m_dynamicData->m_childContexts;
for(int a = childContexts.size() - 1; a >= 0; --a) {
if (DUContext* specific = childContexts[a]->findContextAt(position, includeRightBorder)) {
return specific;
}
}
return const_cast(this);
}
Declaration * DUContext::findDeclarationAt(const CursorInRevision & position) const
{
ENSURE_CAN_READ
if (!range().contains(position))
return 0;
foreach (Declaration* child, m_dynamicData->m_localDeclarations) {
if (child->range().contains(position)) {
return child;
}
}
return 0;
}
DUContext* DUContext::findContextIncluding(const RangeInRevision& range) const
{
ENSURE_CAN_READ
if (!this->range().contains(range))
return 0;
foreach (DUContext* child, m_dynamicData->m_childContexts) {
if (DUContext* specific = child->findContextIncluding(range)) {
return specific;
}
}
return const_cast(this);
}
int DUContext::findUseAt(const CursorInRevision & position) const
{
ENSURE_CAN_READ
if (!range().contains(position))
return -1;
for(unsigned int a = 0; a < d_func()->m_usesSize(); ++a)
if (d_func()->m_uses()[a].m_range.contains(position))
return a;
return -1;
}
bool DUContext::inSymbolTable() const
{
return d_func()->m_inSymbolTable;
}
void DUContext::setInSymbolTable(bool inSymbolTable)
{
d_func_dynamic()->m_inSymbolTable = inSymbolTable;
}
void DUContext::clearImportedParentContexts()
{
ENSURE_CAN_WRITE
DUCHAIN_D_DYNAMIC(DUContext);
while( d->m_importedContextsSize() != 0 ) {
DUContext* ctx = d->m_importedContexts()[0].context(0, false);
if(ctx)
ctx->m_dynamicData->removeImportedChildContext(this);
d->m_importedContextsList().removeOne(d->m_importedContexts()[0]);
}
}
void DUContext::cleanIfNotEncountered(const QSet& encountered)
{
ENSURE_CAN_WRITE
// It may happen that the deletion of one declaration triggers the deletion of another one
// Therefore we copy the list of indexed declarations and work on those. Indexed declarations
// will return zero for already deleted declarations.
KDevVarLengthArray indexedLocal;
if (d_func()->m_localDeclarations()) {
indexedLocal.append(d_func()->m_localDeclarations(), d_func()->m_localDeclarationsSize());
}
foreach (const LocalIndexedDeclaration& indexed, m_dynamicData->m_localDeclarations) {
auto dec = indexed.data(topContext());
if (dec && !encountered.contains(dec) && (!dec->isAutoDeclaration() || !dec->hasUses())) {
delete dec;
}
}
foreach (DUContext* childContext, m_dynamicData->m_childContexts) {
if (!encountered.contains(childContext)) {
delete childContext;
}
}
}
TopDUContext* DUContext::topContext() const
{
return m_dynamicData->m_topContext;
}
QWidget* DUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext,
- const QString& htmlPrefix, const QString& htmlSuffix) const
+ const QString& htmlPrefix, const QString& htmlSuffix,
+ AbstractNavigationWidget::DisplayHints hints) const
{
if (decl) {
AbstractNavigationWidget* widget = new AbstractNavigationWidget;
+ widget->setDisplayHints(hints);
AbstractDeclarationNavigationContext* context = new AbstractDeclarationNavigationContext(DeclarationPointer(decl),
- TopDUContextPointer(topContext));
+ TopDUContextPointer(topContext));
context->setPrefixSuffix(htmlPrefix, htmlSuffix);
widget->setContext(NavigationContextPointer(context));
return widget;
} else {
return 0;
}
}
QList allUses(DUContext* context, int declarationIndex, bool noEmptyUses)
{
QList ret;
for(int a = 0; a < context->usesCount(); ++a)
if(context->uses()[a].m_declarationIndex == declarationIndex)
if(!noEmptyUses || !context->uses()[a].m_range.isEmpty())
ret << context->uses()[a].m_range;
foreach(DUContext* child, context->childContexts())
ret += allUses(child, declarationIndex, noEmptyUses);
return ret;
}
DUContext::SearchItem::SearchItem(const QualifiedIdentifier& id, const Ptr& nextItem, int start)
: isExplicitlyGlobal(start == 0 ? id.explicitlyGlobal() : false)
{
if(!id.isEmpty()) {
if(id.count() > start)
identifier = id.indexedAt(start);
if(id.count() > start+1)
addNext(Ptr( new SearchItem(id, nextItem, start+1) ));
else if(nextItem)
next.append(nextItem);
}else if(nextItem) {
///If there is no prefix, just copy nextItem
isExplicitlyGlobal = nextItem->isExplicitlyGlobal;
identifier = nextItem->identifier;
next = nextItem->next;
}
}
DUContext::SearchItem::SearchItem(const QualifiedIdentifier& id, const PtrList& nextItems, int start)
: isExplicitlyGlobal(start == 0 ? id.explicitlyGlobal() : false)
{
if(id.count() > start)
identifier = id.indexedAt(start);
if(id.count() > start+1)
addNext(Ptr( new SearchItem(id, nextItems, start+1) ));
else
next = nextItems;
}
DUContext::SearchItem::SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const PtrList& nextItems)
: isExplicitlyGlobal(explicitlyGlobal)
, identifier(id)
, next(nextItems)
{
}
DUContext::SearchItem::SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const Ptr& nextItem)
: isExplicitlyGlobal(explicitlyGlobal)
, identifier(id)
{
next.append(nextItem);
}
bool DUContext::SearchItem::match(const QualifiedIdentifier& id, int offset) const {
if(id.isEmpty()) {
if(identifier.isEmpty() && next.isEmpty())
return true;
else
return false;
}
if(id.at(offset) != identifier) //The identifier is different
return false;
if(offset == id.count()-1) {
if(next.isEmpty())
return true; //match
else
return false; //id is too short
}
for(int a = 0; a < next.size(); ++a)
if(next[a]->match(id, offset+1))
return true;
return false;
}
bool DUContext::SearchItem::isEmpty() const {
return identifier.isEmpty();
}
bool DUContext::SearchItem::hasNext() const {
return !next.isEmpty();
}
QList DUContext::SearchItem::toList(const QualifiedIdentifier& prefix) const {
QList ret;
QualifiedIdentifier id = prefix;
if(id.isEmpty())
id.setExplicitlyGlobal(isExplicitlyGlobal);
if(!identifier.isEmpty())
id.push(identifier);
if(next.isEmpty()) {
ret << id;
} else {
for(int a = 0; a < next.size(); ++a)
ret += next[a]->toList(id);
}
return ret;
}
void DUContext::SearchItem::addNext(const SearchItem::Ptr& other) {
next.append(other);
}
void DUContext::SearchItem::addToEachNode(const SearchItem::Ptr& other) {
if(other->isExplicitlyGlobal)
return;
next.append(other);
for(int a = 0; a < next.size()-1; ++a)
next[a]->addToEachNode(other);
}
void DUContext::SearchItem::addToEachNode(const SearchItem::PtrList& other) {
int added = 0;
for (const SearchItem::Ptr& o : other) {
if(!o->isExplicitlyGlobal) {
next.append(o);
++added;
}
}
for(int a = 0; a < next.size()-added; ++a)
next[a]->addToEachNode(other);
}
DUContext::Import::Import(DUContext* _context, const DUContext* importer, const CursorInRevision& _position)
: position(_position)
{
if(_context && _context->owner() && (_context->owner()->specialization().index() || (importer && importer->topContext() != _context->topContext()))) {
m_declaration = _context->owner()->id();
}else{
m_context = _context;
}
}
DUContext::Import::Import(const DeclarationId& id, const CursorInRevision& _position)
: position(_position)
{
m_declaration = id;
}
DUContext* DUContext::Import::context(const TopDUContext* topContext, bool instantiateIfRequired) const {
if(m_declaration.isValid()) {
Declaration* decl = m_declaration.getDeclaration(topContext, instantiateIfRequired);
//This first case rests on the assumption that no context will ever import a function's expression context
//More accurately, that no specialized or cross-topContext imports will, but if the former assumption fails the latter will too
if (AbstractFunctionDeclaration *functionDecl = dynamic_cast(decl))
{
if (functionDecl->internalFunctionContext()) {
return functionDecl->internalFunctionContext();
} else {
qCWarning(LANGUAGE) << "Import of function declaration without internal function context encountered!";
}
}
if(decl)
return decl->logicalInternalContext(topContext);
else
return 0;
}else{
return m_context.data();
}
}
bool DUContext::Import::isDirect() const {
return m_context.isValid();
}
void DUContext::visit(DUChainVisitor& visitor)
{
ENSURE_CAN_READ
visitor.visit(this);
foreach (Declaration* decl, m_dynamicData->m_localDeclarations) {
visitor.visit(decl);
}
foreach (DUContext* childContext, m_dynamicData->m_childContexts) {
childContext->visit(visitor);
}
}
static bool sortByRange(const DUChainBase* lhs, const DUChainBase* rhs)
{
return lhs->range() < rhs->range();
}
void DUContext::resortLocalDeclarations()
{
ENSURE_CAN_WRITE
std::sort(m_dynamicData->m_localDeclarations.begin(), m_dynamicData->m_localDeclarations.end(), sortByRange);
auto top = topContext();
auto& declarations = d_func_dynamic()->m_localDeclarationsList();
std::sort(declarations.begin(), declarations.end(),
[top] (const LocalIndexedDeclaration& lhs, const LocalIndexedDeclaration& rhs) {
return lhs.data(top)->range() < rhs.data(top)->range();
});
}
void DUContext::resortChildContexts()
{
ENSURE_CAN_WRITE
std::sort(m_dynamicData->m_childContexts.begin(), m_dynamicData->m_childContexts.end(), sortByRange);
auto top = topContext();
auto& contexts = d_func_dynamic()->m_childContextsList();
std::sort(contexts.begin(), contexts.end(),
[top] (const LocalIndexedDUContext& lhs, const LocalIndexedDUContext& rhs) {
return lhs.data(top)->range() < rhs.data(top)->range();
});
}
}
diff --git a/language/duchain/ducontext.h b/language/duchain/ducontext.h
index 3930bac2f3..2475132b5f 100644
--- a/language/duchain/ducontext.h
+++ b/language/duchain/ducontext.h
@@ -1,951 +1,954 @@
/* This file is part of KDevelop
Copyright 2006 Hamish Rodda
Copyright 2007-2009 David Nolden
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEVPLATFORM_DUCONTEXT_H
#define KDEVPLATFORM_DUCONTEXT_H
#include
#include
#include
#include
#include
#include "identifier.h"
#include "duchainbase.h"
#include "types/abstracttype.h"
#include "duchainpointer.h"
#include "declarationid.h"
#include "indexedducontext.h"
+#include "navigation/abstractnavigationwidget.h"
+
class QWidget;
namespace KDevelop
{
class Declaration;
class DUChain;
class Use;
class TopDUContext;
class DUContext;
class DUContextData;
class KDEVPLATFORMLANGUAGE_EXPORT DUChainVisitor {
public:
virtual void visit(DUContext* context) = 0;
virtual void visit(Declaration* declaration) = 0;
virtual ~DUChainVisitor();
};
typedef DUChainPointer DUContextPointer;
/**
* A single context in source code, represented as a node in a
* directed acyclic graph.
*
* Access to context objects must be serialised by holding the
* chain lock, ie. DUChain::lock().
*
* NOTE: A du-context can be freely edited as long as it's parent-context is zero.
* In the moment the parent-context is set, the context may only be edited when it
* is allowed to edited it's top-level context(@see TopLevelContext::inDUChain()
*
* @todo change child relationships to a linked list within the context?
*/
class KDEVPLATFORMLANGUAGE_EXPORT DUContext : public DUChainBase
{
friend class Use;
friend class Declaration;
friend class DeclarationData;
friend class DUContextData;
friend class DUContextDynamicData;
friend class Definition;
friend class VisibleDeclarationIterator;
public:
/**
* Constructor. No convenience methods, as the initialisation order is important,
*
* @param anonymous Whether the context should be added as an anonymous context to the parent. That way the context can never be found through any of the parent's member-functions.
*
* If the parent is in the symbol table and the context is not anonymous, it will also be added to the symbol table. You nead a write-lock to the DUChain then
*/
explicit DUContext(const RangeInRevision& range, DUContext* parent = nullptr, bool anonymous = false);
explicit DUContext(DUContextData&);
/**
* Destructor. Will delete all child contexts which are defined within
* the same file as this context.
*/
~DUContext() override;
enum ContextType : quint8 {
Global /**< A context that declares functions, namespaces or classes */,
Namespace /**< A context that declares namespace members */,
Class /**< A context that declares class members */,
Function /**< A context that declares function-arguments */,
Template /**< A context that declares template-parameters */,
Enum /**< A context that contains a list of enumerators */,
Helper /**< A helper context. This context is treated specially during search:
* when searching within the imports of a context, and that context's parent
* is a context of type DUContext::Helper, then the upwards search is continued
* into that helper(The decision happens in shouldSearchInParent) */,
Other /**< Represents executable code, like for example within a compound-statement */
};
enum SearchFlag {
NoSearchFlags = 0 /**< Searching for everything */,
InImportedParentContext = 1 /**< Internal, do not use from outside */,
OnlyContainerTypes = 2 /**< Not implemented yet */,
DontSearchInParent = 4 /**< IF this flag is set, findDeclarations(..) will not search for the identifier in parent-contexts(which does not include imported parent-contexts) */,
NoUndefinedTemplateParams = 8 /**< For languages that support templates(like C++). If this is set, the search should fail as soon as undefined template-parameters are involved. */,
DirectQualifiedLookup = 16 /**< When this flag is used, the searched qualified identifier should NOT be split up into it's components and looked up one by one. Currently only plays a role in C++ specific parts. */,
NoFiltering = 32 /**< Should be set when no filtering at all is wished, not even filtering that is natural for the underlying language(For example in C++, constructors are filtered out be default) */,
OnlyFunctions = 64 /**< When this is given, only function-declarations are returned. In case of C++, this also means that constructors can be retrieved, while normally they are filtered out. */,
NoImportsCheck = 128 /**< With this parameter, a global search will return all matching items, from all contexts, not only from imported ones. */,
NoSelfLookUp = 256 /**< With this parameter, the special-treatment during search that allows finding the context-class by its name is disabled. */,
DontResolveAliases = 512 /**< Disables the resolution of alias declarations in the returned list*/,
LastSearchFlag = 1024
};
Q_DECLARE_FLAGS(SearchFlags, SearchFlag)
ContextType type() const;
void setType(ContextType type);
/**
* If this context was opened by a declaration or definition, this returns that item.
*
* The returned declaration/definition will have this context set as @c internalContext()
*/
Declaration* owner() const;
/**
* Sets the declaration/definition, and also updates it's internal context (they are strictly paired together).
*
* The declaration has to be part of the same top-context.
*/
void setOwner(Declaration* decl);
/**
* Calculate the depth of this context, from the top level context in the file.
*/
int depth() const;
/**
* Find the top context.
*/
TopDUContext* topContext() const override;
/**
* Visits all duchain objects in the whole duchain.
*
* Classes that hold a unique link to duchain objects like instantiations
* have to pass the visitor over to those classes.
* */
virtual void visit(DUChainVisitor& visitor);
/**
* Find the context which most specifically covers @a position.
*
* The search is recursive, so the most specific context is found.
*
* @param includeRightBorder When this is true, contexts will also be found that
* have the position on their right border.
*
* @warning This uses the ranges in the local revision of the document (at last parsing time).
* Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first.
*/
DUContext* findContextAt(const CursorInRevision& position, bool includeBorders = false) const;
/**
* Find a child declaration that has a rang that covers the given @a position.
*
* The search is local, not recursive.
*
* @warning This uses the ranges in the local revision of the document (at last parsing time).
* Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first.
*/
Declaration* findDeclarationAt(const CursorInRevision& position) const;
/**
* Find the context which most specifically covers @a range.
*
* @warning This uses the ranges in the local revision of the document (at last parsing time).
* Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first.
*/
DUContext* findContextIncluding(const RangeInRevision& range) const;
/**
* Calculate the fully qualified scope identifier.
*/
QualifiedIdentifier scopeIdentifier(bool includeClasses = false) const;
/**
* Returns true if this context has the same scope identifier as the given one.
*
* @note This is much more efficient than computing the identifiers through @c scopeIdentifier(..)
* and comparing them
*/
bool equalScopeIdentifier(const DUContext* rhs) const;
/**
* Scope identifier, used to qualify the identifiers occurring in each context.
*
* This is the part relative to the parent context.
*/
QualifiedIdentifier localScopeIdentifier() const;
/**
* Same as @c localScopeIdentifier(), but faster.
*/
IndexedQualifiedIdentifier indexedLocalScopeIdentifier() const;
/**
* Scope identifier, used to qualify the identifiers occurring in each context
* This must not be called once this context has children.
*/
void setLocalScopeIdentifier(const QualifiedIdentifier& identifier);
/**
* Returns whether this context is listed in the symbol table (Namespaces and classes)
*/
bool inSymbolTable() const;
/**
* Move this object into/out of the symbol table.
*
* @note You need to have a duchain write lock, unless this is a TopDUContext.
*/
void setInSymbolTable(bool inSymbolTable);
/**
* Returns the immediate parent context of this context.
*/
DUContext* parentContext() const;
/**
* Represents an imported parent context.
*/
struct KDEVPLATFORMLANGUAGE_EXPORT Import {
/**
* @note DUChain must be read-locked when this is called
*/
Import(DUContext* context, const DUContext* importer,
const CursorInRevision& position = CursorInRevision::invalid());
Import() : position(CursorInRevision::invalid()) { }
Import(const DeclarationId& id, const CursorInRevision& position = CursorInRevision::invalid());
bool operator==(const Import& rhs) const {
return m_context == rhs.m_context && m_declaration == rhs.m_declaration;
}
/**
* @param topContext The top-context from where to start searching.
* This is important to find the correct imports
* in the case of templates or similar structures.
*/
DUContext* context(const TopDUContext* topContext, bool instantiateIfRequired = true) const;
/**
* Returns the top-context index, if this import is not a specialization import.
*/
uint topContextIndex() const {
return m_context.topContextIndex();
}
IndexedDUContext indexedContext() const {
return m_context;
}
/**
* Returns true if this import is direct.
*
* That is, the import is not referred to by its identifier,
* but rather directly by its index.
*/
bool isDirect() const;
/**
* If this import is indirect, returns the imported declaration-id
*/
DeclarationId indirectDeclarationId() const {
return m_declaration;
}
CursorInRevision position;
private:
//Either we store m_declaration, or m_context. That way we can resolve specialized contexts.
///@todo Compress using union
DeclarationId m_declaration;
IndexedDUContext m_context;
};
/**
* Returns the list of imported parent contexts for this context.
*
* @warning The list may contain objects that are not valid any more,
* i.e. data() returns zero, @see addImportedParentContext)
* @warning The import structure may contain loops if this is a TopDUContext,
* so be careful when traversing the tree.
* @note This is expensive.
*/
virtual QVector importedParentContexts() const;
/**
* If the given context is directly imported into this one, and
* @c addImportedParentContext(..) was called with a valid cursor,
* this will return that position. Otherwise an invalid cursor is returned.
*/
virtual CursorInRevision importPosition(const DUContext* target) const;
/**
* Returns true if this context imports @param origin at any depth, else false.
*/
virtual bool imports(const DUContext* origin,
const CursorInRevision& position = CursorInRevision::invalid()) const;
/**
* Adds an imported context.
*
* @param anonymous If this is true, the import will not be registered at the imported context.
* This allows du-chain contexts importing without having a write-lock.
* @param position Position where the context is imported. This is mainly important in C++ with included files.
*
* If the context is already imported, only the position is updated.
*
* @note Be sure to have set the text location first, so that the chain is sorted correctly.
*/
virtual void addImportedParentContext(DUContext* context,
const CursorInRevision& position = CursorInRevision::invalid(),
bool anonymous = false, bool temporary = false);
/**
* Adds an imported context, which may be indirect.
*
* @warning This is only allowed if this context is _NOT_ a top-context.
* @warning When using this mechanism, this context will not be registered as importer to the other one.
* @warning The given import _must_ be indirect.
*
* @return true if the import was already imported before, else false.
*/
bool addIndirectImport(const DUContext::Import& import);
/**
* Removes a child context.
*/
virtual void removeImportedParentContext(DUContext* context);
/**
* Clear all imported parent contexts.
*/
virtual void clearImportedParentContexts();
/**
* If this is set to true, all declarations that are added to this context
* will also be visible in the parent-context.
*
* They will be visible in the parent using @c findDeclarations(...) and
* @c findLocalDeclarations(...), but will not be in the list of @c localDeclarations(...).
*/
void setPropagateDeclarations(bool propagate);
bool isPropagateDeclarations() const;
/**
* Returns the list of contexts importing this context.
*
* @note Very expensive, since the importers top-contexts need to be loaded.
*/
virtual QVector importers() const;
/**
* Returns the list of indexed importers.
*
* Cheap, because nothing needs to be loaded.
*/
KDevVarLengthArray indexedImporters() const;
/**
* Returns the list of immediate child contexts for this context.
*
* @note This is expensive.
*/
QVector childContexts() const;
/**
* Clears and deletes all child contexts recursively.
*
* This will not cross file boundaries.
*/
void deleteChildContextsRecursively();
/**
* Resort the child contexts by their range.
*
* You must call this when you manually change the range of child contexts in a way
* that could break the internal range sorting.
*/
void resortChildContexts();
/**
* Returns true if this declaration is accessible through the du-chain,
* and thus cannot be edited without a du-chain write lock
*/
virtual bool inDUChain() const;
/**
* Retrieve the context which is specialized with the given
* @a specialization as seen from the given @a topContext.
*
* @param specialization the specialization index (see DeclarationId)
* @param topContext the top context representing the perspective from which to specialize.
* if @p topContext is zero, only already existing specializations are returned,
* and if none exists, zero is returned.
* @param upDistance upwards distance in the context-structure of the
* given specialization-info. This allows specializing children.
*/
virtual DUContext* specialize(const IndexedInstantiationInformation& specialization,
const TopDUContext* topContext, int upDistance = 0);
/**
* Searches for and returns a declaration with a given @a identifier in this context, which
* is currently active at the given text @a position, with the given type @a dataType.
* In fact, only items are returned that are declared BEFORE that position.
*
* @param identifier the identifier of the definition to search for
* @param location the text position to search for
* @param topContext the top-context from where a completion is triggered.
* This is needed so delayed types (templates in C++) can be resolved in the correct context.
* @param type the type to match, or null for no type matching.
*
* @returns the requested declaration if one was found, otherwise null.
*
* @warning this may return declarations which are not in this tree, you may need to lock them too...
*/
QList findDeclarations(const QualifiedIdentifier& identifier,
const CursorInRevision& position = CursorInRevision::invalid(),
const AbstractType::Ptr& dataType = AbstractType::Ptr(),
const TopDUContext* topContext = nullptr,
SearchFlags flags = NoSearchFlags) const;
/**
* Searches for and returns a declaration with a given @a identifier in this context, which
* is currently active at the given text @a position.
*
* @param identifier the identifier of the definition to search for
* @param topContext the top-context from where a completion is triggered.
* This is needed so delayed types(templates in C++) can be resolved in the correct context.
* @param location the text position to search for
*
* @returns the requested declaration if one was found, otherwise null.
*
* @warning this may return declarations which are not in this tree, you may need to lock them too...
*
* @overload
*/
QList findDeclarations(const IndexedIdentifier& identifier,
const CursorInRevision& position = CursorInRevision::invalid(),
const TopDUContext* topContext = nullptr,
SearchFlags flags = NoSearchFlags) const;
/**
* Prefer the version above for speed reasons.
*/
QList findDeclarations(const Identifier& identifier,
const CursorInRevision& position = CursorInRevision::invalid(),
const TopDUContext* topContext = nullptr,
SearchFlags flags = NoSearchFlags) const;
/**
* Returns the type of any @a identifier defined in this context, or
* null if one is not found.
*
* Does not search imported parent-contexts(like base-classes).
*/
QList findLocalDeclarations(const IndexedIdentifier& identifier,
const CursorInRevision& position = CursorInRevision::invalid(),
const TopDUContext* topContext = nullptr,
const AbstractType::Ptr& dataType = AbstractType::Ptr(),
SearchFlags flags = NoSearchFlags) const;
/**
* Prefer the version above for speed reasons.
*/
QList findLocalDeclarations(const Identifier& identifier,
const CursorInRevision& position = CursorInRevision::invalid(),
const TopDUContext* topContext = nullptr,
const AbstractType::Ptr& dataType = AbstractType::Ptr(),
SearchFlags flags = NoSearchFlags) const;
/**
* Clears all local declarations.
*
* Does not delete the declaration; the caller assumes ownership.
*/
QVector clearLocalDeclarations();
/**
* Clears all local declarations.
*
* Deletes these declarations, as the context has ownership.
*/
void deleteLocalDeclarations();
/**
* Returns all local declarations
*
* @param source A source-context that is needed to instantiate template-declarations in some cases.
* If it is zero, that signalizes that missing members should not be instantiated.
*/
virtual QVector localDeclarations(const TopDUContext* source = nullptr) const;
/**
* Resort the local declarations by their range.
*
* You must call this when you manually change the range of declarations in a way
* that could break the internal range sorting.
*/
void resortLocalDeclarations();
/**
* Searches for the most specific context for the given cursor @a position in the given @a url.
*
* @param location the text position to search for
* @param parent the parent context to search from (this is mostly an internal detail, but if you only
* want to search in a subbranch of the chain, you may specify the parent here)
*
* @returns the requested context if one was found, otherwise null.
*/
DUContext* findContext(const CursorInRevision& position, DUContext* parent = nullptr) const;
/**
* Iterates the tree to see if the provided @a context is a subcontext of this context.
*
* @returns true if @a context is a subcontext, otherwise false.
*/
bool parentContextOf(DUContext* context) const;
/**
* Return a list of all reachable declarations for a given cursor @a position in a given @a url.
*
* @param location the text position to search for
* @param topContext the top-context from where a completion is triggered.
* This is needed so delayed types(templates in C++) can be resolved
* in the correct context.
* @param searchInParents should declarations from parent-contexts be listed?
* If false, only declarations from this and imported contexts will be returned.
*
* The returned declarations are paired together with their inheritance-depth,
* which is the count of steps to into other contexts that were needed to find the declaration.
* Declarations reached through a namespace- or global-context are offsetted by 1000.
*
* This also includes Declarations from sub-contexts that were propagated upwards
* using @c setPropagateDeclarations(true).
*
* @returns the requested declarations, if any were active at that location.
* Declarations propagated into this context(@c setPropagateDeclarations) are included.
*/
QList< QPair > allDeclarations(const CursorInRevision& position,
const TopDUContext* topContext,
bool searchInParents = true) const;
/**
* Delete and remove all slaves (uses, declarations, definitions, contexts) that are not in the given set.
*/
void cleanIfNotEncountered(const QSet& encountered);
/**
* Uses:
* A "Use" represents any position in a document where a Declaration is used literally.
* For efficiency, since there can be many many uses, they are managed efficiently by
* TopDUContext and DUContext. In TopDUContext, the used declarations are registered
* and assigned a "Declaration-Index" while calling TopDUContext::indexForUsedDeclaration.
* From such a declaration-index, the declaration can be retrieved back by calling
* @c TopDUContext::usedDeclarationForIndex.
*
* The actual uses are stored within DUContext, where each use consists of a range and
* the declaration-index of the used declaration.
* */
/**
* Return a vector of all uses which occur in this context.
*
* To get the actual declarations, use @c TopDUContext::usedDeclarationForIndex(..)
* with the declarationIndex.
*/
const Use* uses() const;
/**
* Returns the count of uses that can be accessed through @c uses()
*/
int usesCount() const;
/**
* Determines whether the given declaration has uses or not
*/
static bool declarationHasUses(Declaration* decl);
/**
* Find the use which encompasses @a position, if one exists.
* @return The local index of the use, or -1
*/
int findUseAt(const CursorInRevision& position) const;
/**
* @note The change must not break the ordering
*/
void changeUseRange(int useIndex, const RangeInRevision& range);
/**
* Assigns the declaration represented by @param declarationIndex
* to the use with index @param useIndex.
*/
void setUseDeclaration(int useIndex, int declarationIndex);
/**
* Creates a new use of the declaration given through @param declarationIndex.
* The index must be retrieved through @c TopDUContext::indexForUsedDeclaration(..).
*
* @param range The range of the use
* @param insertBefore A hint where in the vector of uses to insert the use.
* Must be correct so the order is preserved(ordered by position),
* or -1 to automatically choose the position.
*
* @return Local index of the created use
*/
int createUse(int declarationIndex, const RangeInRevision& range, int insertBefore = -1);
/**
* Deletes the use number @param index.
*
* @param index is the position in the vector of uses, not a used declaration index.
*/
void deleteUse(int index);
/**
* Clear and delete all uses in this context.
*/
virtual void deleteUses();
/**
* Recursively delete all uses in this context and all its child-contexts
*/
virtual void deleteUsesRecursively();
/**
* Can be specialized by languages to create a navigation/information-widget.
*
* Ideally, the widget would be based on @c KDevelop::QuickOpenEmbeddedWidgetInterface
* for user-interaction within the quickopen list.
*
* The returned widget will be owned by the caller.
*
* @param decl A member-declaration of this context the navigation-widget should be created for.
* Zero to create a widget for this context.
* @param topContext Top-context from where the navigation-widget is triggered.
* In C++, this is needed to resolve forward-declarations.
* @param htmlPrefix Html-formatted text that should be prepended before any information shown by this widget
* @param htmlSuffix Html-formatted text that should be appended to any information shown by this widget
*
* Can return zero which disables the navigation widget.
*
* If you setProperty("DoNotCloseOnCursorMove", true) on the widget returned,
* then the widget will not close when the cursor moves in the document, which
* enables you to change the document contents from the widget without immediately closing the widget.
*/
virtual QWidget* createNavigationWidget(Declaration* decl = nullptr, TopDUContext* topContext = nullptr,
const QString& htmlPrefix = QString(),
- const QString& htmlSuffix = QString()) const;
+ const QString& htmlSuffix = QString(),
+ AbstractNavigationWidget::DisplayHints hints = AbstractNavigationWidget::NoHints) const;
enum {
Identity = 2
};
/**
* Represents multiple qualified identifiers in a way that is better
* to manipulate and allows applying namespace-aliases or -imports easily.
*
* A SearchItem generally represents a tree of identifiers, and represents
* all the qualified identifiers that can be constructed by walking
* along the tree starting at an arbitrary root-node into the depth using the "next" pointers.
*
* The insertion order in the hierarchy determines the order of the represented list.
*/
struct KDEVPLATFORMLANGUAGE_EXPORT SearchItem : public QSharedData
{
typedef QExplicitlySharedDataPointer Ptr;
typedef KDevVarLengthArray PtrList;
/**
* Constructs a representation of the given @param id qualified identifier,
* starting at its index @param start.
*
* @param nextItem is set as next item to the last item in the chain
*/
SearchItem(const QualifiedIdentifier& id, const Ptr& nextItem = Ptr(), int start = 0);
/**
* Constructs a representation of the given @param id qualified identifier,
* starting at its index @param start.
*
* @param nextItem is set as next item to the last item in the chain
*/
SearchItem(const QualifiedIdentifier& id, const PtrList& nextItems, int start = 0);
SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const PtrList& nextItems);
SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const Ptr& nextItem);
bool isEmpty() const;
bool hasNext() const;
/**
* Appends the given item to every item that can be reached from this item,
* and not only to the end items.
*
* The effect to search is that the given item is searched with all prefixes
* contained in this earch-item prepended.
*
* @warning This changes all contained sub-nodes, but they can be shared with
* other SearchItem trees. You should not use this on SearchItem trees
* that have shared nodes with other trees.
*
* @note These functions ignore explicitly global items.
*/
void addToEachNode(const Ptr& item);
void addToEachNode(const PtrList& items);
/**
* Returns true if the given identifier matches one of the identifiers
* represented by this SearchItem. Does not respect the explicitlyGlobal flag
*/
bool match(const QualifiedIdentifier& id, int offset = 0) const;
/**
* @note expensive
*/
QList toList(const QualifiedIdentifier& prefix = QualifiedIdentifier()) const;
void addNext(const Ptr& other);
bool isExplicitlyGlobal;
IndexedIdentifier identifier;
PtrList next;
};
///@todo Should be protected, moved here temporarily until I have figured
///out why the gcc 4.1.3 fails in cppducontext.h:212, which should work (within kdevelop)
/// Declaration search implementation
/**
* This is a more complex interface to the declaration search engine.
*
* Always prefer @c findDeclarations(..) when possible.
*
* Advantage of this interface:
* - You can search multiple identifiers at one time.
* However, those should be aliased identifiers for one single item, because
* search might stop as soon as one item is found.
* - The source top-context is needed to correctly resolve template-parameters
*
* @param position A valid position, if in doubt use textRange().end()
*
* @warning @p position must be valid!
*
* @param depth Depth of the search in parents. This is used to prevent endless
* recursions in endless import loops.
*
*
* @return whether the search was successful. If it is false, it had to be stopped
* for special reasons (like some flags)
*/
typedef QList DeclarationList;
virtual bool findDeclarationsInternal(const SearchItem::PtrList& identifiers,
const CursorInRevision& position, const AbstractType::Ptr& dataType,
DeclarationList& ret, const TopDUContext* source, SearchFlags flags,
uint depth ) const;
/**
* Returns the qualified identifier @p id with all aliases (for example namespace imports) applied
*
* @example: If the namespace 'Foo' is imported, and id is 'Bar',
* then the returned list is 'Bar' and 'Foo::Bar'
*/
QList fullyApplyAliases(const QualifiedIdentifier& id,
const TopDUContext* source) const;
protected:
/**
* After one scope was searched, this function is asked whether more
* results should be collected. Override it, for example to collect overloaded functions.
*
* The default-implementation returns true as soon as decls is not empty.
*/
virtual bool foundEnough( const DeclarationList& decls , SearchFlags flags ) const;
/**
* Merges definitions and their inheritance-depth up all branches of the
* definition-use chain into one hash.
*
* This includes declarations propagated from sub-contexts.
*
* @param hadUrls is used to count together all contexts that already were
* visited, so they are not visited again.
*/
virtual void mergeDeclarationsInternal(QList< QPair >& definitions,
const CursorInRevision& position,
QHash& hadContexts,
const TopDUContext* source,
bool searchInParents = true, int currentDepth = 0) const;
void findLocalDeclarationsInternal(const Identifier& identifier,
const CursorInRevision & position,
const AbstractType::Ptr& dataType,
DeclarationList& ret,
const TopDUContext* source,
SearchFlags flags ) const;
virtual void findLocalDeclarationsInternal(const IndexedIdentifier& identifier,
const CursorInRevision & position,
const AbstractType::Ptr& dataType,
DeclarationList& ret,
const TopDUContext* source,
SearchFlags flags ) const;
/**
* Applies namespace-imports and namespace-aliases and returns
* possible absolute identifiers that need to be searched.
*
* @param targetIdentifiers will be filled with all identifiers that should
* be searched for, instead of identifier.
* @param onlyImports if this is true, namespace-aliases will not be respected,
* but only imports. This is faster.
*/
void applyAliases(const SearchItem::PtrList& identifiers, SearchItem::PtrList& targetIdentifiers,
const CursorInRevision& position, bool canBeNamespace, bool onlyImports = false) const;
/**
* Applies the aliases that need to be applied when moving the search
* from this context up to the parent-context.
*
* The default-implementation adds a set of identifiers with the own local
* identifier prefixed, if this is a namespace.
*
* For C++, this is needed when searching out of a namespace, so the item
* can be found within that namespace in another place.
*/
virtual void applyUpwardsAliases(SearchItem::PtrList& identifiers, const TopDUContext* source) const;
DUContext(DUContextData& dd, const RangeInRevision& range, DUContext* parent = nullptr, bool anonymous = false);
/**
* Just uses the data from the given context. Doesn't copy or change anything,
* and the data will not be deleted on this contexts destruction.
*/
DUContext(DUContext& useDataFrom);
/**
* Whether this context, or any of its parent contexts, has been inserte
* anonymously into the du-chain
*
* @see DUContext::DUContext
*/
bool isAnonymous() const;
/**
* This is called whenever the search needs to do the decision whether it
* should be continued in the parent context.
*
* It is not called when the DontSearchInParent flag is set. Else this should
* be overridden to do language-specific logic.
*
* The default implementation returns false if the flag InImportedParentContext is set.
*/
virtual bool shouldSearchInParent(SearchFlags flags) const;
private:
void rebuildDynamicData(DUContext* parent, uint ownIndex) override;
friend class TopDUContext;
friend class IndexedDUContext;
friend class LocalIndexedDUContext;
friend class TopDUContextDynamicData;
DUCHAIN_DECLARE_DATA(DUContext)
class DUContextDynamicData* m_dynamicData;
};
/**
* This is the identifier that can be used to search namespace-import declarations,
* and should be used to store namespace-imports.
*
* It is stored statically for performance-reasons, so it doesn't need to be
* constructed every time it is used.
*
* @see NamespaceAliasDeclaration.
*/
KDEVPLATFORMLANGUAGE_EXPORT const Identifier& globalImportIdentifier();
/**
* This is the identifier that can be used to search namespace-alias declarations.
*
* It is stored statically for performance-reasons, so it doesn't need to be
* constructed every time it is used.
*
* @see NamespaceAliasDeclaration.
*/
KDEVPLATFORMLANGUAGE_EXPORT const Identifier& globalAliasIdentifier();
/**
* This is the identifier that can be used to search namespace-import declarations,
* and should be used to store namespace-imports.
*
* It is stored statically for performance-reasons, so it doesn't need to be
* constructed every time it is used.
*
* @see NamespaceAliasDeclaration.
*/
KDEVPLATFORMLANGUAGE_EXPORT const IndexedIdentifier& globalIndexedImportIdentifier();
/**
* This is the identifier that can be used to search namespace-alias declarations.
*
* It is stored statically for performance-reasons, so it doesn't need to be
* constructed every time it is used.
*
* @see NamespaceAliasDeclaration.
*/
KDEVPLATFORMLANGUAGE_EXPORT const IndexedIdentifier& globalIndexedAliasIdentifier();
/**
* Collects all uses of the given @param declarationIndex
*/
KDEVPLATFORMLANGUAGE_EXPORT QList allUses(DUContext* context,
int declarationIndex,
bool noEmptyRanges = false);
}
Q_DECLARE_TYPEINFO(KDevelop::DUContext::Import, Q_MOVABLE_TYPE);
KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug dbg, const KDevelop::DUContext::Import& import);
#endif // KDEVPLATFORM_DUCONTEXT_H
diff --git a/language/duchain/navigation/abstractnavigationwidget.cpp b/language/duchain/navigation/abstractnavigationwidget.cpp
index a0dda8811f..c9f3721be2 100644
--- a/language/duchain/navigation/abstractnavigationwidget.cpp
+++ b/language/duchain/navigation/abstractnavigationwidget.cpp
@@ -1,299 +1,305 @@
/*
Copyright 2007 David Nolden
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "abstractnavigationwidget.h"
#include
#include
#include
#include
#include
#include
#include "../declaration.h"
#include "../ducontext.h"
#include "../duchain.h"
#include "../duchainlock.h"
#include "util/debug.h"
namespace KDevelop {
AbstractNavigationWidget::AbstractNavigationWidget()
: m_browser(0), m_currentWidget(0)
{
setPalette( QApplication::palette() );
setFocusPolicy(Qt::NoFocus);
resize(100, 100);
}
const int maxNavigationWidgetWidth = 580;
QSize AbstractNavigationWidget::sizeHint() const
{
if(m_browser) {
updateIdealSize();
QSize ret = QSize(qMin(m_idealTextSize.width(), maxNavigationWidgetWidth), qMin(m_idealTextSize.height(), 300));
if(m_idealTextSize.height()>=300) { //make space for the scrollbar in case it's not fitting
ret.rwidth() += 17; //m_browser->verticalScrollBar()->width() returns 100, for some reason
}
if(m_currentWidget) {
ret.setHeight( ret.height() + m_currentWidget->sizeHint().height() );
if(m_currentWidget->sizeHint().width() > ret.width())
ret.setWidth(m_currentWidget->sizeHint().width());
if(ret.width() < 500) //When we embed a widget, give it some space, even if it doesn't have a large size-hint
ret.setWidth(500);
}
return ret;
} else
return QWidget::sizeHint();
}
void AbstractNavigationWidget::initBrowser(int height) {
Q_UNUSED(height);
m_browser = new QTextBrowser;
// since we can embed arbitrary HTML we have to make sure it stays readable by forcing a black-white palette
QPalette p;
p.setColor(QPalette::AlternateBase, Qt::white);
p.setColor(QPalette::Base, Qt::white);
p.setColor(QPalette::Text, Qt::black);
m_browser->setPalette( p );
m_browser->setOpenLinks(false);
m_browser->setOpenExternalLinks(false);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(m_browser);
layout->setMargin(0);
setLayout(layout);
connect( m_browser.data(), &QTextBrowser::anchorClicked, this, &AbstractNavigationWidget::anchorClicked );
foreach(QWidget* w, findChildren())
w->setContextMenuPolicy(Qt::NoContextMenu);
}
AbstractNavigationWidget::~AbstractNavigationWidget() {
if(m_currentWidget)
layout()->removeWidget(m_currentWidget);
}
void AbstractNavigationWidget::setContext(NavigationContextPointer context, int initBrows)
{
if(m_browser == 0)
initBrowser(initBrows);
if(!context) {
qCDebug(LANGUAGE) << "no new context created";
return;
}
if(context == m_context && (!context || context->alreadyComputed()))
return;
if (!m_startContext)
m_startContext = m_context;
bool wasInitial = (m_context == m_startContext);
m_context = context;
update();
emit contextChanged(wasInitial, m_context == m_startContext);
emit sizeHintChanged();
}
void AbstractNavigationWidget::updateIdealSize() const {
if(m_context && !m_idealTextSize.isValid()) {
QTextDocument doc;
doc.setHtml(m_currentText);
if(doc.idealWidth() > maxNavigationWidgetWidth) {
doc.setTextWidth(maxNavigationWidgetWidth);
m_idealTextSize.setWidth(maxNavigationWidgetWidth);
}else{
m_idealTextSize.setWidth(doc.idealWidth());
}
m_idealTextSize.setHeight(doc.size().height());
}
}
+void AbstractNavigationWidget::setDisplayHints(DisplayHints hints) {
+ m_hints = hints;
+}
+
void AbstractNavigationWidget::update() {
setUpdatesEnabled(false);
Q_ASSERT( m_context );
QString html = m_context->html();
if(!html.isEmpty()) {
int scrollPos = m_browser->verticalScrollBar()->value();
- // TODO: Only show that the first time, or the first few times this context is shown?
- html += QStringLiteral("");
- if (m_context->linkCount() > 0) {
- html += i18n("(Hold 'Alt' to show. Navigate via arrow keys, activate by pressing 'Enter')");
- } else {
- html += i18n("(Hold 'Alt' to show this tooltip)");
+ if ( !(m_hints & EmbeddableWidget)) {
+ // TODO: Only show that the first time, or the first few times this context is shown?
+ html += QStringLiteral("
");
+ if (m_context->linkCount() > 0) {
+ html += i18n("(Hold 'Alt' to show. Navigate via arrow keys, activate by pressing 'Enter')");
+ } else {
+ html += i18n("(Hold 'Alt' to show this tooltip)");
+ }
+ html += QStringLiteral("
");
}
- html += QStringLiteral("
");
m_browser->setHtml( html );
m_currentText = html;
m_idealTextSize = QSize();
QSize hint = sizeHint();
if(hint.height() >= m_idealTextSize.height()) {
m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}else{
m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
m_browser->verticalScrollBar()->setValue(scrollPos);
m_browser->scrollToAnchor(QStringLiteral("currentPosition"));
m_browser->show();
}else{
m_browser->hide();
}
if(m_currentWidget) {
layout()->removeWidget(m_currentWidget);
m_currentWidget->setParent(0);
}
m_currentWidget = m_context->widget();
m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_browser->setMaximumHeight(10000);
if(m_currentWidget) {
if (m_currentWidget->metaObject()
->indexOfSignal(QMetaObject::normalizedSignature("navigateDeclaration(KDevelop::IndexedDeclaration)")) != -1)
{
connect(m_currentWidget, SIGNAL(navigateDeclaration(KDevelop::IndexedDeclaration)),
this, SLOT(navigateDeclaration(KDevelop::IndexedDeclaration)));
}
layout()->addWidget(m_currentWidget);
if(m_context->isWidgetMaximized()) {
//Leave unused room to the widget
m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_browser->setMaximumHeight(m_idealTextSize.height());
}
}
setUpdatesEnabled(true);
}
NavigationContextPointer AbstractNavigationWidget::context() {
return m_context;
}
void AbstractNavigationWidget::navigateDeclaration(KDevelop::IndexedDeclaration decl) {
DUChainReadLocker lock( DUChain::lock() );
setContext(m_context->accept(decl));
}
void AbstractNavigationWidget::anchorClicked(const QUrl& url) {
//We may get deleted while the call to acceptLink, so make sure we don't crash in that case
QPointer thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->acceptLink(url.toString());
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::next() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
m_context->nextLink();
update();
}
void AbstractNavigationWidget::previous() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
m_context->previousLink();
update();
}
void AbstractNavigationWidget::accept() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
QPointer thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->accept();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::back() {
DUChainReadLocker lock( DUChain::lock() );
QPointer thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->back();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::up() {
DUChainReadLocker lock( DUChain::lock() );
m_context->up();
update();
}
void AbstractNavigationWidget::down() {
DUChainReadLocker lock( DUChain::lock() );
m_context->down();
update();
}
void AbstractNavigationWidget::embeddedWidgetAccept() {
accept();
}
void AbstractNavigationWidget::embeddedWidgetDown() {
down();
}
void AbstractNavigationWidget::embeddedWidgetRight() {
next();
}
void AbstractNavigationWidget::embeddedWidgetLeft() {
previous();
}
void AbstractNavigationWidget::embeddedWidgetUp() {
up();
}
void AbstractNavigationWidget::wheelEvent(QWheelEvent* event )
{
QWidget::wheelEvent(event);
event->accept();
return;
}
}
diff --git a/language/duchain/navigation/abstractnavigationwidget.h b/language/duchain/navigation/abstractnavigationwidget.h
index 69facf6972..d3e82d1402 100644
--- a/language/duchain/navigation/abstractnavigationwidget.h
+++ b/language/duchain/navigation/abstractnavigationwidget.h
@@ -1,106 +1,113 @@
/*
Copyright 2007 David Nolden
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H
#define KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H
#include
#include
#include
#include
#include "../../interfaces/quickopendataprovider.h"
#include
#include "abstractnavigationcontext.h"
class QTextBrowser;
namespace KDevelop {
/**
* This class deleted itself when its part is deleted, so always use a QPointer when referencing it.
* The duchain must be read-locked for most operations
* */
class KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationWidget : public QWidget, public QuickOpenEmbeddedWidgetInterface
{
Q_OBJECT
public:
- AbstractNavigationWidget();
+ enum DisplayHint {
+ NoHints = 0x0, // < Normal display
+ EmbeddableWidget = 0x1, // < Omit parts which are only useful for the navigation popup
+ };
+ Q_DECLARE_FLAGS(DisplayHints, DisplayHint);
+ AbstractNavigationWidget();
~AbstractNavigationWidget() override;
void setContext(NavigationContextPointer context, int initBrowser = 400);
+ void setDisplayHints(DisplayHints hints);
QSize sizeHint() const override;
public slots:
///Keyboard-action "next"
void next() override;
///Keyboard-action "previous"
void previous() override;
///Keyboard-action "accept"
void accept() override;
void up() override;
void down() override;
virtual void back();
///These are temporarily for gettings these events directly from kate
///@todo Do this through a public interface post 4.2
void embeddedWidgetRight();
///Keyboard-action "previous"
void embeddedWidgetLeft();
///Keyboard-action "accept"
void embeddedWidgetAccept();
void embeddedWidgetUp();
void embeddedWidgetDown();
NavigationContextPointer context();
Q_SIGNALS:
void sizeHintChanged();
/// Emitted whenever the current navigation-context has changed
/// @param wasInitial whether the previous context was the initial context
/// @param isInitial whether the current context is the initial context
void contextChanged(bool wasInitial, bool isInitial);
public slots:
void navigateDeclaration(KDevelop::IndexedDeclaration decl);
private slots:
void anchorClicked(const QUrl&);
protected:
void wheelEvent(QWheelEvent* ) override;
void updateIdealSize() const;
void initBrowser(int height);
void update();
NavigationContextPointer m_startContext;
TopDUContextPointer m_topContext;
QPointer m_browser;
QWidget* m_currentWidget;
QString m_currentText;
mutable QSize m_idealTextSize;
+ DisplayHints m_hints = NoHints;
private:
NavigationContextPointer m_context;
};
}
#endif
diff --git a/plugins/contextbrowser/contextbrowserview.cpp b/plugins/contextbrowser/contextbrowserview.cpp
index f8db5535dc..a39a71cdf9 100644
--- a/plugins/contextbrowser/contextbrowserview.cpp
+++ b/plugins/contextbrowser/contextbrowserview.cpp
@@ -1,315 +1,315 @@
/*
* This file is part of KDevelop
*
* Copyright 2008 David Nolden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "contextbrowserview.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "contextbrowser.h"
#include "debug.h"
#include
#include
#include
#include "browsemanager.h"
#include
#include
#include
#include
#include
#include
using namespace KDevelop;
QWidget* ContextBrowserView::createWidget(KDevelop::DUContext* context) {
m_context = IndexedDUContext(context);
if(m_context.data()) {
- return m_context.data()->createNavigationWidget();
+ return m_context.data()->createNavigationWidget(nullptr, nullptr, {}, {}, AbstractNavigationWidget::EmbeddableWidget);
}
return 0;
}
KDevelop::IndexedDeclaration ContextBrowserView::declaration() const {
return m_declaration;
}
QWidget* ContextBrowserView::createWidget(Declaration* decl, TopDUContext* topContext) {
m_declaration = IndexedDeclaration(decl);
- return decl->context()->createNavigationWidget(decl, topContext);
+ return decl->context()->createNavigationWidget(decl, topContext, {}, {}, AbstractNavigationWidget::EmbeddableWidget);
}
void ContextBrowserView::resetWidget()
{
if (m_navigationWidget) {
delete m_navigationWidget;
m_navigationWidget = 0;
}
}
void ContextBrowserView::declarationMenu() {
DUChainReadLocker lock(DUChain::lock());
AbstractNavigationWidget* navigationWidget = dynamic_cast(m_navigationWidget.data());
if(navigationWidget) {
AbstractDeclarationNavigationContext* navigationContext = dynamic_cast(navigationWidget->context().data());
if(navigationContext && navigationContext->declaration().data()) {
KDevelop::DeclarationContext* c = new KDevelop::DeclarationContext(navigationContext->declaration().data());
lock.unlock();
QMenu menu;
QList extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions( c );
ContextMenuExtension::populateMenu(&menu, extensions);
menu.exec(QCursor::pos());
}
}
}
void ContextBrowserView::updateLockIcon(bool checked) {
m_lockButton->setIcon(QIcon::fromTheme(checked ? QStringLiteral("document-encrypt") : QStringLiteral("document-decrypt")));
}
ContextBrowserView::ContextBrowserView( ContextBrowserPlugin* plugin, QWidget* parent ) : QWidget(parent), m_plugin(plugin), m_navigationWidget(new QTextBrowser()), m_autoLocked(false) {
setWindowIcon( QIcon::fromTheme(QStringLiteral("code-context"), windowIcon()) );
m_allowLockedUpdate = false;
m_buttons = new QHBoxLayout;
m_buttons->addStretch();
m_declarationMenuButton = new QToolButton();
m_declarationMenuButton->setIcon(QIcon::fromTheme(QStringLiteral("code-class")));
m_declarationMenuButton->setToolTip(i18n("Declaration menu"));
connect(m_declarationMenuButton, &QToolButton::clicked, this, &ContextBrowserView::declarationMenu);
m_buttons->addWidget(m_declarationMenuButton);
m_lockButton = new QToolButton();
m_lockButton->setCheckable(true);
m_lockButton->setChecked(false);
m_lockButton->setToolTip(i18n("Lock current view"));
updateLockIcon(m_lockButton->isChecked());
connect(m_lockButton, &QToolButton::toggled, this, &ContextBrowserView::updateLockIcon);
m_buttons->addWidget(m_lockButton);
m_layout = new QVBoxLayout;
m_layout->setSpacing(0);
m_layout->setMargin(0);
m_layout->addLayout(m_buttons);
m_layout->addWidget(m_navigationWidget);
//m_layout->addStretch();
setLayout(m_layout);
m_plugin->registerToolView(this);
}
ContextBrowserView::~ContextBrowserView() {
m_plugin->unRegisterToolView(this);
}
void ContextBrowserView::focusInEvent(QFocusEvent* event) {
//Indicate that we have focus
qCDebug(PLUGIN_CONTEXTBROWSER) << "got focus";
// parentWidget()->setBackgroundRole(QPalette::ToolTipBase);
/* m_layout->removeItem(m_buttons);*/
return QWidget::focusInEvent(event);
}
void ContextBrowserView::focusOutEvent(QFocusEvent* event) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "lost focus";
// parentWidget()->setBackgroundRole(QPalette::Background);
/* m_layout->insertLayout(0, m_buttons);
for(int a = 0; a < m_buttons->count(); ++a) {
QWidgetItem* item = dynamic_cast(m_buttons->itemAt(a));
}*/
QWidget::focusOutEvent(event);
}
bool ContextBrowserView::event(QEvent* event) {
QKeyEvent* keyEvent = dynamic_cast(event);
if(hasFocus() && keyEvent) {
AbstractNavigationWidget* navigationWidget = dynamic_cast(m_navigationWidget.data());
if(navigationWidget && event->type() == QEvent::KeyPress) {
int key = keyEvent->key();
if(key == Qt::Key_Left)
navigationWidget->previous();
if(key == Qt::Key_Right)
navigationWidget->next();
if(key == Qt::Key_Up)
navigationWidget->up();
if(key == Qt::Key_Down)
navigationWidget->down();
if(key == Qt::Key_Return || key == Qt::Key_Enter)
navigationWidget->accept();
if(key == Qt::Key_L)
m_lockButton->toggle();
}
}
return QWidget::event(event);
}
void ContextBrowserView::showEvent(QShowEvent* event)
{
DUChainReadLocker lock(DUChain::lock(), 200);
if (!lock.locked()) {
QWidget::showEvent(event);
return;
}
TopDUContext* top = m_lastUsedTopContext.data();
if(top && m_navigationWidgetDeclaration.isValid() && m_navigationWidgetDeclaration.getDeclaration(top)) {
if(top) {
//Update the navigation-widget
Declaration* decl = m_navigationWidgetDeclaration.getDeclaration(top);
setDeclaration(decl, top, true);
}
}
QWidget::showEvent(event);
}
bool ContextBrowserView::isLocked() const {
bool isLocked;
if (m_allowLockedUpdate) {
isLocked = false;
} else {
isLocked = m_lockButton->isChecked();
}
return isLocked;
}
void ContextBrowserView::updateMainWidget(QWidget* widget)
{
if (widget) {
setUpdatesEnabled(false);
qCDebug(PLUGIN_CONTEXTBROWSER) << "";
resetWidget();
m_navigationWidget = widget;
m_layout->insertWidget(1, widget, 1);
m_allowLockedUpdate = false;
setUpdatesEnabled(true);
if (widget->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("contextChanged(bool,bool)")) != -1) {
connect(widget, SIGNAL(contextChanged(bool,bool)), this, SLOT(navigationContextChanged(bool,bool)));
}
}
}
void ContextBrowserView::navigationContextChanged(bool wasInitial, bool isInitial)
{
if(wasInitial && !isInitial && !m_lockButton->isChecked())
{
m_autoLocked = true;
m_lockButton->setChecked(true);
}else if(!wasInitial && isInitial && m_autoLocked)
{
m_autoLocked = false;
m_lockButton->setChecked(false);
}else if(isInitial) {
m_autoLocked = false;
}
}
void ContextBrowserView::setDeclaration(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext, bool force) {
m_lastUsedTopContext = IndexedTopDUContext(topContext);
if(isLocked() && (!m_navigationWidget.data() || !isVisible()))
{
// Automatically remove the locked state if the view is not visible or the widget was deleted,
// because the locked state has side-effects on other navigation functionality.
m_autoLocked = false;
m_lockButton->setChecked(false);
}
if(m_navigationWidgetDeclaration == decl->id() && !force)
return;
m_navigationWidgetDeclaration = decl->id();
if (!isLocked() && (isVisible() || force)) { // NO-OP if toolview is hidden, for performance reasons
QWidget* w = createWidget(decl, topContext);
updateMainWidget(w);
}
}
KDevelop::IndexedDeclaration ContextBrowserView::lockedDeclaration() const {
if(m_lockButton->isChecked())
return declaration();
else
return KDevelop::IndexedDeclaration();
}
void ContextBrowserView::allowLockedUpdate() {
m_allowLockedUpdate = true;
}
void ContextBrowserView::setNavigationWidget(QWidget* widget) {
updateMainWidget(widget);
}
void ContextBrowserView::setContext(KDevelop::DUContext* context) {
if(!context)
return;
m_lastUsedTopContext = IndexedTopDUContext(context->topContext());
if(context->owner()) {
if(context->owner()->id() == m_navigationWidgetDeclaration)
return;
m_navigationWidgetDeclaration = context->owner()->id();
}else{
m_navigationWidgetDeclaration = DeclarationId();
}
if (!isLocked() && isVisible()) { // NO-OP if toolview is hidden, for performance reasons
QWidget* w = createWidget(context);
updateMainWidget(w);
}
}
void ContextBrowserView::setSpecialNavigationWidget(QWidget* widget) {
if (!isLocked() && isVisible()) {
Q_ASSERT(widget);
updateMainWidget(widget);
} else if(widget) {
widget->deleteLater();
}
}
diff --git a/plugins/quickopen/duchainitemquickopen.cpp b/plugins/quickopen/duchainitemquickopen.cpp
index c6682d8c9a..c938867252 100644
--- a/plugins/quickopen/duchainitemquickopen.cpp
+++ b/plugins/quickopen/duchainitemquickopen.cpp
@@ -1,254 +1,254 @@
/* This file is part of the KDE libraries
Copyright (C) 2007 David Nolden
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "projectitemquickopen.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace KDevelop;
DUChainItemData::DUChainItemData( const DUChainItem& file, bool openDefinition )
: m_item(file)
, m_openDefinition(openDefinition)
{
}
QString DUChainItemData::text() const
{
DUChainReadLocker lock;;
Declaration* decl = m_item.m_item.data();
if(!decl)
return i18n("Not available any more: %1", m_item.m_text);
if(FunctionDefinition* def = dynamic_cast(decl)) {
if(def->declaration()) {
decl = def->declaration();
}
}
QString text = decl->qualifiedIdentifier().toString();
if(!decl->abstractType()) {
//With simplified representation, still mark functions as such by adding parens
if(dynamic_cast(decl)) {
text += QLatin1String("(...)");
}
}else if(TypePtr function = decl->type()) {
text += function->partToString( FunctionType::SignatureArguments );
}
return text;
}
QList DUChainItemData::highlighting() const
{
DUChainReadLocker lock;;
Declaration* decl = m_item.m_item.data();
if(!decl) {
return QList();
}
if(FunctionDefinition* def = dynamic_cast(decl)) {
if(def->declaration()) {
decl = def->declaration();
}
}
QTextCharFormat boldFormat;
boldFormat.setFontWeight(QFont::Bold);
QTextCharFormat normalFormat;
int prefixLength = 0;
QString signature;
TypePtr function = decl->type();
if(function) {
signature = function->partToString( FunctionType::SignatureArguments );
}
//Only highlight the last part of the qualified identifier, so the scope doesn't distract too much
QualifiedIdentifier id = decl->qualifiedIdentifier();
QString fullId = id.toString();
QString lastId;
if(!id.isEmpty()) {
lastId = id.last().toString();
}
prefixLength += fullId.length() - lastId.length();
QList ret;
ret << 0;
ret << prefixLength;
ret << QVariant(normalFormat);
ret << prefixLength;
ret << lastId.length();
ret << QVariant(boldFormat);
if(!signature.isEmpty()) {
ret << prefixLength + lastId.length();
ret << signature.length();
ret << QVariant(normalFormat);
}
return ret;
}
QString DUChainItemData::htmlDescription() const
{
if(m_item.m_noHtmlDestription) {
return QString();
}
DUChainReadLocker lock;;
Declaration* decl = m_item.m_item.data();
if(!decl) {
return i18n("Not available any more");
}
TypePtr function = decl->type();
QString text;
if( function && function->returnType() ) {
text = i18nc("%1: function signature", "Return: %1",
function->partToString(FunctionType::SignatureReturn));
}
text += ' ' + i18nc("%1: file path", "File: %1", decl->url().str());
QString ret = "" + text + "";
if(!m_item.m_project.isEmpty()) {
ret.prepend(i18n("Project %1", m_item.m_project) + (ret.isEmpty() ? ", " : ""));
}
return ret;
}
bool DUChainItemData::execute( QString& /*filterText*/ )
{
DUChainReadLocker lock;;
Declaration* decl = m_item.m_item.data();
if(!decl) {
return false;
}
if(m_openDefinition && FunctionDefinition::definition(decl)) {
decl = FunctionDefinition::definition(decl);
}
QUrl url = decl->url().toUrl();
KTextEditor::Cursor cursor = decl->rangeInCurrentRevision().start();
DUContext* internal = decl->internalContext();
if(internal && (internal->type() == DUContext::Other || internal->type() == DUContext::Class)) {
//Move into the body
if(internal->range().end.line > internal->range().start.line) {
cursor = KTextEditor::Cursor(internal->range().start.line+1, 0); //Move into the body
}
}
lock.unlock();
ICore::self()->documentController()->openDocument( url, cursor );
return true;
}
bool DUChainItemData::isExpandable() const
{
return true;
}
QWidget* DUChainItemData::expandingWidget() const
{
DUChainReadLocker lock;;
Declaration* decl = dynamic_cast(m_item.m_item.data());
if( !decl || !decl->context() ) {
return 0;
}
return decl->context()->createNavigationWidget( decl, decl->topContext(),
m_item.m_project.isEmpty()
? QString()
- : ("" + i18n("Project %1", m_item.m_project) + "
")
- );
+ : ("" + i18n("Project %1", m_item.m_project) + "
"), QString(),
+ KDevelop::AbstractNavigationWidget::EmbeddableWidget);
}
QIcon DUChainItemData::icon() const
{
return QIcon();
}
DUChainItemDataProvider::DUChainItemDataProvider( IQuickOpen* quickopen, bool openDefinitions )
: m_quickopen(quickopen)
, m_openDefinitions(openDefinitions)
{
reset();
}
void DUChainItemDataProvider::setFilterText( const QString& text )
{
Base::setFilter( text );
}
uint DUChainItemDataProvider::itemCount() const
{
return Base::filteredItems().count();
}
uint DUChainItemDataProvider::unfilteredItemCount() const
{
return Base::items().count();
}
QuickOpenDataPointer DUChainItemDataProvider::data( uint row ) const
{
return KDevelop::QuickOpenDataPointer( createData( Base::filteredItems()[row] ) );
}
DUChainItemData* DUChainItemDataProvider::createData( const DUChainItem& item ) const
{
return new DUChainItemData( item, m_openDefinitions );
}
QString DUChainItemDataProvider::itemText( const DUChainItem& data ) const
{
return data.m_text;
}
void DUChainItemDataProvider::reset()
{
}