diff --git a/kdevplatform/debugger/util/pathmappings.cpp b/kdevplatform/debugger/util/pathmappings.cpp index e02084b5f0..92d4bcc825 100644 --- a/kdevplatform/debugger/util/pathmappings.cpp +++ b/kdevplatform/debugger/util/pathmappings.cpp @@ -1,258 +1,260 @@ /* * This file is part of KDevelop * * Copyright 2009 Niko Sams * * 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) 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 "pathmappings.h" #include #include #include #include #include #include #include #include #include #include namespace { static QUrl rebaseMatchingUrl(const QUrl& toRebase, const KConfigGroup& config, const QString& baseEntry, const QString& rebaseEntry) { const QUrl::UrlFormattingOption matchOpts = QUrl::NormalizePathSegments; foreach (const QString &group, config.groupList()) { KConfigGroup pathCfg = config.group(group); const QString baseStr = pathCfg.readEntry(baseEntry, QUrl()).url(matchOpts); const QString searchStr = toRebase.url(matchOpts); if (searchStr.contains(baseStr)) { const QUrl rebase = pathCfg.readEntry(rebaseEntry, QUrl()); return rebase.resolved(QUrl(searchStr.mid(baseStr.length()))); } } //No mapping found return toRebase; } } namespace KDevelop { const QString PathMappings::pathMappingsEntry(QStringLiteral("Path Mappings")); const QString PathMappings::pathMappingRemoteEntry(QStringLiteral("Remote")); const QString PathMappings::pathMappingLocalEntry(QStringLiteral("Local")); QUrl PathMappings::convertToLocalUrl(const KConfigGroup& config, const QUrl& remoteUrl) { if (remoteUrl.isLocalFile() && QFile::exists(remoteUrl.toLocalFile())) { return remoteUrl; } KConfigGroup cfg = config.group(pathMappingsEntry); return rebaseMatchingUrl(remoteUrl, cfg, pathMappingRemoteEntry, pathMappingLocalEntry); } QUrl PathMappings::convertToRemoteUrl(const KConfigGroup& config, const QUrl& localUrl) { KConfigGroup cfg = config.group(pathMappingsEntry); return rebaseMatchingUrl(localUrl, cfg, pathMappingLocalEntry, pathMappingRemoteEntry); } class PathMappingModel : public QAbstractTableModel { Q_OBJECT public: int columnCount(const QModelIndex& parent = QModelIndex()) const override { if (parent.isValid()) return 0; return 2; } int rowCount(const QModelIndex& parent = QModelIndex()) const override { if (parent.isValid()) return 0; return m_paths.count() + 1; } QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) { return i18n("Remote Path"); } else if (section == 1) { return i18n("Local Path"); } } return QAbstractTableModel::headerData(section, orientation, role); } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override { if (!index.isValid()) return QVariant(); if (index.parent().isValid()) return QVariant(); if (index.column() > 1) return QVariant(); if (index.row() > m_paths.count()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.row() == m_paths.count()) return QString(); if (index.column() == 0) { return m_paths[index.row()].remote.toDisplayString(QUrl::PreferLocalFile); } else if (index.column() == 1) { return m_paths[index.row()].local.toDisplayString(QUrl::PreferLocalFile); } } return QVariant(); } Qt::ItemFlags flags(const QModelIndex& index) const override { if (index.parent().isValid()) return Qt::NoItemFlags; if (!index.isValid()) return Qt::NoItemFlags; return ( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ); } bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override { if (!index.isValid()) return false; if (index.parent().isValid()) return false; if (index.column() > 1) return false; if (index.row() > m_paths.count()) return false; if (role == Qt::EditRole) { if (index.row() == m_paths.count()) { beginInsertRows(QModelIndex(), index.row()+1, index.row()+1); m_paths.append(Path()); endInsertRows(); } if (index.column() == 0) { m_paths[index.row()].remote = QUrl::fromUserInput(value.toString()); } else if (index.column() == 1) { m_paths[index.row()].local = QUrl::fromLocalFile(value.toString()); } emit dataChanged(index, index); return true; } return false; } bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override { if (parent.isValid()) return false; if (row+count > m_paths.count()) return false; beginRemoveRows(parent, row, row+count-1); for (int i=0; i m_paths; }; PathMappingsWidget::PathMappingsWidget(QWidget* parent): QWidget(parent) { QVBoxLayout *verticalLayout = new QVBoxLayout(this); m_pathMappingTable = new QTableView(this); m_pathMappingTable->setSelectionBehavior(QAbstractItemView::SelectRows); m_pathMappingTable->horizontalHeader()->setDefaultSectionSize(150); m_pathMappingTable->horizontalHeader()->setStretchLastSection(true); verticalLayout->addWidget(m_pathMappingTable); m_pathMappingTable->setModel(new PathMappingModel()); connect(m_pathMappingTable->model(), &QAbstractItemModel::dataChanged, this, &PathMappingsWidget::changed); connect(m_pathMappingTable->model(), &QAbstractItemModel::rowsRemoved, this, &PathMappingsWidget::changed); connect(m_pathMappingTable->model(), &QAbstractItemModel::rowsInserted, this, &PathMappingsWidget::changed); QAction* deletePath = new QAction( QIcon::fromTheme(QStringLiteral("edit-delete")), i18n( "Delete" ), this ); connect(deletePath, &QAction::triggered, this, &PathMappingsWidget::deletePath); deletePath->setShortcut(Qt::Key_Delete); deletePath->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_pathMappingTable->addAction(deletePath); } void PathMappingsWidget::deletePath() { foreach (const QModelIndex &i, m_pathMappingTable->selectionModel()->selectedRows()) { m_pathMappingTable->model()->removeRow(i.row(), i.parent()); } } void PathMappingsWidget::loadFromConfiguration(const KConfigGroup& cfg) { static_cast(m_pathMappingTable->model())->loadFromConfiguration(cfg); } void PathMappingsWidget::saveToConfiguration(const KConfigGroup& cfg) const { static_cast(m_pathMappingTable->model())->saveToConfiguration(cfg); } } #include "pathmappings.moc" #include "moc_pathmappings.cpp" diff --git a/kdevplatform/language/codegen/sourcefiletemplate.cpp b/kdevplatform/language/codegen/sourcefiletemplate.cpp index 643c987c02..71453cdfd2 100644 --- a/kdevplatform/language/codegen/sourcefiletemplate.cpp +++ b/kdevplatform/language/codegen/sourcefiletemplate.cpp @@ -1,346 +1,348 @@ /* * This file is part of KDevelop * Copyright 2012 Miha Čančula * * 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 "sourcefiletemplate.h" #include "templaterenderer.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; typedef SourceFileTemplate::ConfigOption ConfigOption; class KDevelop::SourceFileTemplatePrivate { public: KArchive* archive; QString descriptionFileName; QStringList searchLocations; ConfigOption readEntry(const QDomElement& element, TemplateRenderer* renderer); }; ConfigOption SourceFileTemplatePrivate::readEntry(const QDomElement& element, TemplateRenderer* renderer) { ConfigOption entry; entry.name = element.attribute(QStringLiteral("name")); entry.type = element.attribute(QStringLiteral("type"), QStringLiteral("String")); bool isDefaultValueSet = false; for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { QString tag = e.tagName(); if (tag == QLatin1String("label")) { entry.label = e.text(); } else if (tag == QLatin1String("tooltip")) { entry.label = e.text(); } else if (tag == QLatin1String("whatsthis")) { entry.label = e.text(); } else if ( tag == QLatin1String("min") ) { entry.minValue = e.text(); } else if ( tag == QLatin1String("max") ) { entry.maxValue = e.text(); } else if ( tag == QLatin1String("default") ) { entry.value = renderer->render(e.text(), entry.name); isDefaultValueSet = true; } else if (tag == QLatin1String("choices")) { QStringList values; QDomNodeList choices = element.elementsByTagName(QStringLiteral("choice")); for (int j = 0; j < choices.size(); ++j) { QDomElement choiceElement = choices.at(j).toElement(); values << choiceElement.attribute(QStringLiteral("name")); } Q_ASSERT(!values.isEmpty()); if (values.isEmpty()) { qCWarning(LANGUAGE) << "Entry " << entry.name << "has an enum without any choices"; } entry.values = values; } } qCDebug(LANGUAGE) << "Read entry" << entry.name << "with default value" << entry.value; // preset value for enum if needed if (!entry.values.isEmpty()) { if (isDefaultValueSet) { const bool isSaneDefaultValue = entry.values.contains(entry.value.toString()); Q_ASSERT(isSaneDefaultValue); if (!isSaneDefaultValue) { qCWarning(LANGUAGE) << "Default value" << entry.value << "not in enum" << entry.values; entry.value = entry.values.at(0); } } else { entry.value = entry.values.at(0); } } return entry; } SourceFileTemplate::SourceFileTemplate (const QString& templateDescription) : d(new KDevelop::SourceFileTemplatePrivate) { d->archive = nullptr; setTemplateDescription(templateDescription); } SourceFileTemplate::SourceFileTemplate() : d(new KDevelop::SourceFileTemplatePrivate) { d->archive = nullptr; } SourceFileTemplate::SourceFileTemplate (const SourceFileTemplate& other) : d(new KDevelop::SourceFileTemplatePrivate) { d->archive = nullptr; *this = other; } SourceFileTemplate::~SourceFileTemplate() { delete d->archive; } SourceFileTemplate& SourceFileTemplate::operator=(const SourceFileTemplate& other) { if (other.d == d) { return *this; } delete d->archive; if (other.d->archive) { if (other.d->archive->fileName().endsWith(QLatin1String(".zip"))) { d->archive = new KZip(other.d->archive->fileName()); } else { d->archive = new KTar(other.d->archive->fileName()); } d->archive->open(QIODevice::ReadOnly); } else { d->archive = nullptr; } d->descriptionFileName = other.d->descriptionFileName; return *this; } void SourceFileTemplate::setTemplateDescription(const QString& templateDescription) { delete d->archive; d->descriptionFileName = templateDescription; QString archiveFileName; const QString templateBaseName = QFileInfo(templateDescription).baseName(); d->searchLocations.append(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("/kdevfiletemplates/templates/"), QStandardPaths::LocateDirectory)); foreach(const QString& dir, d->searchLocations) { foreach(const auto& entry, QDir(dir).entryInfoList(QDir::Files)) { if (entry.baseName() == templateBaseName) { archiveFileName = entry.absoluteFilePath(); qCDebug(LANGUAGE) << "Found template archive" << archiveFileName; break; } } } if (archiveFileName.isEmpty() || !QFileInfo::exists(archiveFileName)) { qCWarning(LANGUAGE) << "Could not find a template archive for description" << templateDescription << ", archive file" << archiveFileName; d->archive = nullptr; } else { QFileInfo info(archiveFileName); if (info.suffix() == QLatin1String("zip")) { d->archive = new KZip(archiveFileName); } else { d->archive = new KTar(archiveFileName); } d->archive->open(QIODevice::ReadOnly); } } bool SourceFileTemplate::isValid() const { return d->archive; } QString SourceFileTemplate::name() const { KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); return cg.readEntry("Name"); } QString SourceFileTemplate::type() const { KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); return cg.readEntry("Type", QString()); } QString SourceFileTemplate::languageName() const { KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); return cg.readEntry("Language", QString()); } QStringList SourceFileTemplate::category() const { KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); return cg.readEntry("Category", QStringList()); } QStringList SourceFileTemplate::defaultBaseClasses() const { KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); return cg.readEntry("BaseClasses", QStringList()); } const KArchiveDirectory* SourceFileTemplate::directory() const { Q_ASSERT(isValid()); return d->archive->directory(); } QVector SourceFileTemplate::outputFiles() const { QVector outputFiles; KConfig templateConfig(d->descriptionFileName); KConfigGroup group(&templateConfig, "General"); QStringList files = group.readEntry("Files", QStringList()); qCDebug(LANGUAGE) << "Files in template" << files; + + outputFiles.reserve(files.size()); foreach (const QString& fileGroup, files) { KConfigGroup cg(&templateConfig, fileGroup); OutputFile f; f.identifier = cg.name(); f.label = cg.readEntry("Name"); f.fileName = cg.readEntry("File"); f.outputName = cg.readEntry("OutputFile"); outputFiles << f; } return outputFiles; } bool SourceFileTemplate::hasCustomOptions() const { Q_ASSERT(isValid()); KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); bool hasOptions = d->archive->directory()->entries().contains(cg.readEntry("OptionsFile", "options.kcfg")); qCDebug(LANGUAGE) << cg.readEntry("OptionsFile", "options.kcfg") << hasOptions; return hasOptions; } QVector SourceFileTemplate::customOptions(TemplateRenderer* renderer) const { Q_ASSERT(isValid()); KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); const KArchiveEntry* entry = d->archive->directory()->entry(cg.readEntry("OptionsFile", "options.kcfg")); QVector optionGroups; if (!entry->isFile()) { return optionGroups; } const KArchiveFile* file = static_cast(entry); /* * Copied from kconfig_compiler.kcfg */ QDomDocument doc; QString errorMsg; int errorRow; int errorCol; if ( !doc.setContent( file->data(), &errorMsg, &errorRow, &errorCol ) ) { qCDebug(LANGUAGE) << "Unable to load document."; qCDebug(LANGUAGE) << "Parse error in line " << errorRow << ", col " << errorCol << ": " << errorMsg; return optionGroups; } QDomElement cfgElement = doc.documentElement(); if ( cfgElement.isNull() ) { qCDebug(LANGUAGE) << "No document in kcfg file"; return optionGroups; } QDomNodeList groups = cfgElement.elementsByTagName(QStringLiteral("group")); optionGroups.reserve(groups.size()); for (int i = 0; i < groups.size(); ++i) { QDomElement group = groups.at(i).toElement(); ConfigOptionGroup optionGroup; optionGroup.name = group.attribute(QStringLiteral("name")); QDomNodeList entries = group.elementsByTagName(QStringLiteral("entry")); optionGroup.options.reserve(entries.size()); for (int j = 0; j < entries.size(); ++j) { QDomElement entry = entries.at(j).toElement(); optionGroup.options << d->readEntry(entry, renderer); } optionGroups << optionGroup; } return optionGroups; } void SourceFileTemplate::addAdditionalSearchLocation(const QString& location) { if(!d->searchLocations.contains(location)) d->searchLocations.append(location); } diff --git a/kdevplatform/language/duchain/declaration.cpp b/kdevplatform/language/duchain/declaration.cpp index f061e3bac6..c04ebfaa81 100644 --- a/kdevplatform/language/duchain/declaration.cpp +++ b/kdevplatform/language/duchain/declaration.cpp @@ -1,770 +1,772 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007 2008 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 "declaration.h" #include "declarationdata.h" #include #include #include "topducontext.h" #include "topducontextdynamicdata.h" #include "use.h" #include "forwarddeclaration.h" #include "duchain.h" #include "duchainlock.h" #include "ducontextdata.h" #include "declarationid.h" #include "uses.h" #include #include "duchainregister.h" #include "persistentsymboltable.h" #include "types/identifiedtype.h" #include "types/structuretype.h" #include "functiondefinition.h" #include "codemodel.h" #include "specializationstore.h" #include "types/typeutils.h" #include "types/typealiastype.h" #include "classdeclaration.h" #include "serialization/stringrepository.h" #include "ducontextdynamicdata.h" namespace KDevelop { REGISTER_DUCHAIN_ITEM(Declaration); DeclarationData::DeclarationData() : m_isDefinition(false) , m_inSymbolTable(false) , m_isTypeAlias(false) , m_anonymousInContext(false) , m_isDeprecated(false) , m_alwaysForceDirect(false) , m_isAutoDeclaration(false) , m_isExplicitlyDeleted(false) { } ///@todo Use reference counting static Repositories::StringRepository& commentRepository() { static Repositories::StringRepository commentRepositoryObject(QStringLiteral("Comment Repository")); return commentRepositoryObject; } void initDeclarationRepositories() { commentRepository(); } Declaration::Kind Declaration::kind() const { DUCHAIN_D(Declaration); return d->m_kind; } void Declaration::setKind(Kind kind) { DUCHAIN_D_DYNAMIC(Declaration); d->m_kind = kind; updateCodeModel(); } bool Declaration::inDUChain() const { DUCHAIN_D(Declaration); if( d->m_anonymousInContext ) return false; if( !context() ) return false; TopDUContext* top = topContext(); return top && top->inDUChain(); } Declaration::Declaration( const RangeInRevision& range, DUContext* context ) : DUChainBase(*new DeclarationData, range) { d_func_dynamic()->setClassId(this); m_topContext = nullptr; m_context = nullptr; m_indexInTopContext = 0; if(context) setContext(context); } uint Declaration::ownIndex() const { ENSURE_CAN_READ return m_indexInTopContext; } Declaration::Declaration(const Declaration& rhs) : DUChainBase(*new DeclarationData( *rhs.d_func() )) { } Declaration::Declaration( DeclarationData & dd ) : DUChainBase(dd) { } Declaration::Declaration( DeclarationData & dd, const RangeInRevision& range ) : DUChainBase(dd, range) { } bool Declaration::persistentlyDestroying() const { TopDUContext* topContext = this->topContext(); return !topContext->deleting() || !topContext->isOnDisk(); } Declaration::~Declaration() { uint oldOwnIndex = m_indexInTopContext; TopDUContext* topContext = this->topContext(); //Only perform the actions when the top-context isn't being deleted, or when it hasn't been stored to disk if(persistentlyDestroying()) { DUCHAIN_D_DYNAMIC(Declaration); // Inserted by the builder after construction has finished. if( d->m_internalContext.context() ) d->m_internalContext.context()->setOwner(nullptr); setInSymbolTable(false); } // If the parent-context already has dynamic data, like for example any temporary context, // always delete the declaration, to not create crashes within more complex code like C++ template stuff. if (context() && !d_func()->m_anonymousInContext) { if(!topContext->deleting() || !topContext->isOnDisk() || context()->d_func()->isDynamic()) context()->m_dynamicData->removeDeclaration(this); } clearOwnIndex(); if(!topContext->deleting() || !topContext->isOnDisk()) { setContext(nullptr); setAbstractType(AbstractType::Ptr()); } Q_ASSERT(d_func()->isDynamic() == (!topContext->deleting() || !topContext->isOnDisk() || topContext->m_dynamicData->isTemporaryDeclarationIndex(oldOwnIndex))); Q_UNUSED(oldOwnIndex); } QByteArray Declaration::comment() const { DUCHAIN_D(Declaration); if(!d->m_comment) return nullptr; else return Repositories::arrayFromItem(commentRepository().itemFromIndex(d->m_comment)); } void Declaration::setComment(const QByteArray& str) { DUCHAIN_D_DYNAMIC(Declaration); if(str.isEmpty()) d->m_comment = 0; else d->m_comment = commentRepository().index(Repositories::StringRepositoryItemRequest(str.constData(), IndexedString::hashString(str.constData(), str.length()), str.length())); } void Declaration::setComment(const QString& str) { setComment(str.toUtf8()); } Identifier Declaration::identifier( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_identifier.identifier(); } const IndexedIdentifier& Declaration::indexedIdentifier( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_identifier; } void Declaration::rebuildDynamicData(DUContext* parent, uint ownIndex) { DUChainBase::rebuildDynamicData(parent, ownIndex); m_context = parent; m_topContext = parent->topContext(); m_indexInTopContext = ownIndex; } void Declaration::setIdentifier(const Identifier& identifier) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); bool wasInSymbolTable = d->m_inSymbolTable; setInSymbolTable(false); d->m_identifier = identifier; setInSymbolTable(wasInSymbolTable); } IndexedType Declaration::indexedType() const { return d_func()->m_type; } AbstractType::Ptr Declaration::abstractType( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_type.abstractType(); } void Declaration::setAbstractType(AbstractType::Ptr type) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); d->m_type = type ? type->indexed() : IndexedType(); updateCodeModel(); } Declaration* Declaration::specialize(const IndexedInstantiationInformation& /*specialization*/, const TopDUContext* topContext, int /*upDistance*/) { if(!topContext) return nullptr; return this; } QualifiedIdentifier Declaration::qualifiedIdentifier() const { ENSURE_CAN_READ QualifiedIdentifier ret; DUContext* ctx = m_context; if(ctx) ret = ctx->scopeIdentifier(true); ret.push(d_func()->m_identifier); return ret; } DUContext * Declaration::context() const { //ENSURE_CAN_READ Commented out for performance reasons return m_context; } bool Declaration::isAnonymous() const { return d_func()->m_anonymousInContext; } void Declaration::setContext(DUContext* context, bool anonymous) { Q_ASSERT(!context || context->topContext()); DUCHAIN_D_DYNAMIC(Declaration); if (context == m_context && anonymous == d->m_anonymousInContext) { // skip costly operations below when the same context is set // this happens often when updating a TopDUContext from the cache return; } setInSymbolTable(false); //We don't need to clear, because it's not allowed to move from one top-context into another // clearOwnIndex(); if (m_context && context) { Q_ASSERT(m_context->topContext() == context->topContext()); } if (m_context) { if( !d->m_anonymousInContext ) { m_context->m_dynamicData->removeDeclaration(this); } } if(context) m_topContext = context->topContext(); else m_topContext = nullptr; d->m_anonymousInContext = anonymous; m_context = context; if (context) { if(!m_indexInTopContext) allocateOwnIndex(); if(!d->m_anonymousInContext) { context->m_dynamicData->addDeclaration(this); } if(context->inSymbolTable() && !anonymous) setInSymbolTable(true); } } void Declaration::clearOwnIndex() { if(!m_indexInTopContext) return; if(!context() || (!d_func()->m_anonymousInContext && !context()->isAnonymous())) { ENSURE_CAN_WRITE } if(m_indexInTopContext) { Q_ASSERT(m_topContext); m_topContext->m_dynamicData->clearDeclarationIndex(this); } m_indexInTopContext = 0; } void Declaration::allocateOwnIndex() { ///@todo Fix multithreading stuff with template instantiation, preferably using some internal mutexes // if(context() && (!context()->isAnonymous() && !d_func()->m_anonymousInContext)) { // ENSURE_CAN_WRITE // } Q_ASSERT(m_topContext); m_indexInTopContext = m_topContext->m_dynamicData->allocateDeclarationIndex(this, d_func()->m_anonymousInContext || !context() || context()->isAnonymous()); Q_ASSERT(m_indexInTopContext); if(!m_topContext->m_dynamicData->getDeclarationForIndex(m_indexInTopContext)) qFatal("Could not re-retrieve declaration\nindex: %d", m_indexInTopContext); } const Declaration* Declaration::logicalDeclaration(const TopDUContext* topContext) const { ENSURE_CAN_READ if(isForwardDeclaration()) { const auto dec = static_cast(this); Declaration* ret = dec->resolve(topContext); if(ret) return ret; } return this; } Declaration* Declaration::logicalDeclaration(const TopDUContext* topContext) { ENSURE_CAN_READ if(isForwardDeclaration()) { const auto dec = static_cast(this); Declaration* ret = dec->resolve(topContext); if(ret) return ret; } return this; } DUContext * Declaration::logicalInternalContext(const TopDUContext* topContext) const { ENSURE_CAN_READ if(!isDefinition()) { Declaration* def = FunctionDefinition::definition(this); if( def ) return def->internalContext(); } if( d_func()->m_isTypeAlias ) { ///If this is a type-alias, return the internal context of the actual type. TypeAliasType::Ptr t = type(); if(t) { AbstractType::Ptr target = t->type(); IdentifiedType* idType = dynamic_cast(target.data()); if( idType ) { Declaration* decl = idType->declaration(topContext); if(decl && decl != this) { return decl->logicalInternalContext( topContext ); } } } } return internalContext(); } DUContext * Declaration::internalContext() const { // ENSURE_CAN_READ return d_func()->m_internalContext.context(); } void Declaration::setInternalContext(DUContext* context) { if(this->context()) { ENSURE_CAN_WRITE } DUCHAIN_D_DYNAMIC(Declaration); if( context == d->m_internalContext.context() ) return; if(!m_topContext) { //Take the top-context from the other side. We need to allocate an index, so we can safely call setOwner(..) m_topContext = context->topContext(); allocateOwnIndex(); } DUContext* oldInternalContext = d->m_internalContext.context(); d->m_internalContext = context; //Q_ASSERT( !oldInternalContext || oldInternalContext->owner() == this ); if( oldInternalContext && oldInternalContext->owner() == this ) oldInternalContext->setOwner(nullptr); if( context ) context->setOwner(this); } bool Declaration::operator ==(const Declaration & other) const { ENSURE_CAN_READ return this == &other; } QString Declaration::toString() const { return QStringLiteral("%3 %4").arg(abstractType() ? abstractType()->toString() : QStringLiteral(""), identifier().toString()); } bool Declaration::isDefinition() const { ENSURE_CAN_READ DUCHAIN_D(Declaration); return d->m_isDefinition; } void Declaration::setDeclarationIsDefinition(bool dd) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); d->m_isDefinition = dd; // if (d->m_isDefinition && definition()) { // setDefinition(0); // } } bool Declaration::isAutoDeclaration() const { return d_func()->m_isAutoDeclaration; } void Declaration::setAutoDeclaration(bool _auto) { d_func_dynamic()->m_isAutoDeclaration = _auto; } bool Declaration::isDeprecated() const { return d_func()->m_isDeprecated; } void Declaration::setDeprecated(bool deprecated) { d_func_dynamic()->m_isDeprecated = deprecated; } bool Declaration::alwaysForceDirect() const { return d_func()->m_alwaysForceDirect; } void Declaration::setAlwaysForceDirect(bool direct) { d_func_dynamic()->m_alwaysForceDirect = direct; } bool Declaration::isExplicitlyDeleted() const { return d_func()->m_isExplicitlyDeleted; } void Declaration::setExplicitlyDeleted(bool deleted) { d_func_dynamic()->m_isExplicitlyDeleted = deleted; } ///@todo see whether it would be useful to create an own TypeAliasDeclaration sub-class for this bool Declaration::isTypeAlias() const { DUCHAIN_D(Declaration); return d->m_isTypeAlias; } void Declaration::setIsTypeAlias(bool isTypeAlias) { DUCHAIN_D_DYNAMIC(Declaration); d->m_isTypeAlias = isTypeAlias; } IndexedInstantiationInformation Declaration::specialization() const { return IndexedInstantiationInformation(); } void Declaration::activateSpecialization() { if(specialization().index()) { DeclarationId baseId(id()); baseId.setSpecialization(IndexedInstantiationInformation()); SpecializationStore::self().set(baseId, specialization()); } } DeclarationId Declaration::id(bool forceDirect) const { ENSURE_CAN_READ if(inSymbolTable() && !forceDirect && !alwaysForceDirect()) return DeclarationId(qualifiedIdentifier(), additionalIdentity(), specialization()); else return DeclarationId(IndexedDeclaration(const_cast(this)), specialization()); } bool Declaration::inSymbolTable() const { DUCHAIN_D(Declaration); return d->m_inSymbolTable; } CodeModelItem::Kind kindForDeclaration(Declaration* decl) { CodeModelItem::Kind kind = CodeModelItem::Unknown; if(decl->kind() == Declaration::Namespace) return CodeModelItem::Namespace; if(decl->isFunctionDeclaration()) { kind = CodeModelItem::Function; } if(decl->kind() == Declaration::Type && (decl->type() || dynamic_cast(decl))) kind = CodeModelItem::Class; if(kind == CodeModelItem::Unknown && decl->kind() == Declaration::Instance) kind = CodeModelItem::Variable; if(decl->isForwardDeclaration()) kind = (CodeModelItem::Kind)(kind | CodeModelItem::ForwardDeclaration); if ( decl->context() && decl->context()->type() == DUContext::Class ) kind = (CodeModelItem::Kind)(kind | CodeModelItem::ClassMember); return kind; } void Declaration::updateCodeModel() { DUCHAIN_D(Declaration); if(!d->m_identifier.isEmpty() && d->m_inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); CodeModel::self().updateItem(url(), id, kindForDeclaration(this)); } } void Declaration::setInSymbolTable(bool inSymbolTable) { DUCHAIN_D_DYNAMIC(Declaration); if(!d->m_identifier.isEmpty()) { if(!d->m_inSymbolTable && inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); PersistentSymbolTable::self().addDeclaration(id, this); CodeModel::self().addItem(url(), id, kindForDeclaration(this)); } else if(d->m_inSymbolTable && !inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); PersistentSymbolTable::self().removeDeclaration(id, this); CodeModel::self().removeItem(url(), id); } } d->m_inSymbolTable = inSymbolTable; } TopDUContext * Declaration::topContext() const { return m_topContext; } Declaration* Declaration::clonePrivate() const { return new Declaration(*this); } Declaration* Declaration::clone() const { Declaration* ret = clonePrivate(); ret->d_func_dynamic()->m_inSymbolTable = false; return ret; } bool Declaration::isForwardDeclaration() const { return false; } bool Declaration::isFunctionDeclaration() const { return false; } uint Declaration::additionalIdentity() const { return 0; } bool Declaration::equalQualifiedIdentifier(const Declaration* rhs) const { ENSURE_CAN_READ DUCHAIN_D(Declaration); if(d->m_identifier != rhs->d_func()->m_identifier) return false; return m_context->equalScopeIdentifier(m_context); } QMap > Declaration::uses() const { ENSURE_CAN_READ QMap > tempUses; //First, search for uses within the own context { QMap& ranges(tempUses[topContext()->url()]); foreach(const RangeInRevision range, allUses(topContext(), const_cast(this))) ranges[range] = true; } DeclarationId _id = id(); KDevVarLengthArray useContexts = DUChain::uses()->uses(_id); if (!_id.isDirect()) { // also check uses based on direct IDs KDevVarLengthArray directUseContexts = DUChain::uses()->uses(id(true)); useContexts.append(directUseContexts.data(), directUseContexts.size()); } foreach (const IndexedTopDUContext indexedContext, useContexts) { TopDUContext* context = indexedContext.data(); if(context) { QMap& ranges(tempUses[context->url()]); foreach(const RangeInRevision range, allUses(context, const_cast(this))) ranges[range] = true; } } QMap> ret; for(QMap >::const_iterator it = tempUses.constBegin(); it != tempUses.constEnd(); ++it) { if(!(*it).isEmpty()) { auto& list = ret[it.key()]; + list.reserve((*it).size()); for(QMap::const_iterator it2 = (*it).constBegin(); it2 != (*it).constEnd(); ++it2) list << it2.key(); } } return ret; } bool hasDeclarationUse(DUContext* context, int declIdx) { bool ret=false; int usescount=context->usesCount(); const Use* uses=context->uses(); for(int i=0; !ret && ichildContexts()) { ret = ret || hasDeclarationUse(child, declIdx); if(ret) break; } return ret; } bool Declaration::hasUses() const { ENSURE_CAN_READ int idx = topContext()->indexForUsedDeclaration(const_cast(this), false); bool ret = idx != std::numeric_limits::max() && (idx>=0 || hasDeclarationUse(topContext(), idx)); //hasLocalUses DeclarationId myId = id(); if (!ret && DUChain::uses()->hasUses(myId)) { ret = true; } if (!ret && !myId.isDirect() && DUChain::uses()->hasUses(id(true))) { ret = true; } return ret; } QMap> Declaration::usesCurrentRevision() const { ENSURE_CAN_READ QMap > tempUses; //First, search for uses within the own context { QMap& ranges(tempUses[topContext()->url()]); foreach(const RangeInRevision range, allUses(topContext(), const_cast(this))) { ranges[topContext()->transformFromLocalRevision(range)] = true; } } DeclarationId _id = id(); KDevVarLengthArray useContexts = DUChain::uses()->uses(_id); if (!_id.isDirect()) { // also check uses based on direct IDs KDevVarLengthArray directUseContexts = DUChain::uses()->uses(id(true)); useContexts.append(directUseContexts.data(), directUseContexts.size()); } foreach (const IndexedTopDUContext indexedContext, useContexts) { TopDUContext* context = indexedContext.data(); if(context) { QMap& ranges(tempUses[context->url()]); foreach(const RangeInRevision range, allUses(context, const_cast(this))) ranges[context->transformFromLocalRevision(range)] = true; } } QMap> ret; for(QMap >::const_iterator it = tempUses.constBegin(); it != tempUses.constEnd(); ++it) { if(!(*it).isEmpty()) { auto& list = ret[it.key()]; + list.reserve((*it).size()); for(QMap::const_iterator it2 = (*it).constBegin(); it2 != (*it).constEnd(); ++it2) list << it2.key(); } } return ret; } } diff --git a/kdevplatform/language/highlighting/colorcache.cpp b/kdevplatform/language/highlighting/colorcache.cpp index bed78f2ce4..655575a2d0 100644 --- a/kdevplatform/language/highlighting/colorcache.cpp +++ b/kdevplatform/language/highlighting/colorcache.cpp @@ -1,329 +1,332 @@ /* * This file is part of KDevelop * * Copyright 2009 Milian Wolff * * 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 "colorcache.h" #include "configurablecolors.h" #include "codehighlighting.h" #include #include "../../interfaces/icore.h" #include "../../interfaces/ilanguagecontroller.h" #include "../../interfaces/icompletionsettings.h" #include "../../interfaces/idocument.h" #include "../../interfaces/idocumentcontroller.h" #include "../interfaces/ilanguagesupport.h" #include "../duchain/duchain.h" #include "../duchain/duchainlock.h" #include #include "widgetcolorizer.h" #include #include #include #define ifDebug(x) namespace KDevelop { ColorCache* ColorCache::m_self = nullptr; ColorCache::ColorCache(QObject* parent) : QObject(parent), m_defaultColors(nullptr), m_validColorCount(0), m_colorOffset(0), m_localColorRatio(0), m_globalColorRatio(0), m_boldDeclarations(true) { Q_ASSERT(m_self == nullptr); updateColorsFromScheme(); // default / fallback updateColorsFromSettings(); connect(ICore::self()->languageController()->completionSettings(), &ICompletionSettings::settingsChanged, this, &ColorCache::updateColorsFromSettings, Qt::QueuedConnection); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &ColorCache::slotDocumentActivated); bool hadDoc = tryActiveDocument(); updateInternal(); m_self = this; if (!hadDoc) { // try to update later on again QMetaObject::invokeMethod(this, "tryActiveDocument", Qt::QueuedConnection); } } bool ColorCache::tryActiveDocument() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if ( view ) { updateColorsFromView(view); return true; } return false; } ColorCache::~ColorCache() { m_self = nullptr; delete m_defaultColors; m_defaultColors = nullptr; } ColorCache* ColorCache::self() { if (!m_self) { m_self = new ColorCache; } return m_self; } void ColorCache::generateColors() { if ( m_defaultColors ) { delete m_defaultColors; } m_defaultColors = new CodeHighlightingColors(this); // Primary colors taken from: http://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12 const QColor colors[] = { {"#b15928"}, {"#ff7f00"}, {"#b2df8a"}, {"#33a02c"}, {"#a6cee3"}, {"#1f78b4"}, {"#6a3d9a"}, {"#cab2d6"}, {"#e31a1c"}, {"#fb9a99"} }; + const int colorCount = std::extent::value; // Supplementary colors generated by: http://tools.medialab.sciences-po.fr/iwanthue/ const QColor supplementaryColors[] = { {"#D33B67"}, {"#5EC764"}, {"#6CC82D"}, {"#995729"}, {"#FB4D84"}, {"#4B8828"}, {"#D847D0"}, {"#B56AC5"}, {"#E96F0C"}, {"#DC7161"}, {"#4D7279"}, {"#01AAF1"}, {"#D2A237"}, {"#F08CA5"}, {"#C83E93"}, {"#5D7DF7"}, {"#EFBB51"}, {"#108BBB"}, {"#5C84B8"}, {"#02F8BC"}, {"#A5A9F7"}, {"#F28E64"}, {"#A461E6"}, {"#6372D3"} }; + const int supplementaryColorCount = std::extent::value; m_colors.clear(); + m_colors.reserve(colorCount + supplementaryColorCount); for(const auto& color: colors){ m_colors.append(blendLocalColor(color)); } m_primaryColorCount = m_colors.count(); for(const auto& color: supplementaryColors){ m_colors.append(blendLocalColor(color)); } m_validColorCount = m_colors.count(); } void ColorCache::slotDocumentActivated() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); ifDebug(qCDebug(LANGUAGE) << "doc activated:" << doc;) if ( view ) { updateColorsFromView(view); } } void ColorCache::slotViewSettingsChanged() { KTextEditor::View* view = qobject_cast(sender()); Q_ASSERT(view); ifDebug(qCDebug(LANGUAGE) << "settings changed" << view;) updateColorsFromView(view); } void ColorCache::updateColorsFromView(KTextEditor::View* view) { if ( !view ) { // yeah, the HighlightInterface methods returning an Attribute // require a View... kill me for that mess return; } QColor foreground(QColor::Invalid); QColor background(QColor::Invalid); KTextEditor::Attribute::Ptr style = view->defaultStyleAttribute(KTextEditor::dsNormal); foreground = style->foreground().color(); if (style->hasProperty(QTextFormat::BackgroundBrush)) { background = style->background().color(); } // FIXME: this is in kateview // qCDebug(LANGUAGE) << "got foreground:" << foreground.name() << "old is:" << m_foregroundColor.name(); //NOTE: this slot is defined in KatePart > 4.4, see ApiDocs of the ConfigInterface // the signal is not defined in ConfigInterface, but according to the docs it should be // can't use new signal slot syntax here, since ConfigInterface is not a QObject if ( KTextEditor::View* view = m_view.data() ) { Q_ASSERT(qobject_cast(view)); // we only listen to a single view, i.e. the active one disconnect(view, SIGNAL(configChanged()), this, SLOT(slotViewSettingsChanged())); } Q_ASSERT(qobject_cast(view)); connect(view, SIGNAL(configChanged()), this, SLOT(slotViewSettingsChanged())); m_view = view; if ( !foreground.isValid() ) { // fallback to colorscheme variant ifDebug(qCDebug(LANGUAGE) << "updating from scheme";) updateColorsFromScheme(); } else if ( m_foregroundColor != foreground || m_backgroundColor != background ) { m_foregroundColor = foreground; m_backgroundColor = background; ifDebug(qCDebug(LANGUAGE) << "updating from document";) update(); } } void ColorCache::updateColorsFromScheme() { KColorScheme scheme(QPalette::Normal, KColorScheme::View); QColor foreground = scheme.foreground(KColorScheme::NormalText).color(); QColor background = scheme.background(KColorScheme::NormalBackground).color(); if ( foreground != m_foregroundColor || background != m_backgroundColor ) { m_foregroundColor = foreground; m_backgroundColor = background; update(); } } void ColorCache::updateColorsFromSettings() { int localRatio = ICore::self()->languageController()->completionSettings()->localColorizationLevel(); int globalRatio = ICore::self()->languageController()->completionSettings()->globalColorizationLevel(); bool boldDeclartions = ICore::self()->languageController()->completionSettings()->boldDeclarations(); if ( localRatio != m_localColorRatio || globalRatio != m_globalColorRatio ) { m_localColorRatio = localRatio; m_globalColorRatio = globalRatio; update(); } if (boldDeclartions != m_boldDeclarations) { m_boldDeclarations = boldDeclartions; update(); } } void ColorCache::update() { if ( !m_self ) { ifDebug(qCDebug(LANGUAGE) << "not updating - still initializating";) // don't update on startup, updateInternal is called directly there return; } QMetaObject::invokeMethod(this, "updateInternal", Qt::QueuedConnection); } void ColorCache::updateInternal() { ifDebug(qCDebug(LANGUAGE) << "update internal" << m_self;) generateColors(); if ( !m_self ) { // don't do anything else fancy on startup return; } emit colorsGotChanged(); // rehighlight open documents if (!ICore::self() || ICore::self()->shuttingDown()) { return; } foreach (IDocument* doc, ICore::self()->documentController()->openDocuments()) { foreach (const auto lang, ICore::self()->languageController()->languagesForUrl(doc->url())) { ReferencedTopDUContext top; { DUChainReadLocker lock; top = lang->standardContext(doc->url()); } if(top) { if ( ICodeHighlighting* highlighting = lang->codeHighlighting() ) { highlighting->highlightDUChain(top); } } } } } QColor ColorCache::blend(QColor color, uchar ratio) const { Q_ASSERT(m_backgroundColor.isValid()); Q_ASSERT(m_foregroundColor.isValid()); return WidgetColorizer::blendForeground(color, float(ratio) / float(0xff), m_foregroundColor, m_backgroundColor); } QColor ColorCache::blendBackground(QColor color, uchar ratio) const { return WidgetColorizer::blendBackground(color, float(ratio) / float(0xff), m_foregroundColor, m_backgroundColor); } QColor ColorCache::blendGlobalColor(QColor color) const { return blend(color, m_globalColorRatio); } QColor ColorCache::blendLocalColor(QColor color) const { return blend(color, m_localColorRatio); } CodeHighlightingColors* ColorCache::defaultColors() const { Q_ASSERT(m_defaultColors); return m_defaultColors; } QColor ColorCache::generatedColor(uint num) const { return num > (uint)m_colors.size() ? foregroundColor() : m_colors[num]; } uint ColorCache::validColorCount() const { return m_validColorCount; } uint ColorCache::primaryColorCount() const { return m_primaryColorCount; } QColor ColorCache::foregroundColor() const { return m_foregroundColor; } } // kate: space-indent on; indent-width 2; remove-trailing-spaces all; show-tabs on; tab-indents on; tab-width 2; diff --git a/plugins/debuggercommon/miframestackmodel.cpp b/plugins/debuggercommon/miframestackmodel.cpp index 990f65e52a..78ea0f5740 100644 --- a/plugins/debuggercommon/miframestackmodel.cpp +++ b/plugins/debuggercommon/miframestackmodel.cpp @@ -1,155 +1,157 @@ /* * Implementation of thread and frame model that are common to debuggers using MI. * * Copyright 2009 Vladimir Prus * * 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) 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 "miframestackmodel.h" #include "midebugsession.h" #include "mi/micommand.h" #include #include using namespace KDevelop; using namespace KDevMI; using namespace KDevMI::MI; QString getFunctionOrAddress(const Value &frame) { if (frame.hasField(QStringLiteral("func"))) return frame[QStringLiteral("func")].literal(); else return frame[QStringLiteral("addr")].literal(); } QPair getSource(const Value &frame) { QPair ret(QString(), -1); if (frame.hasField(QStringLiteral("fullname"))) ret=qMakePair(frame[QStringLiteral("fullname")].literal(), frame[QStringLiteral("line")].toInt()-1); else if (frame.hasField(QStringLiteral("file"))) ret=qMakePair(frame[QStringLiteral("file")].literal(), frame[QStringLiteral("line")].toInt()-1); else if (frame.hasField(QStringLiteral("from"))) ret.first=frame[QStringLiteral("from")].literal(); return ret; } MIFrameStackModel::MIFrameStackModel(MIDebugSession * session) : FrameStackModel(session) { } MIDebugSession * MIFrameStackModel::session() { return static_cast(FrameStackModel::session()); } void MIFrameStackModel::fetchThreads() { session()->addCommand(ThreadInfo, QLatin1String(""), this, &MIFrameStackModel::handleThreadInfo); } void MIFrameStackModel::handleThreadInfo(const ResultRecord& r) { const Value& threads = r[QStringLiteral("threads")]; QVector threadsList; + threadsList.reserve(threads.size()); for (int i = 0; i!= threads.size(); ++i) { const auto &threadMI = threads[i]; FrameStackModel::ThreadItem threadItem; threadItem.nr = threadMI[QStringLiteral("id")].toInt(); if (threadMI[QStringLiteral("state")].literal() == QLatin1String("stopped")) { threadItem.name = getFunctionOrAddress(threadMI[QStringLiteral("frame")]); } else { i18n("(running)"); } threadsList << threadItem; } // Sort the list by id, some old version of GDB // reports them in backward order. We want UI to // show thread IDs in the natural order. std::sort(threadsList.begin(), threadsList.end(), [](const FrameStackModel::ThreadItem &a, const FrameStackModel::ThreadItem &b){ return a.nr < b.nr; }); setThreads(threadsList); if (r.hasField(QStringLiteral("current-thread-id"))) { int currentThreadId = r[QStringLiteral("current-thread-id")].toInt(); setCurrentThread(currentThreadId); if (session()->hasCrashed()) { setCrashedThreadIndex(currentThreadId); } } } struct FrameListHandler : public MICommandHandler { FrameListHandler(MIFrameStackModel* model, int thread, int to) : model(model), m_thread(thread) , m_to(to) {} void handle(const ResultRecord &r) override { const Value& stack = r[QStringLiteral("stack")]; int first = stack[0][QStringLiteral("level")].toInt(); QVector frames; + frames.reserve(stack.size()); for (int i = 0; i< stack.size(); ++i) { const Value& frame = stack[i]; KDevelop::FrameStackModel::FrameItem f; f.nr = frame[QStringLiteral("level")].toInt(); f.name = getFunctionOrAddress(frame); QPair loc = getSource(frame); f.file = QUrl::fromLocalFile(loc.first).adjusted(QUrl::NormalizePathSegments); f.line = loc.second; frames << f; } bool hasMore = false; if (!frames.isEmpty()) { if (frames.last().nr == m_to+1) { frames.takeLast(); hasMore = true; } } if (first == 0) { model->setFrames(m_thread, frames); } else { model->insertFrames(m_thread, frames); } model->setHasMoreFrames(m_thread, hasMore); } private: MIFrameStackModel* model; int m_thread; int m_to; }; void MIFrameStackModel::fetchFrames(int threadNumber, int from, int to) { //to+1 so we know if there are more QString arg = QStringLiteral("%1 %2").arg(from).arg(to+1); MICommand *c = session()->createCommand(StackListFrames, arg); c->setHandler(new FrameListHandler(this, threadNumber, to)); c->setThread(threadNumber); session()->addCommand(c); } diff --git a/plugins/lldb/controllers/framestackmodel.cpp b/plugins/lldb/controllers/framestackmodel.cpp index cc31aec554..457261fb5c 100644 --- a/plugins/lldb/controllers/framestackmodel.cpp +++ b/plugins/lldb/controllers/framestackmodel.cpp @@ -1,119 +1,120 @@ /* * LLDB-specific implementation of frame stack model * Copyright 2016 Aetf * * 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 "framestackmodel.h" #include "debuglog.h" #include "debugsession.h" #include "mi/micommand.h" #include namespace { QString getFunctionOrAddress(const KDevMI::MI::Value &frame) { if (frame.hasField(QStringLiteral("func"))) return frame[QStringLiteral("func")].literal(); else return frame[QStringLiteral("addr")].literal(); } } using namespace KDevMI::LLDB; using namespace KDevMI::MI; using namespace KDevMI; LldbFrameStackModel::LldbFrameStackModel(DebugSession *session) : MIFrameStackModel(session) , stoppedAtThread(-1) { connect(session, &DebugSession::inferiorStopped, this, &LldbFrameStackModel::inferiorStopped); } DebugSession* LldbFrameStackModel::session() { return static_cast(FrameStackModel::session()); } void LldbFrameStackModel::inferiorStopped(const MI::AsyncRecord& r) { if (session()->debuggerStateIsOn(s_shuttingDown)) return; if (r.hasField(QStringLiteral("thread-id"))) { stoppedAtThread = r[QStringLiteral("thread-id")].toInt(); } } void LldbFrameStackModel::fetchThreads() { // TODO: preliminary test shows there might be a bug in lldb-mi // that's causing std::logic_error when executing -thread-info with // more than one threads. Find a workaround for this (and report bug // if it truly is). session()->addCommand(ThreadInfo, QLatin1String(""), this, &LldbFrameStackModel::handleThreadInfo); } void LldbFrameStackModel::handleThreadInfo(const ResultRecord& r) { const Value& threads = r[QStringLiteral("threads")]; QVector threadsList; + threadsList.reserve(threads.size()); for (int gidx = 0; gidx != threads.size(); ++gidx) { FrameStackModel::ThreadItem i; const Value & threadMI = threads[gidx]; i.nr = threadMI[QStringLiteral("id")].toInt(); if (threadMI[QStringLiteral("state")].literal() == QLatin1String("stopped")) { // lldb-mi returns multiple frame entry for each thread // so can't directly use threadMI["frame"] auto &th = static_cast(threadMI); Value *topFrame = nullptr; for (auto res : th.results) { if (res->variable == QLatin1String("frame")) { if (!topFrame || (*res->value)[QStringLiteral("level")].toInt() < (*topFrame)[QStringLiteral("level")].toInt()) { topFrame = res->value; } } } i.name = getFunctionOrAddress(*topFrame); } else { i.name = i18n("(running)"); } threadsList << i; } setThreads(threadsList); if (r.hasField(QStringLiteral("current-thread-id"))) { int currentThreadId = r[QStringLiteral("current-thread-id")].toInt(); setCurrentThread(currentThreadId); if (session()->hasCrashed()) { setCrashedThreadIndex(currentThreadId); } } // lldb-mi doesn't have current-thread-id field. Use the thread-id field when inferiorStopped if (stoppedAtThread != -1) { setCurrentThread(stoppedAtThread); } stoppedAtThread = -1; }