diff --git a/kdevplatform/language/codegen/basicrefactoring.cpp b/kdevplatform/language/codegen/basicrefactoring.cpp index 0def6fed5f..b6d66ad540 100644 --- a/kdevplatform/language/codegen/basicrefactoring.cpp +++ b/kdevplatform/language/codegen/basicrefactoring.cpp @@ -1,374 +1,373 @@ /* This file is part of KDevelop * * Copyright 2014 Miquel Sabaté * * 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. */ // Qt #include // KF #include #include #include #include // KDevelop #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "progressdialogs/refactoringdialog.h" #include #include "ui_basicrefactoring.h" namespace { QPair splitFileAtExtension(const QString& fileName) { int idx = fileName.indexOf(QLatin1Char('.')); if (idx == -1) { return qMakePair(fileName, QString()); } return qMakePair(fileName.left(idx), fileName.mid(idx)); } } using namespace KDevelop; //BEGIN: BasicRefactoringCollector BasicRefactoringCollector::BasicRefactoringCollector(const IndexedDeclaration& decl) : UsesWidgetCollector(decl) { setCollectConstructors(true); setCollectDefinitions(true); setCollectOverloads(true); } QVector BasicRefactoringCollector::allUsingContexts() const { return m_allUsingContexts; } void BasicRefactoringCollector::processUses(KDevelop::ReferencedTopDUContext topContext) { m_allUsingContexts << IndexedTopDUContext(topContext.data()); UsesWidgetCollector::processUses(topContext); } //END: BasicRefactoringCollector //BEGIN: BasicRefactoring BasicRefactoring::BasicRefactoring(QObject* parent) : QObject(parent) { /* There's nothing to do here. */ } void BasicRefactoring::fillContextMenu(ContextMenuExtension& extension, Context* context, QWidget* parent) { auto* declContext = dynamic_cast(context); if (!declContext) return; DUChainReadLocker lock; Declaration* declaration = declContext->declaration().data(); if (declaration && acceptForContextMenu(declaration)) { QFileInfo finfo(declaration->topContext()->url().str()); if (finfo.isWritable()) { QAction* action = new QAction(i18n("Rename \"%1\"...", declaration->qualifiedIdentifier().toString()), parent); action->setData(QVariant::fromValue(IndexedDeclaration(declaration))); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); connect(action, &QAction::triggered, this, &BasicRefactoring::executeRenameAction); extension.addAction(ContextMenuExtension::RefactorGroup, action); } } } bool BasicRefactoring::shouldRenameUses(KDevelop::Declaration* declaration) const { // Now we know we're editing a declaration, but some declarations we don't offer a rename for // basically that's any declaration that wouldn't be fully renamed just by renaming its uses(). if (declaration->internalContext() || declaration->isForwardDeclaration()) { //make an exception for non-class functions if (!declaration->isFunctionDeclaration() || dynamic_cast(declaration)) return false; } return true; } QString BasicRefactoring::newFileName(const QUrl& current, const QString& newName) { QPair nameExtensionPair = splitFileAtExtension(current.fileName()); // if current file is lowercased, keep that if (nameExtensionPair.first == nameExtensionPair.first.toLower()) { return newName.toLower() + nameExtensionPair.second; } else { return newName + nameExtensionPair.second; } } DocumentChangeSet::ChangeResult BasicRefactoring::addRenameFileChanges(const QUrl& current, const QString& newName, DocumentChangeSet* changes) { return changes->addDocumentRenameChange( IndexedString(current), IndexedString(newFileName(current, newName))); } bool BasicRefactoring::shouldRenameFile(Declaration* declaration) { // only try to rename files when we renamed a class/struct if (!dynamic_cast(declaration)) { return false; } const QUrl currUrl = declaration->topContext()->url().toUrl(); const QString fileName = currUrl.fileName(); const QPair nameExtensionPair = splitFileAtExtension(fileName); // check whether we renamed something that is called like the document it lives in return nameExtensionPair.first.compare(declaration->identifier().toString(), Qt::CaseInsensitive) == 0; } DocumentChangeSet::ChangeResult BasicRefactoring::applyChanges(const QString& oldName, const QString& newName, DocumentChangeSet& changes, DUContext* context, int usedDeclarationIndex) { if (usedDeclarationIndex == std::numeric_limits::max()) return DocumentChangeSet::ChangeResult::successfulResult(); for (int a = 0; a < context->usesCount(); ++a) { const Use& use(context->uses()[a]); if (use.m_declarationIndex != usedDeclarationIndex) continue; if (use.m_range.isEmpty()) { qCDebug(LANGUAGE) << "found empty use"; continue; } DocumentChangeSet::ChangeResult result = changes.addChange(DocumentChange(context->url(), context->transformFromLocalRevision(use.m_range), oldName, newName)); if (!result) return result; } foreach (DUContext* child, context->childContexts()) { DocumentChangeSet::ChangeResult result = applyChanges(oldName, newName, changes, child, usedDeclarationIndex); if (!result) return result; } return DocumentChangeSet::ChangeResult::successfulResult(); } DocumentChangeSet::ChangeResult BasicRefactoring::applyChangesToDeclarations(const QString& oldName, const QString& newName, DocumentChangeSet& changes, const QList& declarations) { for (auto& decl : declarations) { Declaration* declaration = decl.data(); if (!declaration) continue; if (declaration->range().isEmpty()) qCDebug(LANGUAGE) << "found empty declaration"; TopDUContext* top = declaration->topContext(); DocumentChangeSet::ChangeResult result = changes.addChange(DocumentChange(top->url(), declaration->rangeInCurrentRevision(), oldName, newName)); if (!result) return result; } return DocumentChangeSet::ChangeResult::successfulResult(); } KDevelop::IndexedDeclaration BasicRefactoring::declarationUnderCursor(bool allowUse) { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if (!view) return KDevelop::IndexedDeclaration(); KTextEditor::Document* doc = view->document(); DUChainReadLocker lock; if (allowUse) return DUChainUtils::itemUnderCursor(doc->url(), KTextEditor::Cursor(view->cursorPosition())).declaration; else return DUChainUtils::declarationInLine(KTextEditor::Cursor( view->cursorPosition()), DUChainUtils::standardContextForUrl(doc->url())); } void BasicRefactoring::startInteractiveRename(const KDevelop::IndexedDeclaration& decl) { DUChainReadLocker lock(DUChain::lock()); Declaration* declaration = decl.data(); if (!declaration) { KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), i18n("No declaration under cursor")); return; } QFileInfo info(declaration->topContext()->url().str()); if (!info.isWritable()) { KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), i18n("Declaration is located in non-writable file %1.", declaration->topContext()->url().str())); return; } QString originalName = declaration->identifier().identifier().str(); lock.unlock(); NameAndCollector nc = newNameForDeclaration(DeclarationPointer(declaration)); if (nc.newName == originalName || nc.newName.isEmpty()) return; renameCollectedDeclarations(nc.collector.data(), nc.newName, originalName); } bool BasicRefactoring::acceptForContextMenu(const Declaration* decl) { // Default implementation. Some language plugins might override it to // handle some cases. Q_UNUSED(decl); return true; } void BasicRefactoring::executeRenameAction() { auto* action = qobject_cast(sender()); if (action) { IndexedDeclaration decl = action->data().value(); if (!decl.isValid()) decl = declarationUnderCursor(); if (!decl.isValid()) return; startInteractiveRename(decl); } } BasicRefactoring::NameAndCollector BasicRefactoring::newNameForDeclaration( const KDevelop::DeclarationPointer& declaration) { DUChainReadLocker lock; if (!declaration) { return {}; } QSharedPointer collector(new BasicRefactoringCollector(declaration.data())); Ui::RenameDialog renameDialog; QDialog dialog; renameDialog.setupUi(&dialog); UsesWidget uses(declaration.data(), collector); //So the context-links work - QWidget* navigationWidget = declaration->context()->createNavigationWidget(declaration.data()); - auto* abstractNavigationWidget = dynamic_cast(navigationWidget); - if (abstractNavigationWidget) - connect(&uses, &UsesWidget::navigateDeclaration, abstractNavigationWidget, + auto* navigationWidget = declaration->context()->createNavigationWidget(declaration.data()); + if (navigationWidget) + connect(&uses, &UsesWidget::navigateDeclaration, navigationWidget, &AbstractNavigationWidget::navigateDeclaration); QString declarationName = declaration->toString(); dialog.setWindowTitle(i18nc("Renaming some declaration", "Rename \"%1\"", declarationName)); renameDialog.edit->setText(declaration->identifier().identifier().str()); renameDialog.edit->selectAll(); renameDialog.tabWidget->addTab(&uses, i18n("Uses")); if (navigationWidget) renameDialog.tabWidget->addTab(navigationWidget, i18n("Declaration Info")); lock.unlock(); if (dialog.exec() != QDialog::Accepted) return {}; const auto text = renameDialog.edit->text().trimmed(); RefactoringProgressDialog refactoringProgress(i18n("Renaming \"%1\" to \"%2\"", declarationName, text), collector.data()); if (!collector->isReady()) { if (refactoringProgress.exec() != QDialog::Accepted) { // krazy:exclude=crashy return {}; } } //TODO: input validation return { text, collector }; } DocumentChangeSet BasicRefactoring::renameCollectedDeclarations(KDevelop::BasicRefactoringCollector* collector, const QString& replacementName, const QString& originalName, bool apply) { DocumentChangeSet changes; DUChainReadLocker lock; foreach (const KDevelop::IndexedTopDUContext collected, collector->allUsingContexts()) { QSet hadIndices; foreach (const IndexedDeclaration decl, collector->declarations()) { uint usedDeclarationIndex = collected.data()->indexForUsedDeclaration(decl.data(), false); if (hadIndices.contains(usedDeclarationIndex)) continue; hadIndices.insert(usedDeclarationIndex); DocumentChangeSet::ChangeResult result = applyChanges(originalName, replacementName, changes, collected.data(), usedDeclarationIndex); if (!result) { KMessageBox::error(nullptr, i18n("Applying changes failed: %1", result.m_failureReason)); return {}; } } } DocumentChangeSet::ChangeResult result = applyChangesToDeclarations(originalName, replacementName, changes, collector->declarations()); if (!result) { KMessageBox::error(nullptr, i18n("Applying changes failed: %1", result.m_failureReason)); return {}; } ///We have to ignore failed changes for now, since uses of a constructor or of operator() may be created on "(" parens changes.setReplacementPolicy(DocumentChangeSet::IgnoreFailedChange); if (!apply) { return changes; } result = changes.applyAllChanges(); if (!result) { KMessageBox::error(nullptr, i18n("Applying changes failed: %1", result.m_failureReason)); } return {}; } //END: BasicRefactoring diff --git a/kdevplatform/language/duchain/ducontext.cpp b/kdevplatform/language/duchain/ducontext.cpp index db0f742cb4..1636006cf5 100644 --- a/kdevplatform/language/duchain/ducontext.cpp +++ b/kdevplatform/language/duchain/ducontext.cpp @@ -1,1750 +1,1750 @@ /* 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 "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 // 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(nullptr) , 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 = nullptr; 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 = nullptr; 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(nullptr); 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::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(nullptr); //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 auto* 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 { 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; } QVector 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(); } QVector 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; // 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(); SearchItem::PtrList 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; ret.reserve(d_func()->m_importersSize()); FOREACH_FUNCTION(const IndexedDUContext &ctx, d_func()->m_importers) ret << ctx.context(); if (owner()) { //Add indirect importers to the list const KDevVarLengthArray indirect = Importers::self().importers(owner()->id()); ret.reserve(ret.size() + indirect.size()); for (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 nullptr; } 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; } QVector> DUContext::allDeclarations(const CursorInRevision& position, const TopDUContext* topContext, bool searchInParents) const { ENSURE_CAN_READ QVector> 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(QVector>& 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(nullptr); } 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 nullptr; 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)) { auto* 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, nullptr, 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 auto* 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 explicitly 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) << "searching" << position << "in:" << scopeIdentifier(true).toString() << range() << includeRightBorder; if (!range().contains(position) && (!includeRightBorder || range().end != position)) { // qCDebug(LANGUAGE) << "mismatch"; return nullptr; } 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 nullptr; foreach (Declaration* child, m_dynamicData->m_localDeclarations) { if (child->range().contains(position)) { return child; } } return nullptr; } DUContext* DUContext::findContextIncluding(const RangeInRevision& range) const { ENSURE_CAN_READ if (!this->range().contains(range)) return nullptr; 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(nullptr, 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, - AbstractNavigationWidget::DisplayHints hints) const +AbstractNavigationWidget* DUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, + AbstractNavigationWidget::DisplayHints hints) const { if (decl) { auto* widget = new AbstractNavigationWidget; widget->setDisplayHints(hints); AbstractDeclarationNavigationContext* context = new AbstractDeclarationNavigationContext(DeclarationPointer( decl), TopDUContextPointer( topContext)); widget->setContext(NavigationContextPointer(context)); return widget; } else { return nullptr; } } QVector allUses(DUContext* context, int declarationIndex, bool noEmptyUses) { QVector 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(); } QVector DUContext::SearchItem::toList(const QualifiedIdentifier& prefix) const { QVector 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.declaration(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 (auto* 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 nullptr; } 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/kdevplatform/language/duchain/ducontext.h b/kdevplatform/language/duchain/ducontext.h index 64ae965347..dee3185ea7 100644 --- a/kdevplatform/language/duchain/ducontext.h +++ b/kdevplatform/language/duchain/ducontext.h @@ -1,963 +1,960 @@ /* 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(); }; using DUContextPointer = DUChainPointer; /** * 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 @p position. * * The search is recursive, so the most specific context is found. * * @param includeBorders When this is true, contexts will also be found that * have the position on their borders. * * @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 @p 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()) { } explicit 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 @p identifier in this context, which * is currently active at the given text @p position, with the given type @p dataType. * In fact, only items are returned that are declared BEFORE that position. * * @param identifier the identifier of the definition to search for * @param position 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 dataType 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 position 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 @p position in the given @p url. * * @param position 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 @p position in a given @p url. * * @param position 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. */ QVector> 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 @p declarationIndex * to the use with index @p useIndex. */ void setUseDeclaration(int useIndex, int declarationIndex); /** * Creates a new use of the declaration given through @p 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 @p 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. * * 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, - AbstractNavigationWidget::DisplayHints hints = - AbstractNavigationWidget::NoHints) const; + virtual AbstractNavigationWidget* + createNavigationWidget(Declaration* decl = nullptr, TopDUContext* topContext = nullptr, + 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 { using Ptr = QExplicitlySharedDataPointer; using PtrList = KDevVarLengthArray; /** * Constructs a representation of the given @p id qualified identifier, * starting at its index @p start. * * @param nextItem is set as next item to the last item in the chain */ explicit SearchItem(const QualifiedIdentifier& id, const Ptr& nextItem = Ptr(), int start = 0); /** * Constructs a representation of the given @p id qualified identifier, * starting at its index @p start. * * @param nextItems 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 */ QVector 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 using DeclarationList = QList; /** * 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) */ 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' */ QVector 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 @p 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 hadContexts is used to count together all contexts that already were * visited, so they are not visited again. */ virtual void mergeDeclarationsInternal(QVector>& 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 @p declarationIndex */ KDEVPLATFORMLANGUAGE_EXPORT QVector 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/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp index 33b998081d..1291ea515e 100644 --- a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp @@ -1,535 +1,535 @@ /* 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 "abstractnavigationcontext.h" #include #include #include "abstractdeclarationnavigationcontext.h" #include "abstractnavigationwidget.h" #include "usesnavigationcontext.h" #include "../../../interfaces/icore.h" #include "../../../interfaces/idocumentcontroller.h" #include "../functiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../types/functiontype.h" #include "../types/structuretype.h" #include #include #include #include namespace KDevelop { class AbstractNavigationContextPrivate { public: QVector m_children; //Used to keep alive all children until this is deleted int m_selectedLink = 0; //The link currently selected NavigationAction m_selectedLinkAction; //Target of the currently selected link bool m_shorten = false; //A counter used while building the html-code to count the used links. int m_linkCount = -1; //Something else than -1 if the current position is represented by a line-number, not a link. int m_currentLine = 0; int m_currentPositionLine = 0; QMap m_links; QMap m_linkLines; //Holds the line for each link QMap m_intLinks; AbstractNavigationContext* m_previousContext; TopDUContextPointer m_topContext; QString m_currentText; //Here the text is built }; void AbstractNavigationContext::setTopContext(const TopDUContextPointer& context) { d->m_topContext = context; } TopDUContextPointer AbstractNavigationContext::topContext() const { return d->m_topContext; } AbstractNavigationContext::AbstractNavigationContext(const TopDUContextPointer& topContext, AbstractNavigationContext* previousContext) : d(new AbstractNavigationContextPrivate) { d->m_previousContext = previousContext; d->m_topContext = topContext; qRegisterMetaType("KTextEditor::Cursor"); qRegisterMetaType("IDocumentation::Ptr"); } AbstractNavigationContext::~AbstractNavigationContext() { } void AbstractNavigationContext::makeLink(const QString& name, const DeclarationPointer& declaration, NavigationAction::Type actionType) { NavigationAction action(declaration, actionType); makeLink(name, QString(), action); } QString AbstractNavigationContext::createLink(const QString& name, const QString&, const NavigationAction& action) { if (d->m_shorten) { //Do not create links in shortened mode, it's only for viewing return typeHighlight(name.toHtmlEscaped()); } // NOTE: Since the by definition in the HTML standard some uri components // are case-insensitive, we define a new lowercase link-id for each // link. Otherwise Qt 5 seems to mess up the casing and the link // cannot be matched when it's executed. QString hrefId = QStringLiteral("link_%1").arg(d->m_links.count()); d->m_links[hrefId] = action; d->m_intLinks[d->m_linkCount] = action; d->m_linkLines[d->m_linkCount] = d->m_currentLine; if (d->m_currentPositionLine == d->m_currentLine) { d->m_currentPositionLine = -1; d->m_selectedLink = d->m_linkCount; } QString str = name.toHtmlEscaped(); if (d->m_linkCount == d->m_selectedLink) str = QLatin1String("") + str + QLatin1String( ""); QString ret = QLatin1String("m_linkCount == d->m_selectedLink && d->m_currentPositionLine == -1) ? QStringLiteral(" name = \"currentPosition\"") : QString()) + QLatin1Char('>') + str + QLatin1String(""); if (d->m_selectedLink == d->m_linkCount) d->m_selectedLinkAction = action; ++d->m_linkCount; return ret; } void AbstractNavigationContext::makeLink(const QString& name, const QString& targetId, const NavigationAction& action) { modifyHtml() += createLink(name, targetId, action); } void AbstractNavigationContext::clear() { d->m_linkCount = 0; d->m_currentLine = 0; d->m_currentText.clear(); d->m_links.clear(); d->m_intLinks.clear(); d->m_linkLines.clear(); } void AbstractNavigationContext::executeLink(const QString& link) { const auto actionIt = d->m_links.constFind(link); if (actionIt == d->m_links.constEnd()) return; execute(*actionIt); } NavigationContextPointer AbstractNavigationContext::executeKeyAction(const QString& key) { Q_UNUSED(key); return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::execute(const NavigationAction& action) { if (action.targetContext) return NavigationContextPointer(action.targetContext); if (action.type == NavigationAction::ExecuteKey) return executeKeyAction(action.key); if (!action.decl && (action.type != NavigationAction::JumpToSource || action.document.isEmpty())) { qCDebug(LANGUAGE) << "Navigation-action has invalid declaration" << endl; return NavigationContextPointer(this); } switch (action.type) { case NavigationAction::ExecuteKey: break; case NavigationAction::None: qCDebug(LANGUAGE) << "Tried to execute an invalid action in navigation-widget" << endl; break; case NavigationAction::NavigateDeclaration: { auto ctx = dynamic_cast(d->m_previousContext); if (ctx && ctx->declaration() == action.decl) return NavigationContextPointer(d->m_previousContext); return registerChild(action.decl); } case NavigationAction::NavigateUses: { auto* browser = ICore::self()->pluginController()->extensionForPlugin(); if (browser) { browser->showUses(action.decl); return NavigationContextPointer(this); } Q_FALLTHROUGH(); } case NavigationAction::ShowUses: { return registerChild(new UsesNavigationContext(action.decl.data(), this)); } case NavigationAction::JumpToSource: { QUrl doc = action.document; KTextEditor::Cursor cursor = action.cursor; { DUChainReadLocker lock(DUChain::lock()); if (action.decl) { if (doc.isEmpty()) { doc = action.decl->url().toUrl(); /* if(action.decl->internalContext()) cursor = action.decl->internalContext()->range().start() + KTextEditor::Cursor(0, 1); else*/ cursor = action.decl->rangeInCurrentRevision().start(); } action.decl->activateSpecialization(); } } //This is used to execute the slot delayed in the event-loop, so crashes are avoided QMetaObject::invokeMethod(ICore::self()->documentController(), "openDocument", Qt::QueuedConnection, Q_ARG(QUrl, doc), Q_ARG(KTextEditor::Cursor, cursor)); break; } case NavigationAction::ShowDocumentation: { auto doc = ICore::self()->documentationController()->documentationForDeclaration(action.decl.data()); // This is used to execute the slot delayed in the event-loop, so crashes are avoided // which can happen e.g. due to focus change events resulting in tooltip destruction and thus this object QMetaObject::invokeMethod( ICore::self()->documentationController(), "showDocumentation", Qt::QueuedConnection, Q_ARG(IDocumentation::Ptr, doc)); } break; } return NavigationContextPointer(this); } AbstractNavigationContext* AbstractNavigationContext::previousContext() const { return d->m_previousContext; } void AbstractNavigationContext::setPreviousContext(AbstractNavigationContext* previous) { d->m_previousContext = previous; } NavigationContextPointer AbstractNavigationContext::registerChild(AbstractNavigationContext* context) { d->m_children << NavigationContextPointer(context); return d->m_children.last(); } NavigationContextPointer AbstractNavigationContext::registerChild(const DeclarationPointer& declaration) { //We create a navigation-widget here, and steal its context.. evil ;) - QScopedPointer navigationWidget(declaration->context()->createNavigationWidget(declaration.data())); - if (auto* abstractNavigationWidget = - dynamic_cast(navigationWidget.data())) { - NavigationContextPointer ret = abstractNavigationWidget->context(); + QScopedPointer navigationWidget( + declaration->context()->createNavigationWidget(declaration.data())); + if (navigationWidget) { + NavigationContextPointer ret = navigationWidget->context(); ret->setPreviousContext(this); d->m_children << ret; return ret; } else { return NavigationContextPointer(this); } } const int lineJump = 3; void AbstractNavigationContext::down() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } int fromLine = d->m_currentPositionLine; // try to select the next link within our lineJump distance if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { if (fromLine == -1) fromLine = d->m_linkLines[d->m_selectedLink]; for (int newSelectedLink = d->m_selectedLink + 1; newSelectedLink < d->m_linkCount; ++newSelectedLink) { if (d->m_linkLines[newSelectedLink] > fromLine && d->m_linkLines[newSelectedLink] - fromLine <= lineJump) { d->m_selectedLink = newSelectedLink; d->m_currentPositionLine = -1; return; } } } if (fromLine == d->m_currentLine - 1) // nothing to do, we are at the end of the document return; // scroll down by applying the lineJump if (fromLine == -1) fromLine = 0; d->m_currentPositionLine = fromLine + lineJump; if (d->m_currentPositionLine >= d->m_currentLine) d->m_currentPositionLine = d->m_currentLine - 1; } void AbstractNavigationContext::up() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } int fromLine = d->m_currentPositionLine; if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { if (fromLine == -1) fromLine = d->m_linkLines[d->m_selectedLink]; for (int newSelectedLink = d->m_selectedLink - 1; newSelectedLink >= 0; --newSelectedLink) { if (d->m_linkLines[newSelectedLink] < fromLine && fromLine - d->m_linkLines[newSelectedLink] <= lineJump) { d->m_selectedLink = newSelectedLink; d->m_currentPositionLine = -1; return; } } } if (fromLine == -1) fromLine = d->m_currentLine - 1; d->m_currentPositionLine = fromLine - lineJump; if (d->m_currentPositionLine < 0) d->m_currentPositionLine = 0; } void AbstractNavigationContext::nextLink() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } d->m_currentPositionLine = -1; if (d->m_linkCount > 0) d->m_selectedLink = (d->m_selectedLink + 1) % d->m_linkCount; } void AbstractNavigationContext::previousLink() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } d->m_currentPositionLine = -1; if (d->m_linkCount > 0) { --d->m_selectedLink; if (d->m_selectedLink < 0) d->m_selectedLink += d->m_linkCount; } Q_ASSERT(d->m_selectedLink >= 0); } int AbstractNavigationContext::linkCount() const { return d->m_linkCount; } NavigationContextPointer AbstractNavigationContext::back() { if (d->m_previousContext) return NavigationContextPointer(d->m_previousContext); else return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::accept() { if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { NavigationAction action = d->m_intLinks[d->m_selectedLink]; return execute(action); } return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::accept(IndexedDeclaration decl) { if (decl.data()) { NavigationAction action(DeclarationPointer(decl.data()), NavigationAction::NavigateDeclaration); return execute(action); } else { return NavigationContextPointer(this); } } NavigationContextPointer AbstractNavigationContext::acceptLink(const QString& link) { const auto actionIt = d->m_links.constFind(link); if (actionIt == d->m_links.constEnd()) { qCDebug(LANGUAGE) << "Executed unregistered link " << link << endl; return NavigationContextPointer(this); } return execute(*actionIt); } NavigationAction AbstractNavigationContext::currentAction() const { return d->m_selectedLinkAction; } QString AbstractNavigationContext::declarationKind(const DeclarationPointer& decl) { const auto* function = dynamic_cast(decl.data()); QString kind; if (decl->isTypeAlias()) kind = i18n("Typedef"); else if (decl->kind() == Declaration::Type) { if (decl->type()) kind = i18n("Class"); } else if (decl->kind() == Declaration::Instance) { kind = i18n("Variable"); } else if (decl->kind() == Declaration::Namespace) { kind = i18n("Namespace"); } if (auto* alias = dynamic_cast(decl.data())) { if (alias->identifier().isEmpty()) kind = i18n("Namespace import"); else kind = i18n("Namespace alias"); } if (function) kind = i18n("Function"); if (decl->isForwardDeclaration()) kind = i18n("Forward Declaration"); return kind; } QString AbstractNavigationContext::html(bool shorten) { d->m_shorten = shorten; return QString(); } bool AbstractNavigationContext::alreadyComputed() const { return !d->m_currentText.isEmpty(); } bool AbstractNavigationContext::isWidgetMaximized() const { return true; } QWidget* AbstractNavigationContext::widget() const { return nullptr; } ///Splits the string by the given regular expression, but keeps the split-matches at the end of each line static QStringList splitAndKeep(QString str, const QRegExp& regExp) { QStringList ret; int place = regExp.indexIn(str); while (place != -1) { ret << str.left(place + regExp.matchedLength()); str.remove(0, place + regExp.matchedLength()); place = regExp.indexIn(str); } ret << str; return ret; } void AbstractNavigationContext::addHtml(const QString& html) { QRegExp newLineRegExp(QStringLiteral("
|
|

")); foreach (const QString& line, splitAndKeep(html, newLineRegExp)) { d->m_currentText += line; if (line.indexOf(newLineRegExp) != -1) { ++d->m_currentLine; if (d->m_currentLine == d->m_currentPositionLine) { d->m_currentText += QStringLiteral( " <-> "); // ><-> is <-> } } } } QString AbstractNavigationContext::currentHtml() const { return d->m_currentText; } QString Colorizer::operator()(const QString& str) const { QString ret = QLatin1String("") + str + QLatin1String(""); if (m_formatting & Fixed) ret = QLatin1String("") + ret + QLatin1String(""); if (m_formatting & Bold) ret = QLatin1String("") + ret + QLatin1String(""); if (m_formatting & Italic) ret = QLatin1String("") + ret + QLatin1String(""); return ret; } const Colorizer AbstractNavigationContext::typeHighlight(QStringLiteral("006000")); const Colorizer AbstractNavigationContext::errorHighlight(QStringLiteral("990000")); const Colorizer AbstractNavigationContext::labelHighlight(QStringLiteral("000000")); const Colorizer AbstractNavigationContext::codeHighlight(QStringLiteral("005000")); const Colorizer AbstractNavigationContext::propertyHighlight(QStringLiteral("009900")); const Colorizer AbstractNavigationContext::navigationHighlight(QStringLiteral("000099")); const Colorizer AbstractNavigationContext::importantHighlight(QStringLiteral( "000000"), Colorizer::Bold | Colorizer::Italic); const Colorizer AbstractNavigationContext::commentHighlight(QStringLiteral("303030")); const Colorizer AbstractNavigationContext::nameHighlight(QStringLiteral("000000"), Colorizer::Bold); } diff --git a/kdevplatform/shell/mainwindow_p.cpp b/kdevplatform/shell/mainwindow_p.cpp index b586182483..078ac22f5a 100644 --- a/kdevplatform/shell/mainwindow_p.cpp +++ b/kdevplatform/shell/mainwindow_p.cpp @@ -1,484 +1,484 @@ /* This file is part of the KDevelop project Copyright 2002 Falk Brettschneider Copyright 2003 John Firebaugh Copyright 2006 Adam Treat Copyright 2006, 2007 Alexander Dymo This library 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 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 "mainwindow_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "partdocument.h" #include "partcontroller.h" #include "uicontroller.h" #include "statusbar.h" #include "mainwindow.h" #include "textdocument.h" #include "sessioncontroller.h" #include "sourceformattercontroller.h" #include "debug.h" #include "ktexteditorpluginintegration.h" #include "colorschemechooser.h" #include #include #include #include #include namespace KDevelop { MainWindowPrivate::MainWindowPrivate(MainWindow *mainWindow) : QObject(mainWindow) , m_mainWindow(mainWindow) , m_statusBar(nullptr) , lastXMLGUIClientView(nullptr) , m_changingActiveView(false) , m_kateWrapper(new KTextEditorIntegration::MainWindow(mainWindow)) { } void MainWindowPrivate::setupGui() { m_statusBar = new KDevelop::StatusBar(m_mainWindow); setupStatusBar(); } void MainWindowPrivate::setupStatusBar() { QWidget *location = m_mainWindow->statusBarLocation(); if (m_statusBar) location->layout()->addWidget(m_statusBar); } void MainWindowPrivate::addPlugin( IPlugin *plugin ) { qCDebug(SHELL) << "add plugin" << plugin << plugin->componentName(); Q_ASSERT( plugin ); //The direct plugin client can only be added to the first mainwindow if(m_mainWindow == Core::self()->uiControllerInternal()->mainWindows()[0]) m_mainWindow->guiFactory()->addClient( plugin ); Q_ASSERT(!m_pluginCustomClients.contains(plugin)); KXMLGUIClient* ownClient = plugin->createGUIForMainWindow(m_mainWindow); if(ownClient) { m_pluginCustomClients[plugin] = ownClient; connect(plugin, &IPlugin::destroyed, this, &MainWindowPrivate::pluginDestroyed); m_mainWindow->guiFactory()->addClient(ownClient); } } void MainWindowPrivate::pluginDestroyed(QObject* pluginObj) { auto* plugin = static_cast(pluginObj); KXMLGUIClient* p = m_pluginCustomClients.take(plugin); m_mainWindow->guiFactory()->removeClient( p ); delete p; } MainWindowPrivate::~MainWindowPrivate() { qDeleteAll(m_pluginCustomClients); } void MainWindowPrivate::removePlugin( IPlugin *plugin ) { Q_ASSERT( plugin ); pluginDestroyed(plugin); disconnect(plugin, &IPlugin::destroyed, this, &MainWindowPrivate::pluginDestroyed); m_mainWindow->guiFactory()->removeClient( plugin ); } void MainWindowPrivate::updateSourceFormatterGuiClient(bool hasFormatters) { auto sourceFormatterController = Core::self()->sourceFormatterControllerInternal(); auto guiFactory = m_mainWindow->guiFactory(); if (hasFormatters) { guiFactory->addClient(sourceFormatterController); } else { guiFactory->removeClient(sourceFormatterController); } } void MainWindowPrivate::activePartChanged(KParts::Part *part) { if ( Core::self()->uiController()->activeMainWindow() == m_mainWindow) m_mainWindow->createGUI(part); } void MainWindowPrivate::changeActiveView(Sublime::View *view) { //disable updates on a window to avoid toolbar flickering on xmlgui client change Sublime::HoldUpdates s(m_mainWindow); mergeView(view); if(!view) return; auto *doc = dynamic_cast(view->document()); if (doc) { doc->activate(view, m_mainWindow); } else { //activated view is not a part document so we need to remove active part gui ///@todo adymo: only this window needs to remove GUI // KParts::Part *activePart = Core::self()->partController()->activePart(); // if (activePart) // guiFactory()->removeClient(activePart); } } void MainWindowPrivate::mergeView(Sublime::View* view) { PushPositiveValue block(m_changingActiveView, true); // If the previous view was KXMLGUIClient, remove its actions // In the case that that view was removed, lastActiveView // will auto-reset, and xmlguifactory will disconnect that // client, I think. if (lastXMLGUIClientView) { qCDebug(SHELL) << "clearing last XML GUI client" << lastXMLGUIClientView; m_mainWindow->guiFactory()->removeClient(dynamic_cast(lastXMLGUIClientView)); disconnect (lastXMLGUIClientView, &QWidget::destroyed, this, nullptr); lastXMLGUIClientView = nullptr; } if (!view) return; QWidget* viewWidget = view->widget(); Q_ASSERT(viewWidget); qCDebug(SHELL) << "changing active view to" << view << "doc" << view->document() << "mw" << m_mainWindow; // If the new view is KXMLGUIClient, add it. if (auto* c = dynamic_cast(viewWidget)) { qCDebug(SHELL) << "setting new XMLGUI client" << viewWidget; lastXMLGUIClientView = viewWidget; m_mainWindow->guiFactory()->addClient(c); connect(viewWidget, &QWidget::destroyed, this, &MainWindowPrivate::xmlguiclientDestroyed); } } void MainWindowPrivate::xmlguiclientDestroyed(QObject* obj) { /* We're informed the QWidget for the active view that is also KXMLGUIclient is dying. KXMLGUIFactory will not like deleted clients, really. Unfortunately, there's nothing we can do at this point. For example, KateView derives from QWidget and KXMLGUIClient. The destroyed() signal is emitted by ~QWidget. At this point, event attempt to cross-cast to KXMLGUIClient is undefined behaviour. We hope to catch view deletion a bit later, but if we fail, we better report it now, rather than get a weird crash a bit later. */ Q_ASSERT(obj == lastXMLGUIClientView); Q_ASSERT(false && "xmlgui clients management is messed up"); Q_UNUSED(obj); } void MainWindowPrivate::setupActions() { connect(Core::self()->sessionController(), &SessionController::quitSession, this, &MainWindowPrivate::quitAll); QAction* action; const QString app = qApp->applicationName(); action = KStandardAction::preferences( this, SLOT(settingsDialog()), actionCollection()); action->setToolTip( i18nc( "%1 = application name", "Configure %1", app ) ); action->setWhatsThis( i18n( "Lets you customize %1.", app ) ); action = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection()); action->setText( i18n("Configure Notifications...") ); action->setToolTip( i18nc("@info:tooltip", "Configure notifications") ); action->setWhatsThis( i18nc( "@info:whatsthis", "Shows a dialog that lets you configure notifications." ) ); action = actionCollection()->addAction( QStringLiteral("about_platform"), this, SLOT(showAboutPlatform()) ); action->setIcon(QIcon::fromTheme(QStringLiteral("kdevelop"))); action->setText( i18n("About KDevelop Platform") ); action->setMenuRole( QAction::NoRole ); // OSX: prevent QT from hiding this due to conflict with 'About KDevelop' action->setStatusTip( i18n("Show Information about KDevelop Platform") ); action->setWhatsThis( i18nc( "@info:whatsthis", "Shows a dialog with information about KDevelop Platform." ) ); action = actionCollection()->addAction( QStringLiteral("loaded_plugins"), this, SLOT(showLoadedPlugins()) ); action->setIcon(QIcon::fromTheme(QStringLiteral("plugins"))); action->setText( i18n("Loaded Plugins") ); action->setStatusTip( i18n("Show a list of all loaded plugins") ); action->setWhatsThis( i18nc( "@info:whatsthis", "Shows a dialog with information about all loaded plugins." ) ); action = actionCollection()->addAction( QStringLiteral("view_next_window") ); action->setText( i18n( "&Next Window" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoNextWindow ); actionCollection()->setDefaultShortcut(action, Qt::ALT + Qt::SHIFT + Qt::Key_Right ); action->setToolTip( i18nc( "@info:tooltip", "Next window" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the next window." ) ); action->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); action = actionCollection()->addAction( QStringLiteral("view_previous_window") ); action->setText( i18n( "&Previous Window" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoPreviousWindow ); actionCollection()->setDefaultShortcut(action, Qt::ALT + Qt::SHIFT + Qt::Key_Left ); action->setToolTip( i18nc( "@info:tooltip", "Previous window" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the previous window." ) ); action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); action = actionCollection()->addAction(QStringLiteral("next_error")); action->setText(i18n("Jump to Next Outputmark")); actionCollection()->setDefaultShortcut( action, QKeySequence(Qt::Key_F4) ); action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right"))); connect(action, &QAction::triggered, this, &MainWindowPrivate::selectNextItem); action = actionCollection()->addAction(QStringLiteral("prev_error")); action->setText(i18n("Jump to Previous Outputmark")); actionCollection()->setDefaultShortcut( action, QKeySequence(Qt::SHIFT | Qt::Key_F4) ); action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left"))); connect(action, &QAction::triggered, this, &MainWindowPrivate::selectPrevItem); action = actionCollection()->addAction( QStringLiteral("split_horizontal") ); action->setIcon(QIcon::fromTheme( QStringLiteral("view-split-top-bottom") )); action->setText( i18n( "Split View &Top/Bottom" ) ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_T ); connect( action, &QAction::triggered, this, &MainWindowPrivate::splitHorizontal ); action->setToolTip( i18nc( "@info:tooltip", "Split horizontal" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Splits the current view horizontally." ) ); action = actionCollection()->addAction( QStringLiteral("split_vertical") ); action->setIcon(QIcon::fromTheme( QStringLiteral("view-split-left-right") )); action->setText( i18n( "Split View &Left/Right" ) ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_L ); connect( action, &QAction::triggered, this, &MainWindowPrivate::splitVertical ); action->setToolTip( i18nc( "@info:tooltip", "Split vertical" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Splits the current view vertically." ) ); action = actionCollection()->addAction( QStringLiteral("view_next_split") ); action->setText( i18n( "&Next Split View" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoNextSplit ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_N ); action->setToolTip( i18nc( "@info:tooltip", "Next split view" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the next split view." ) ); action->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); action = actionCollection()->addAction( QStringLiteral("view_previous_split") ); action->setText( i18n( "&Previous Split View" ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::gotoPreviousSplit ); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_P ); action->setToolTip( i18nc( "@info:tooltip", "Previous split view" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Switches to the previous split view." ) ); action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); KStandardAction::fullScreen( this, SLOT(toggleFullScreen(bool)), m_mainWindow, actionCollection() ); action = actionCollection()->addAction( QStringLiteral("file_new") ); action->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::Key_N ); action->setText( i18n( "&New" ) ); action->setIconText( i18nc( "Shorter Text for 'New File' shown in the toolbar", "New") ); connect( action, &QAction::triggered, this, &MainWindowPrivate::fileNew ); action->setToolTip( i18nc( "@info:tooltip", "New file" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Creates an empty file." ) ); action = actionCollection()->addAction( QStringLiteral("add_toolview") ); action->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); actionCollection()->setDefaultShortcut(action, Qt::CTRL + Qt::SHIFT + Qt::Key_V ); action->setText( i18n( "&Add Tool View..." ) ); connect( action, &QAction::triggered, this, &MainWindowPrivate::viewAddNewToolView ); action->setToolTip( i18nc( "@info:tooltip", "Add tool view" ) ); action->setWhatsThis( i18nc( "@info:whatsthis", "Adds a new tool view to this window." ) ); //Load themes actionCollection()->addAction(QStringLiteral("colorscheme_menu"), new ColorSchemeChooser(actionCollection())); } void MainWindowPrivate::toggleArea(bool b) { if (!b) return; auto* action = qobject_cast(sender()); if (!action) return; m_mainWindow->controller()->showArea(action->data().toString(), m_mainWindow); } KActionCollection * MainWindowPrivate::actionCollection() { return m_mainWindow->actionCollection(); } void MainWindowPrivate::registerStatus(QObject* status) { m_statusBar->registerStatus(status); } void MainWindowPrivate::showErrorMessage(const QString& message, int timeout) { m_statusBar->showErrorMessage(message, timeout); } void MainWindowPrivate::tabContextMenuRequested(Sublime::View* view, QMenu* menu) { m_tabView = view; QAction* action; action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")), i18n("Split View Top/Bottom")); connect(action, &QAction::triggered, this, &MainWindowPrivate::contextMenuSplitHorizontal); action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-split-left-right")), i18n("Split View Left/Right")); connect(action, &QAction::triggered, this, &MainWindowPrivate::contextMenuSplitVertical); menu->addSeparator(); action = menu->addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New File")); connect(action, &QAction::triggered, this, &MainWindowPrivate::contextMenuFileNew); if (view) { if (auto* doc = dynamic_cast(view->document())) { action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reload")); connect(action, &QAction::triggered, doc, &TextDocument::reload); action = menu->addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reload All")); connect(action, &QAction::triggered, this, &MainWindowPrivate::reloadAll); } } } void MainWindowPrivate::tabToolTipRequested(Sublime::View* view, Sublime::Container* container, int tab) { if (m_tabTooltip.second) { if (m_tabTooltip.first == view) { // tooltip already shown, don't do anything. prevents flicker when moving mouse over same tab return; } else { m_tabTooltip.second.data()->close(); } } auto* urlDoc = dynamic_cast(view->document()); if (urlDoc) { DUChainReadLocker lock; TopDUContext* top = DUChainUtils::standardContextForUrl(urlDoc->url()); if (top) { - if ( QWidget* navigationWidget = top->createNavigationWidget() ) { + if (auto* navigationWidget = top->createNavigationWidget()) { NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(m_mainWindow, QCursor::pos() + QPoint(20, 20), navigationWidget); tooltip->resize(navigationWidget->sizeHint() + QSize(10, 10)); tooltip->setHandleRect(container->tabRect(tab)); m_tabTooltip.first = view; m_tabTooltip.second = tooltip; ActiveToolTip::showToolTip(m_tabTooltip.second.data()); } } } } void MainWindowPrivate::dockBarContextMenuRequested(Qt::DockWidgetArea area, const QPoint& position) { QMenu menu(m_mainWindow); menu.addSection(QIcon::fromTheme(QStringLiteral("window-new")), i18n("Add Tool View")); QHash factories = Core::self()->uiControllerInternal()->factoryDocuments(); QHash actionToFactory; if ( !factories.isEmpty() ) { // sorted actions QMap actionMap; for (QHash::const_iterator it = factories.constBegin(); it != factories.constEnd(); ++it) { QAction* action = new QAction(it.value()->statusIcon(), it.value()->title(), &menu); action->setIcon(it.value()->statusIcon()); if (!it.key()->allowMultiple() && Core::self()->uiControllerInternal()->toolViewPresent(it.value(), m_mainWindow->area())) { action->setDisabled(true); } actionToFactory.insert(action, it.key()); actionMap[action->text()] = action; } menu.addActions(actionMap.values()); } auto* lockAction = new QAction(this); lockAction->setCheckable(true); lockAction->setText(i18n("Lock the Panel from Hiding")); KConfigGroup config = KSharedConfig::openConfig()->group("UI"); lockAction->setChecked(config.readEntry(QStringLiteral("Toolview Bar (%1) Is Locked").arg(area), false)); menu.addSeparator(); menu.addAction(lockAction); QAction* triggered = menu.exec(position); if ( !triggered ) { return; } if (triggered == lockAction) { KConfigGroup config = KSharedConfig::openConfig()->group("UI"); config.writeEntry(QStringLiteral("Toolview Bar (%1) Is Locked").arg(area), lockAction->isChecked()); return; } Core::self()->uiControllerInternal()->addToolViewToDockArea( actionToFactory[triggered], area ); } bool MainWindowPrivate::changingActiveView() const { return m_changingActiveView; } KTextEditorIntegration::MainWindow *MainWindowPrivate::kateWrapper() const { return m_kateWrapper; } } #include "mainwindow_actions.cpp" diff --git a/plugins/clang/duchain/clangducontext.cpp b/plugins/clang/duchain/clangducontext.cpp index 99048e5708..971eadee1d 100644 --- a/plugins/clang/duchain/clangducontext.cpp +++ b/plugins/clang/duchain/clangducontext.cpp @@ -1,60 +1,62 @@ /* * Copyright 2014 Kevin Funk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "clangducontext.h" #include "duchain/navigationwidget.h" #include "../util/clangdebug.h" #include #include using namespace KDevelop; -template<> -QWidget* ClangTopDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, - KDevelop::AbstractNavigationWidget::DisplayHints hints) const +template <> +KDevelop::AbstractNavigationWidget* +ClangTopDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, + KDevelop::AbstractNavigationWidget::DisplayHints hints) const { if (!decl) { const QUrl u = url().toUrl(); IncludeItem item; item.pathNumber = -1; item.name = u.fileName(); item.isDirectory = false; item.basePath = u.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); return new ClangNavigationWidget(item, TopDUContextPointer(topContext ? topContext : this->topContext()), hints); } return new ClangNavigationWidget(DeclarationPointer(decl), hints); } -template<> -QWidget* ClangNormalDUContext::createNavigationWidget(Declaration* decl, TopDUContext* /*topContext*/, - KDevelop::AbstractNavigationWidget::DisplayHints hints) const +template <> +KDevelop::AbstractNavigationWidget* +ClangNormalDUContext::createNavigationWidget(Declaration* decl, TopDUContext* /*topContext*/, + KDevelop::AbstractNavigationWidget::DisplayHints hints) const { if (!decl) { clangDebug() << "no declaration, not returning navigationwidget"; return nullptr; } return new ClangNavigationWidget(DeclarationPointer(decl), hints); } DUCHAIN_DEFINE_TYPE_WITH_DATA(ClangNormalDUContext, DUContextData) DUCHAIN_DEFINE_TYPE_WITH_DATA(ClangTopDUContext, TopDUContextData) diff --git a/plugins/clang/duchain/clangducontext.h b/plugins/clang/duchain/clangducontext.h index b4a1e9b00b..7eb270a791 100644 --- a/plugins/clang/duchain/clangducontext.h +++ b/plugins/clang/duchain/clangducontext.h @@ -1,57 +1,58 @@ /* * Copyright 2014 Kevin Funk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #ifndef CLANGDUCONTEXT_H #define CLANGDUCONTEXT_H #include #include #include template class ClangDUContext : public BaseContext { public: template explicit ClangDUContext(Data& data) : BaseContext(data) { } ///Parameters will be reached to the base-class template explicit ClangDUContext(Params... params) : BaseContext(params...) { static_cast(this)->d_func_dynamic()->setClassId(this); } - QWidget* createNavigationWidget(KDevelop::Declaration* decl = nullptr, - KDevelop::TopDUContext* topContext = nullptr, - KDevelop::AbstractNavigationWidget::DisplayHints hints = KDevelop::AbstractNavigationWidget::NoHints) const override; + KDevelop::AbstractNavigationWidget* + createNavigationWidget(KDevelop::Declaration* decl = nullptr, KDevelop::TopDUContext* topContext = nullptr, + KDevelop::AbstractNavigationWidget::DisplayHints hints + = KDevelop::AbstractNavigationWidget::NoHints) const override; enum { Identity = IdentityT }; }; using ClangTopDUContext = ClangDUContext; using ClangNormalDUContext = ClangDUContext; DUCHAIN_DECLARE_TYPE(ClangTopDUContext) DUCHAIN_DECLARE_TYPE(ClangNormalDUContext) #endif // CLANGDUCONTEXT_H diff --git a/plugins/classbrowser/classtree.cpp b/plugins/classbrowser/classtree.cpp index 5df629d38b..a0f84da245 100644 --- a/plugins/classbrowser/classtree.cpp +++ b/plugins/classbrowser/classtree.cpp @@ -1,160 +1,159 @@ /* * KDevelop Class viewer * * Copyright 2006 Adam Treat * Copyright (c) 2006-2007 Hamish Rodda * Copyright 2009 Lior Mualem * * 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 "classtree.h" #include #include #include #include #include "interfaces/contextmenuextension.h" #include "interfaces/icore.h" #include "interfaces/idocument.h" #include "interfaces/iplugincontroller.h" #include "language/interfaces/codecontext.h" #include "language/duchain/duchainbase.h" #include "language/duchain/duchain.h" #include "language/duchain/duchainlock.h" #include "language/duchain/declaration.h" #include "language/classmodel/classmodel.h" #include "classbrowserplugin.h" using namespace KDevelop; ClassTree::ClassTree(QWidget* parent, ClassBrowserPlugin* plugin) : QTreeView(parent) , m_plugin(plugin) , m_tooltip(nullptr) { header()->hide(); setIndentation(10); setUniformRowHeights(true); connect(this, &ClassTree::activated, this, &ClassTree::itemActivated); } ClassTree::~ClassTree() { } static bool _populatingClassBrowserContextMenu = false; bool ClassTree::populatingClassBrowserContextMenu() { return _populatingClassBrowserContextMenu; } void ClassTree::contextMenuEvent(QContextMenuEvent* e) { auto* menu = new QMenu(this); QModelIndex index = indexAt(e->pos()); if (index.isValid()) { Context* c; { DUChainReadLocker readLock(DUChain::lock()); if (auto* decl = dynamic_cast(model()->duObjectForIndex(index))) c = new DeclarationContext(decl); else { delete menu; return; } } _populatingClassBrowserContextMenu = true; QList extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions(c, menu); ContextMenuExtension::populateMenu(menu, extensions); _populatingClassBrowserContextMenu = false; } if (!menu->actions().isEmpty()) menu->exec(e->globalPos()); delete menu; } bool ClassTree::event(QEvent* event) { if (event->type() == QEvent::ToolTip) { // if we request a tooltip over a duobject item, show a tooltip for it auto* helpEvent = static_cast(event); const QModelIndex idxView = indexAt(helpEvent->pos()); DUChainReadLocker readLock(DUChain::lock()); if (auto* decl = dynamic_cast(model()->duObjectForIndex(idxView))) { if (m_tooltip) { m_tooltip->close(); } - QWidget* navigationWidget = decl->topContext()->createNavigationWidget(decl); - if (navigationWidget) { + if (auto* navigationWidget = decl->topContext()->createNavigationWidget(decl)) { m_tooltip = new KDevelop::NavigationToolTip(this, helpEvent->globalPos() + QPoint(40, 0), navigationWidget); m_tooltip->resize(navigationWidget->sizeHint() + QSize(10, 10)); ActiveToolTip::showToolTip(m_tooltip); return true; } } } return QAbstractItemView::event(event); } ClassModel* ClassTree::model() { return static_cast(QTreeView::model()); } void ClassTree::itemActivated(const QModelIndex& index) { DUChainReadLocker readLock(DUChain::lock()); DeclarationPointer decl = DeclarationPointer(dynamic_cast(model()->duObjectForIndex(index))); readLock.unlock(); // Delegate to plugin function m_plugin->showDefinition(decl); if (isExpanded(index)) collapse(index); else expand(index); } void ClassTree::highlightIdentifier(const KDevelop::IndexedQualifiedIdentifier& a_id) { QModelIndex index = model()->indexForIdentifier(a_id); if (!index.isValid()) return; // expand and select the item. selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); scrollTo(index, PositionAtCenter); horizontalScrollBar()->setValue(horizontalScrollBar()->minimum()); expand(index); } diff --git a/plugins/contextbrowser/contextbrowserview.cpp b/plugins/contextbrowser/contextbrowserview.cpp index daf3000400..bae2f7ed9f 100644 --- a/plugins/contextbrowser/contextbrowserview.cpp +++ b/plugins/contextbrowser/contextbrowserview.cpp @@ -1,409 +1,407 @@ /* * 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 "contextbrowser.h" #include "debug.h" #include #include #include #include "browsemanager.h" #include #include #include #include #include #include using namespace KDevelop; namespace { enum Direction { NextUse, PreviousUse }; void selectUse(ContextBrowserView* view, Direction direction) { auto abstractNaviWidget = dynamic_cast(view->navigationWidget()); if (!abstractNaviWidget) { return; } auto usesWidget = dynamic_cast(abstractNaviWidget->context()->widget()); if (!usesWidget) { return; } OneUseWidget* first = nullptr, * previous = nullptr, * current = nullptr; const auto& usesWidgetItems = usesWidget->items(); for (auto item : usesWidgetItems) { auto topContext = dynamic_cast(item); if (!topContext) { continue; } const auto& topContextItems = topContext->items(); for (auto item : topContextItems) { auto navigationList = dynamic_cast(item); if (!navigationList) { continue; } const auto& navigationListItems = navigationList->items(); for (auto item : navigationListItems) { auto use = dynamic_cast(item); if (!use) { continue; } if (!first) { first = use; } current = use; if (direction == PreviousUse && current->isHighlighted() && previous) { previous->setHighlighted(true); previous->activateLink(); current->setHighlighted(false); return; } if (direction == NextUse && previous && previous->isHighlighted()) { current->setHighlighted(true); current->activateLink(); previous->setHighlighted(false); return; } previous = current; } } } if (direction == NextUse && first) { first->setHighlighted(true); first->activateLink(); if (current && current->isHighlighted()) current->setHighlighted(false); return; } if (direction == PreviousUse && current) { current->setHighlighted(true); current->activateLink(); if (first && first->isHighlighted()) { first->setHighlighted(false); } } } } -QWidget* ContextBrowserView::createWidget(KDevelop::DUContext* context) +AbstractNavigationWidget* ContextBrowserView::createWidget(KDevelop::DUContext* context) { m_context = IndexedDUContext(context); if (m_context.data()) { return m_context.data()->createNavigationWidget(nullptr, nullptr, AbstractNavigationWidget::EmbeddableWidget); } return nullptr; } KDevelop::IndexedDeclaration ContextBrowserView::declaration() const { return m_declaration; } -QWidget* ContextBrowserView::createWidget(Declaration* decl, TopDUContext* topContext) +AbstractNavigationWidget* ContextBrowserView::createWidget(Declaration* decl, TopDUContext* topContext) { m_declaration = IndexedDeclaration(decl); return decl->context()->createNavigationWidget(decl, topContext, AbstractNavigationWidget::EmbeddableWidget); } void ContextBrowserView::resetWidget() { if (m_navigationWidget) { delete m_navigationWidget; m_navigationWidget = nullptr; } } void ContextBrowserView::declarationMenu() { DUChainReadLocker lock(DUChain::lock()); auto* 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(this); QList extensions = ICore::self()->pluginController()->queryPluginsForContextMenuExtensions(c, &menu); ContextMenuExtension::populateMenu(&menu, extensions); menu.exec(QCursor::pos()); } } } ContextBrowserView::ContextBrowserView(ContextBrowserPlugin* plugin, QWidget* parent) : QWidget(parent) , m_plugin(plugin) , m_navigationWidget(new QTextBrowser()) , m_autoLocked(false) { setWindowTitle(i18n("Code Browser")); setWindowIcon(QIcon::fromTheme(QStringLiteral("code-context"), windowIcon())); m_allowLockedUpdate = false; m_declarationMenuAction = new QAction(QIcon::fromTheme(QStringLiteral("code-class")), QString(), this); m_declarationMenuAction->setToolTip(i18n("Show declaration menu")); // expose the declaration menu via the context menu; allows hiding the toolbar to save some space // (this will not make it behave like a submenu though) m_declarationMenuAction->setText(i18n("Declaration Menu")); connect(m_declarationMenuAction, &QAction::triggered, this, &ContextBrowserView::declarationMenu); addAction(m_declarationMenuAction); m_lockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-unlocked")), i18n( "Lock Current View"), this); m_lockAction->setToolTip(i18n("Lock current view")); m_lockAction->setCheckedState(KGuiItem(i18n("Unlock Current View"), QIcon::fromTheme(QStringLiteral("object-locked")), i18n("Unlock current view"))); m_lockAction->setChecked(false); addAction(m_lockAction); m_layout = new QVBoxLayout; m_layout->setSpacing(0); m_layout->setMargin(0); 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);*/ 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) { auto* keyEvent = dynamic_cast(event); if (hasFocus() && keyEvent) { auto* 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_lockAction->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()) { //Update the navigation-widget Declaration* decl = m_navigationWidgetDeclaration.declaration(top); if (decl) setDeclaration(decl, top, true); } QWidget::showEvent(event); } bool ContextBrowserView::isLocked() const { bool isLocked; if (m_allowLockedUpdate) { isLocked = false; } else { isLocked = m_lockAction->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)").constData()) != -1) { connect(widget, SIGNAL(contextChanged(bool,bool)), this, SLOT(navigationContextChanged(bool,bool))); } } } void ContextBrowserView::navigationContextChanged(bool wasInitial, bool isInitial) { if (wasInitial && !isInitial && !m_lockAction->isChecked()) { m_autoLocked = true; m_lockAction->setChecked(true); } else if (!wasInitial && isInitial && m_autoLocked) { m_autoLocked = false; m_lockAction->setChecked(false); } else if (isInitial) { m_autoLocked = false; } } void ContextBrowserView::selectNextItem() { selectUse(this, NextUse); } void ContextBrowserView::selectPreviousItem() { selectUse(this, PreviousUse); } 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_lockAction->setChecked(false); } if (m_navigationWidgetDeclaration == decl->id() && !force) return; m_navigationWidgetDeclaration = decl->id(); if (!isLocked() && (isVisible() || force)) { // NO-OP if tool view is hidden, for performance reasons - QWidget* w = createWidget(decl, topContext); - updateMainWidget(w); + updateMainWidget(createWidget(decl, topContext)); } } KDevelop::IndexedDeclaration ContextBrowserView::lockedDeclaration() const { if (m_lockAction->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 tool view is hidden, for performance reasons - QWidget* w = createWidget(context); - updateMainWidget(w); + updateMainWidget(createWidget(context)); } } void ContextBrowserView::setSpecialNavigationWidget(QWidget* widget) { if (!isLocked() && isVisible()) { Q_ASSERT(widget); updateMainWidget(widget); } else if (widget) { widget->deleteLater(); } } diff --git a/plugins/contextbrowser/contextbrowserview.h b/plugins/contextbrowser/contextbrowserview.h index 425af86277..10a1f28e32 100644 --- a/plugins/contextbrowser/contextbrowserview.h +++ b/plugins/contextbrowser/contextbrowserview.h @@ -1,117 +1,117 @@ /* * 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. */ #ifndef KDEVPLATFORM_PLUGIN_CONTEXTBROWSERVIEW_H #define KDEVPLATFORM_PLUGIN_CONTEXTBROWSERVIEW_H #include #include #include #include class ContextBrowserPlugin; class QVBoxLayout; class QHBoxLayout; class QAction; class KToggleAction; namespace KDevelop { class IDocument; } class ContextBrowserView : public QWidget , public KDevelop::IToolViewActionListener { Q_OBJECT Q_INTERFACES(KDevelop::IToolViewActionListener) public: ContextBrowserView(ContextBrowserPlugin*, QWidget* parent); ~ContextBrowserView() override; //duchain must be locked void setContext(KDevelop::DUContext* context); void setDeclaration(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext, bool force = false); void setSpecialNavigationWidget(QWidget*); void updateMainWidget(QWidget*); /** Allows a single update of the view even if in locked state. * This is needed to browse while the locked button is checked. */ void allowLockedUpdate(); ///Returns the currently locked declaration, or invalid of none is locked atm. KDevelop::IndexedDeclaration lockedDeclaration() const; void setNavigationWidget(QWidget* widget); QWidget* navigationWidget() { return m_navigationWidget; } //duchain must be locked - QWidget* createWidget(KDevelop::DUContext* context); + KDevelop::AbstractNavigationWidget* createWidget(KDevelop::DUContext* context); //duchain must be locked - QWidget* createWidget(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext); + KDevelop::AbstractNavigationWidget* createWidget(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext); KDevelop::IndexedDeclaration declaration() const; ///Returns whether the view is currently locked bool isLocked() const; private Q_SLOTS: void declarationMenu(); void navigationContextChanged(bool wasInitial, bool isInitial); void selectNextItem() override; void selectPreviousItem() override; private: void showEvent(QShowEvent* event) override; bool event(QEvent* event) override; void focusInEvent(QFocusEvent* event) override; void focusOutEvent(QFocusEvent* event) override; void resetWidget(); private: KDevelop::IndexedDeclaration m_declaration; ContextBrowserPlugin* m_plugin; QVBoxLayout* m_layout; KToggleAction* m_lockAction; QAction* m_declarationMenuAction; QHBoxLayout* m_buttons; QPointer m_navigationWidget; KDevelop::DeclarationId m_navigationWidgetDeclaration; bool m_allowLockedUpdate; KDevelop::IndexedTopDUContext m_lastUsedTopContext; KDevelop::IndexedDUContext m_context; // Whether the lock-button was activated automatically due to user navigation bool m_autoLocked; }; #endif diff --git a/plugins/projectmanagerview/projectmodelitemdelegate.cpp b/plugins/projectmanagerview/projectmodelitemdelegate.cpp index 92299ede81..89c0cc8e0a 100644 --- a/plugins/projectmanagerview/projectmodelitemdelegate.cpp +++ b/plugins/projectmanagerview/projectmodelitemdelegate.cpp @@ -1,227 +1,226 @@ /* This file is part of KDevelop Copyright 2013 Aleix Pol This library 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 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 "projectmodelitemdelegate.h" #include "vcsoverlayproxymodel.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; ProjectModelItemDelegate::ProjectModelItemDelegate(QObject* parent) : QItemDelegate(parent) {} static QIcon::Mode IconMode( QStyle::State state ) { if (!(state & QStyle::State_Enabled)) { return QIcon::Disabled; } else if (state & QStyle::State_Selected) { return QIcon::Selected; } else { return QIcon::Normal; } } static QIcon::State IconState(QStyle::State state) { return (state & QStyle::State_Open) ? QIcon::On : QIcon::Off; } void ProjectModelItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opt, const QModelIndex& index) const { // Qt5.5 HiDPI Fix part (1/2) // This fix is based on how Qt5.5's QItemDelegate::paint implementation deals with the same issue // Unfortunately, there doesn't seem to be a clean way to use the base implementation // and have the added functionality this class provides QPixmap decoData; QRect decorationRect; QIcon icon; QIcon::Mode mode = QIcon::Mode::Disabled; QIcon::State state = QIcon::State::Off; { QVariant value; value = index.data(Qt::DecorationRole); if (value.isValid()) { decoData = decoration(opt, value); if (value.type() == QVariant::Icon) { icon = qvariant_cast(value); mode = IconMode(opt.state); state = IconState(opt.state); QSize size = icon.actualSize( opt.decorationSize, mode, state ); decorationRect = QRect(QPoint(0, 0), size); } else { decorationRect = QRect(QPoint(0, 0), decoData.size()); } } else { decorationRect = QRect(); } } QRect checkRect; //unused in practice QRect spaceLeft = opt.rect; spaceLeft.setLeft(decorationRect.right()); QString displayData = index.data(Qt::DisplayRole).toString(); QRect displayRect = textRectangle(painter, spaceLeft, opt.font, displayData); displayRect.setLeft(spaceLeft.left()); QRect branchNameRect(displayRect.topRight(), opt.rect.bottomRight()); doLayout(opt, &checkRect, &decorationRect, &displayRect, false); branchNameRect.setLeft(branchNameRect.left() + displayRect.left()); branchNameRect.setTop(displayRect.top()); drawStyledBackground(painter, opt); // drawCheck(painter, opt, checkRect, checkState); // Qt5.5 HiDPI Fix part (2/2) // use the QIcon from above if possible if (!icon.isNull()) { icon.paint(painter, decorationRect, opt.decorationAlignment, mode, state ); } else { drawDecoration(painter, opt, decorationRect, decoData); } drawDisplay(painter, opt, displayRect, displayData); /// FIXME: this can apparently trigger a nested eventloop, see /// https://bugs.kde.org/show_bug.cgi?id=355099 QString branchNameData = index.data(VcsOverlayProxyModel::VcsStatusRole).toString(); drawBranchName(painter, opt, branchNameRect, branchNameData); drawFocus(painter, opt, displayRect); } void ProjectModelItemDelegate::drawBranchName(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& branchName) const { QString text = option.fontMetrics.elidedText(branchName, Qt::ElideRight, rect.width()); bool selected = option.state & QStyle::State_Selected; QPalette::ColorGroup colorGroup = selected ? QPalette::Active : QPalette::Disabled; painter->save(); painter->setPen(option.palette.color(colorGroup, QPalette::Text)); painter->drawText(rect, text); painter->restore(); } void ProjectModelItemDelegate::drawStyledBackground(QPainter* painter, const QStyleOptionViewItem& option) const { QStyleOptionViewItem opt(option); QStyle *style = opt.widget->style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, opt.widget); } void ProjectModelItemDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& text) const { QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; if (option.state & QStyle::State_Editing) { painter->save(); painter->setPen(option.palette.color(cg, QPalette::Text)); painter->drawRect(rect.adjusted(0, 0, -1, -1)); painter->restore(); } if(text.isEmpty()) { return; } if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } QFontMetrics fm(painter->fontMetrics()); painter->drawText(rect, fm.elidedText(text, Qt::ElideRight, rect.width())); } bool ProjectModelItemDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) { if (!event || !view) { return false; } if (event->type() == QEvent::ToolTip) { auto* helpEvent = static_cast(event); // explicitly close current tooltip, as its autoclose margins overlap items if ((m_tooltippedIndex != index) && m_tooltip) { m_tooltip->close(); m_tooltip.clear(); } const ProjectBaseItem* it = index.data(ProjectModel::ProjectItemRole).value(); // show navigation tooltip for files if (it && it->file()) { if (!m_tooltip) { m_tooltippedIndex = index; KDevelop::DUChainReadLocker lock(KDevelop::DUChain::lock()); const TopDUContext* top = DUChainUtils::standardContextForUrl(it->file()->path().toUrl()); if (top) { - QWidget* navigationWidget = top->createNavigationWidget(); - if (navigationWidget) { + if (auto* navigationWidget = top->createNavigationWidget()) { // force possible existing normal tooltip for other list item to hide // Seems that is still only done with a small delay though, // but the API seems not to allow more control. QToolTip::hideText(); m_tooltip = new KDevelop::NavigationToolTip(view, helpEvent->globalPos() + QPoint(40, 0), navigationWidget); m_tooltip->resize(navigationWidget->sizeHint() + QSize(10, 10)); auto rect = view->visualRect(m_tooltippedIndex); rect.moveTopLeft(view->mapToGlobal(rect.topLeft())); m_tooltip->setHandleRect(rect); ActiveToolTip::showToolTip(m_tooltip); } } } // tooltip successfully handled by us? if (m_tooltip) { return true; } } } return QItemDelegate::helpEvent(event, view, option, index); } diff --git a/plugins/qmljs/duchain/qmljsducontext.cpp b/plugins/qmljs/duchain/qmljsducontext.cpp index 23c1db3dbf..8b9c25afb2 100644 --- a/plugins/qmljs/duchain/qmljsducontext.cpp +++ b/plugins/qmljs/duchain/qmljsducontext.cpp @@ -1,67 +1,69 @@ /* * This file is part of qmljs, the QML/JS language support plugin for KDevelop * Copyright (c) 2013 Sven Brauch * Copyright (c) 2014 Denis Steckelmacher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #include "qmljsducontext.h" #include "debug.h" #include #include #include #include #include #include "navigation/navigationwidget.h" using namespace KDevelop; namespace QmlJS { -template<> -QWidget* QmlJSTopDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, - AbstractNavigationWidget::DisplayHints hints) const +template <> +AbstractNavigationWidget* QmlJSTopDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, + AbstractNavigationWidget::DisplayHints hints) const { if (!decl) { const QUrl u = url().toUrl(); IncludeItem item; item.pathNumber = -1; item.name = u.fileName(); item.isDirectory = false; item.basePath = u.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); return new NavigationWidget(item, TopDUContextPointer(topContext ? topContext : this->topContext()), hints); } return new NavigationWidget(decl, topContext, hints); } -template<> -QWidget* QmlJSNormalDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, AbstractNavigationWidget::DisplayHints hints) const { +template <> +AbstractNavigationWidget* +QmlJSNormalDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, + AbstractNavigationWidget::DisplayHints hints) const +{ if (!decl) { qCDebug(KDEV_QMLJS_DUCHAIN) << "no declaration, not returning navigationwidget"; return nullptr; } return new NavigationWidget(decl, topContext, hints); } - } DUCHAIN_DEFINE_TYPE_WITH_DATA(QmlJS::QmlJSNormalDUContext, DUContextData) DUCHAIN_DEFINE_TYPE_WITH_DATA(QmlJS::QmlJSTopDUContext, TopDUContextData) diff --git a/plugins/qmljs/duchain/qmljsducontext.h b/plugins/qmljs/duchain/qmljsducontext.h index f7ce87a36a..a31c81c490 100644 --- a/plugins/qmljs/duchain/qmljsducontext.h +++ b/plugins/qmljs/duchain/qmljsducontext.h @@ -1,81 +1,79 @@ /* * This file is part of qmljs, the QML/JS language support plugin for KDevelop * Copyright (c) 2013 Sven Brauch * Copyright (c) 2014 Denis Steckelmacher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #ifndef __QMLJSDUCONTEXT_H__ #define __QMLJSDUCONTEXT_H__ #include #include -class QWidget; - namespace KDevelop { class Declaration; class TopDUContext; } namespace QmlJS { template class QmlJSDUContext : public BaseContext { public: template explicit QmlJSDUContext(Data& data) : BaseContext(data) { } ///Parameters will be reached to the base-class template QmlJSDUContext(const Param1& p1, const Param2& p2, bool isInstantiationContext) : BaseContext(p1, p2, isInstantiationContext) { static_cast(this)->d_func_dynamic()->setClassId(this); } ///Both parameters will be reached to the base-class. This fits TopDUContext. template QmlJSDUContext(const Param1& p1, const Param2& p2, const Param3& p3) : BaseContext(p1, p2, p3) { static_cast(this)->d_func_dynamic()->setClassId(this); } template QmlJSDUContext(const Param1& p1, const Param2& p2) : BaseContext(p1, p2) { static_cast(this)->d_func_dynamic()->setClassId(this); } - QWidget* createNavigationWidget(KDevelop::Declaration* decl, - KDevelop::TopDUContext* topContext, - KDevelop::AbstractNavigationWidget::DisplayHints hints) const override; + KDevelop::AbstractNavigationWidget* + createNavigationWidget(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext, + KDevelop::AbstractNavigationWidget::DisplayHints hints) const override; enum { Identity = IdentityT }; }; using QmlJSTopDUContext = QmlJSDUContext; using QmlJSNormalDUContext = QmlJSDUContext; } DUCHAIN_DECLARE_TYPE(QmlJS::QmlJSTopDUContext) DUCHAIN_DECLARE_TYPE(QmlJS::QmlJSNormalDUContext) #endif