diff --git a/kdevplatform/util/activetooltip.cpp b/kdevplatform/util/activetooltip.cpp index dbc87068fc..40e54245a8 100644 --- a/kdevplatform/util/activetooltip.cpp +++ b/kdevplatform/util/activetooltip.cpp @@ -1,336 +1,337 @@ /* This file is part of the KDE project Copyright 2007 Vladimir Prus Copyright 2009-2010 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 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 "activetooltip.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { class ActiveToolTipManager : public QObject { Q_OBJECT + public Q_SLOTS: void doVisibility(); public: - typedef QMultiMap, QString> > ToolTipPriorityMap; + typedef QMultiMap, QString>> ToolTipPriorityMap; ToolTipPriorityMap registeredToolTips; }; ActiveToolTipManager* manager() { static ActiveToolTipManager m; return &m; } QWidget* masterWidget(QWidget* w) { while (w && w->parent() && qobject_cast(w->parent())) { w = qobject_cast(w->parent()); } return w; } void ActiveToolTipManager::doVisibility() { bool exclusive = false; int lastBottomPosition = -1; int lastLeftPosition = -1; QRect fullGeometry; //Geometry of all visible tooltips together for (auto it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) { - QPointer< ActiveToolTip > w = (*it).first; + QPointer w = (*it).first; if (w) { if (exclusive) { (w.data())->hide(); } else { QRect geom = (w.data())->geometry(); if ((w.data())->geometry().top() < lastBottomPosition) { geom.moveTop(lastBottomPosition); } if (lastLeftPosition != -1) { geom.moveLeft(lastLeftPosition); } (w.data())->setGeometry(geom); lastBottomPosition = (w.data())->geometry().bottom(); lastLeftPosition = (w.data())->geometry().left(); if (it == registeredToolTips.constBegin()) { fullGeometry = (w.data())->geometry(); } else { fullGeometry = fullGeometry.united((w.data())->geometry()); } } if (it.key() == 0) { exclusive = true; } } } + if (!fullGeometry.isEmpty()) { QRect oldFullGeometry = fullGeometry; QRect screenGeometry = QApplication::desktop()->screenGeometry(fullGeometry.topLeft()); if (fullGeometry.bottom() > screenGeometry.bottom()) { //Move up, avoiding the mouse-cursor - fullGeometry.moveBottom(fullGeometry.top()-10); + fullGeometry.moveBottom(fullGeometry.top() - 10); if (fullGeometry.adjusted(-20, -20, 20, 20).contains(QCursor::pos())) { fullGeometry.moveBottom(QCursor::pos().y() - 20); } } if (fullGeometry.right() > screenGeometry.right()) { //Move to left, avoiding the mouse-cursor - fullGeometry.moveRight(fullGeometry.left()-10); + fullGeometry.moveRight(fullGeometry.left() - 10); if (fullGeometry.adjusted(-20, -20, 20, 20).contains(QCursor::pos())) { fullGeometry.moveRight(QCursor::pos().x() - 20); } } // Now fit this to screen if (fullGeometry.left() < 0) { fullGeometry.setLeft(0); } if (fullGeometry.top() < 0) { fullGeometry.setTop(0); } QPoint offset = fullGeometry.topLeft() - oldFullGeometry.topLeft(); if (!offset.isNull()) { for (auto it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) if (it->first) { it->first.data()->move(it->first.data()->pos() + offset); } } } //Always include the mouse cursor in the full geometry, to avoid //closing the tooltip inexpectedly fullGeometry = fullGeometry.united(QRect(QCursor::pos(), QCursor::pos())); //Set bounding geometry, and remove old tooltips - for (auto it = registeredToolTips.begin(); it != registeredToolTips.end(); ) { + for (auto it = registeredToolTips.begin(); it != registeredToolTips.end();) { if (!it->first) { it = registeredToolTips.erase(it); } else { it->first.data()->setBoundingGeometry(fullGeometry); ++it; } } //Final step: Show tooltips foreach (const auto& tooltip, registeredToolTips) { if (tooltip.first.data() && masterWidget(tooltip.first.data())->isActiveWindow()) { tooltip.first.data()->show(); } if (exclusive) { break; } } } } namespace KDevelop { class ActiveToolTipPrivate { public: QRect rect_; QRect handleRect_; QVector> friendWidgets_; }; } -ActiveToolTip::ActiveToolTip(QWidget *parent, const QPoint& position) +ActiveToolTip::ActiveToolTip(QWidget* parent, const QPoint& position) : QWidget(parent, Qt::ToolTip) , d(new ActiveToolTipPrivate) { Q_ASSERT(parent); setMouseTracking(true); d->rect_ = QRect(position, position); d->rect_.adjust(-10, -10, 10, 10); move(position); QPalette p; // adjust background color to use tooltip colors p.setColor(backgroundRole(), p.color(QPalette::ToolTipBase)); p.setColor(QPalette::Base, p.color(QPalette::ToolTipBase)); // adjust foreground color to use tooltip colors p.setColor(foregroundRole(), p.color(QPalette::ToolTipText)); p.setColor(QPalette::Text, p.color(QPalette::ToolTipText)); setPalette(p); setWindowFlags(Qt::WindowDoesNotAcceptFocus | windowFlags()); qApp->installEventFilter(this); } ActiveToolTip::~ActiveToolTip() = default; -bool ActiveToolTip::eventFilter(QObject *object, QEvent *e) +bool ActiveToolTip::eventFilter(QObject* object, QEvent* e) { switch (e->type()) { case QEvent::MouseMove: if (underMouse() || insideThis(object)) { return false; } else { QPoint globalPos = static_cast(e)->globalPos(); QRect mergedRegion = d->rect_.united(d->handleRect_); if (mergedRegion.contains(globalPos)) { return false; } close(); } break; case QEvent::WindowActivate: if (insideThis(object)) { return false; } close(); break; case QEvent::WindowBlocked: // Modal dialog activated somewhere, it is the only case where a cursor // move may be missed and the popup has to be force-closed close(); break; default: break; } return false; } void ActiveToolTip::addFriendWidget(QWidget* widget) { - d->friendWidgets_.append((QObject*)widget); + d->friendWidgets_.append(( QObject* )widget); } bool ActiveToolTip::insideThis(QObject* object) { while (object) { if (dynamic_cast(object)) { return true; } - if (object == this || object == (QObject*)this->windowHandle() || d->friendWidgets_.contains(object)) { + if (object == this || object == ( QObject* )this->windowHandle() || d->friendWidgets_.contains(object)) { return true; } object = object->parent(); } // If the object clicked is inside a QQuickWidget, its parent is null even // if it is part of a tool-tip. This check ensures that a tool-tip is never // closed while the mouse is in it return underMouse(); } void ActiveToolTip::showEvent(QShowEvent*) { adjustRect(); } void ActiveToolTip::resizeEvent(QResizeEvent*) { adjustRect(); // set mask from style QStyleOptionFrame opt; opt.init(this); QStyleHintReturnMask mask; - if (style()->styleHint( QStyle::SH_ToolTip_Mask, &opt, this, &mask ) && !mask.region.isEmpty()) { - setMask( mask.region ); + if (style()->styleHint(QStyle::SH_ToolTip_Mask, &opt, this, &mask) && !mask.region.isEmpty()) { + setMask(mask.region); } emit resized(); } void ActiveToolTip::paintEvent(QPaintEvent* event) { - QStylePainter painter( this ); - painter.setClipRegion( event->region() ); + QStylePainter painter(this); + painter.setClipRegion(event->region()); QStyleOptionFrame opt; opt.init(this); painter.drawPrimitive(QStyle::PE_PanelTipLabel, opt); } void ActiveToolTip::setHandleRect(const QRect& rect) { - d->handleRect_= rect; + d->handleRect_ = rect; } void ActiveToolTip::adjustRect() { // For tooltip widget, geometry() returns global coordinates. QRect r = geometry(); r.adjust(-10, -10, 10, 10); d->rect_ = r; } void ActiveToolTip::setBoundingGeometry(const QRect& geometry) { d->rect_ = geometry; d->rect_.adjust(-10, -10, 10, 10); } void ActiveToolTip::showToolTip(ActiveToolTip* tooltip, float priority, const QString& uniqueId) { auto& registeredToolTips = manager()->registeredToolTips; if (!uniqueId.isEmpty()) { foreach (const auto& tooltip, registeredToolTips) { if (tooltip.second == uniqueId) { delete tooltip.first.data(); } } } registeredToolTips.insert(priority, qMakePair(QPointer(tooltip), uniqueId)); connect(tooltip, &ActiveToolTip::resized, manager(), &ActiveToolTipManager::doVisibility); QMetaObject::invokeMethod(manager(), "doVisibility", Qt::QueuedConnection); } - void ActiveToolTip::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); deleteLater(); } #include "activetooltip.moc" diff --git a/kdevplatform/util/activetooltip.h b/kdevplatform/util/activetooltip.h index 819e37fdc7..2dfa82fc8e 100644 --- a/kdevplatform/util/activetooltip.h +++ b/kdevplatform/util/activetooltip.h @@ -1,96 +1,98 @@ /* This file is part of the KDE project Copyright 2007 Vladimir Prus Copyright 2009-2010 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 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. */ #ifndef KDEVPLATFORM_ACTIVE_TOOLTIP_H #define KDEVPLATFORM_ACTIVE_TOOLTIP_H #include #include "utilexport.h" namespace KDevelop { /** This class implements a tooltip that can contain arbitrary widgets that the user can interact with. Usage example: @code KDevelop::ActiveToolTip* tooltip = new KDevelop::ActiveToolTip(mainWindow, QCursor::pos()); QVBoxLayout* layout = new QVBoxLayout(tooltip); layout->addWidget(widget); tooltip->resize( tooltip->sizeHint() ); ActiveToolTip::showToolTip(tooltip); @endcode */ class KDEVPLATFORMUTIL_EXPORT ActiveToolTip : public QWidget { Q_OBJECT + public: ///@param parent Parent widget. Must not be zero, else the widget won't be shown. /// @param position Position where to show the tooltip, in global coordinates. - ActiveToolTip(QWidget *parent, const QPoint& position); + ActiveToolTip(QWidget* parent, const QPoint& position); ~ActiveToolTip() override; ///Shows and registers the given tool-tip. ///This should be used instead of just calling show() to make multiple different ///tooltips work together. ///The tooltip is owned by the manager after this is called. It will delete itself. ///@param tooltip The tooltip to show. It should not be visible yet, show() will eventually be called from here, with some delay. /// The ownership stays with the caller. ///@param priority The priority of this tooltip. Lower is better. Multiple tooltips will be stacked down in the given order. /// If it is zero, the given tooltip will be shown exclusively. ///@param uniqueId If this is nonempty, ActiveTooltip will make sure that only one tooltip with the given id is shown at a time static void showToolTip(ActiveToolTip* tooltip, float priority = 100, const QString& uniqueId = QString()); - bool eventFilter(QObject *object, QEvent *e) override; + bool eventFilter(QObject* object, QEvent* e) override; bool insideThis(QObject* object); void showEvent(QShowEvent*) override; void resizeEvent(QResizeEvent*) override; void paintEvent(QPaintEvent*) override; void adjustRect(); ///Clicks within the friend widget are allowed void addFriendWidget(QWidget* widget); ///Set rect of handle (object) this tool tip is created for ///Moving mouse inside this rect, and between this and bounding geometry won't hide the tooltip void setHandleRect(const QRect& rect); ///Set the area within which the mouse can be moved freely without hiding the tooltip void setBoundingGeometry(const QRect& geometry); Q_SIGNALS: void resized(); // Emitted whenever mouse-activity is noticed within the tooltip area void mouseIn(); // Emitted whenever mouse-activity is noticed outside of the tooltip area void mouseOut(); + private: - void closeEvent(QCloseEvent* ) override; + void closeEvent(QCloseEvent*) override; private: const QScopedPointer d; }; } #endif diff --git a/kdevplatform/util/autoorientedsplitter.cpp b/kdevplatform/util/autoorientedsplitter.cpp index b33192ee4a..8d5c557276 100644 --- a/kdevplatform/util/autoorientedsplitter.cpp +++ b/kdevplatform/util/autoorientedsplitter.cpp @@ -1,45 +1,44 @@ /* This file is part of KDevelop Copyright 2014 Kevin Funk 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 "autoorientedsplitter.h" #include using namespace KDevelop; AutoOrientedSplitter::AutoOrientedSplitter(QWidget* parent) : QSplitter(parent) { } AutoOrientedSplitter::AutoOrientedSplitter(Qt::Orientation orientation, QWidget* parent) : QSplitter(orientation, parent) { } void AutoOrientedSplitter::resizeEvent(QResizeEvent* e) { const QSize size = e->size(); - const float ratio = (float)size.width() / size.height(); + const float ratio = ( float )size.width() / size.height(); const Qt::Orientation orientation = (ratio < 1.0 ? Qt::Vertical : Qt::Horizontal); setOrientation(orientation); QSplitter::resizeEvent(e); } - diff --git a/kdevplatform/util/commandexecutor.cpp b/kdevplatform/util/commandexecutor.cpp index 3a7e247c4b..15fa0372b3 100644 --- a/kdevplatform/util/commandexecutor.cpp +++ b/kdevplatform/util/commandexecutor.cpp @@ -1,168 +1,175 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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 "commandexecutor.h" #include "processlinemaker.h" #include #include #include #include #include -namespace KDevelop -{ +namespace KDevelop { class CommandExecutorPrivate { public: - explicit CommandExecutorPrivate( CommandExecutor* cmd ) - : m_exec(cmd), m_useShell(false) + explicit CommandExecutorPrivate(CommandExecutor* cmd) + : m_exec(cmd) + , m_useShell(false) { } CommandExecutor* m_exec; KProcess* m_process; ProcessLineMaker* m_lineMaker; QString m_command; QStringList m_args; QString m_workDir; - QMap m_env; + QMap m_env; bool m_useShell; - void procError( QProcess::ProcessError error ) + void procError(QProcess::ProcessError error) { Q_UNUSED(error) m_lineMaker->flushBuffers(); - emit m_exec->failed( error ); + emit m_exec->failed(error); } - void procFinished( int code, QProcess::ExitStatus status ) + void procFinished(int code, QProcess::ExitStatus status) { m_lineMaker->flushBuffers(); - if( status == QProcess::NormalExit ) + if (status == QProcess::NormalExit) emit m_exec->completed(code); } }; -CommandExecutor::CommandExecutor( const QString& command, QObject* parent ) - : QObject(parent), d(new CommandExecutorPrivate(this)) +CommandExecutor::CommandExecutor(const QString& command, QObject* parent) + : QObject(parent) + , d(new CommandExecutorPrivate(this)) { d->m_process = new KProcess(this); - d->m_process->setOutputChannelMode( KProcess::SeparateChannels ); - d->m_lineMaker = new ProcessLineMaker( d->m_process ); + d->m_process->setOutputChannelMode(KProcess::SeparateChannels); + d->m_lineMaker = new ProcessLineMaker(d->m_process); d->m_command = command; - connect( d->m_lineMaker, &ProcessLineMaker::receivedStdoutLines, - this, &CommandExecutor::receivedStandardOutput ); - connect( d->m_lineMaker, &ProcessLineMaker::receivedStderrLines, - this, &CommandExecutor::receivedStandardError ); - connect( d->m_process, static_cast(&KProcess::error), - this, [&] (QProcess::ProcessError error) { d->procError(error); } ); - connect( d->m_process, static_cast(&KProcess::finished), - this, [&] (int code, QProcess::ExitStatus status) { d->procFinished(code, status); } ); + connect(d->m_lineMaker, &ProcessLineMaker::receivedStdoutLines, + this, &CommandExecutor::receivedStandardOutput); + connect(d->m_lineMaker, &ProcessLineMaker::receivedStderrLines, + this, &CommandExecutor::receivedStandardError); + connect(d->m_process, static_cast(&KProcess::error), + this, [&](QProcess::ProcessError error) { + d->procError(error); + }); + connect(d->m_process, static_cast(&KProcess::finished), + this, [&](int code, QProcess::ExitStatus status) { + d->procFinished(code, status); + }); } - CommandExecutor::~CommandExecutor() { delete d->m_process; delete d->m_lineMaker; } -void CommandExecutor::setEnvironment( const QMap& env ) +void CommandExecutor::setEnvironment(const QMap& env) { d->m_env = env; } -void CommandExecutor::setEnvironment( const QStringList& env ) +void CommandExecutor::setEnvironment(const QStringList& env) { - QMap envmap; + QMap envmap; for (const QString& var : env) { int sep = var.indexOf(QLatin1Char('=')); - envmap.insert( var.left( sep ), var.mid( sep + 1 ) ); + envmap.insert(var.left(sep), var.mid(sep + 1)); } + d->m_env = envmap; } -void CommandExecutor::setArguments( const QStringList& args ) +void CommandExecutor::setArguments(const QStringList& args) { d->m_args = args; } -void CommandExecutor::setWorkingDirectory( const QString& dir ) +void CommandExecutor::setWorkingDirectory(const QString& dir) { d->m_workDir = dir; } bool CommandExecutor::useShell() const { return d->m_useShell; } -void CommandExecutor::setUseShell( bool shell ) +void CommandExecutor::setUseShell(bool shell) { d->m_useShell = shell; } void CommandExecutor::start() { - for(auto it = d->m_env.constBegin(), itEnd = d->m_env.constEnd(); it!=itEnd; ++it) - { - d->m_process->setEnv( it.key(), it.value() ); + for (auto it = d->m_env.constBegin(), itEnd = d->m_env.constEnd(); it != itEnd; ++it) { + d->m_process->setEnv(it.key(), it.value()); } - d->m_process->setWorkingDirectory( d->m_workDir ); - if( !d->m_useShell ) { - d->m_process->setProgram( d->m_command, d->m_args ); + + d->m_process->setWorkingDirectory(d->m_workDir); + if (!d->m_useShell) { + d->m_process->setProgram(d->m_command, d->m_args); } else { QStringList arguments; arguments.reserve(d->m_args.size()); - Q_FOREACH( const QString &a, d->m_args ) arguments << KShell::quoteArg( a ); + Q_FOREACH (const QString& a, d->m_args) + arguments << KShell::quoteArg(a); + d->m_process->setShellCommand(d->m_command + QLatin1Char(' ') + arguments.join(QLatin1Char(' '))); } d->m_process->start(); } -void CommandExecutor::setCommand( const QString& command ) +void CommandExecutor::setCommand(const QString& command) { d->m_command = command; } void CommandExecutor::kill() { d->m_process->kill(); } QString CommandExecutor::command() const { return d->m_command; } QStringList CommandExecutor::arguments() const { return d->m_args; } QString CommandExecutor::workingDirectory() const { return d->m_workDir; } } #include "moc_commandexecutor.cpp" diff --git a/kdevplatform/util/commandexecutor.h b/kdevplatform/util/commandexecutor.h index e6a56ccabd..4a2585e3d8 100644 --- a/kdevplatform/util/commandexecutor.h +++ b/kdevplatform/util/commandexecutor.h @@ -1,142 +1,142 @@ /* This file is part of KDevelop Copyright 2007 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_COMMANDEXECUTOR_H #define KDEVPLATFORM_COMMANDEXECUTOR_H #include #include #include "utilexport.h" -namespace KDevelop -{ +namespace KDevelop { /** * Simplifying the exeuction of a Command through (QK)Process. * * This class emits only very basic signals when the process writes something - * to stdout or stderr and for signaling completed and failed status of running + * to stdout or stderr and for signaling completed and failed status of running * the process. This means that a process that is executed without a crash or so * is considered to be completed, even if it indicates an error during execution * using a non-zero return value. This needs to be handled by the user of the class * using the argument in the completed signal * * If you need more fine-grained control use (QK)Process directly and also * check whether you can use \ref KDevelop::ProcessLineMaker to use properly * terminated lines of output. * * Also this class provides only asynchronous operation, it doesn't allow to * wait for the program to finish. * * @author Andreas Pakulat * TODO: Should this be a KJob?? */ class KDEVPLATFORMUTIL_EXPORT CommandExecutor : public QObject { Q_OBJECT + public: /** * Create a command using the given executable, arguments and environment * * The process is not started immediately, instead start() has to be called. */ - explicit CommandExecutor( const QString& command, QObject* parent = nullptr ); + explicit CommandExecutor(const QString& command, QObject* parent = nullptr); ~CommandExecutor() override; /** * set additional arguments to be used when executing the command */ - void setArguments( const QStringList& args ); + void setArguments(const QStringList& args); /** * set additional environment variables to be used when executing the command */ - void setEnvironment( const QMap& env ); + void setEnvironment(const QMap& env); /** * set additional environment variables to be used when executing the command */ - void setEnvironment( const QStringList& env ); + void setEnvironment(const QStringList& env); /** * Sets the working directory of the command */ - void setWorkingDirectory( const QString& dir ); + void setWorkingDirectory(const QString& dir); /** * start the command, after this has been called signals may be emitted */ void start(); - + /** * kill the process, failed() will likely be emitted */ void kill(); /** * set the Command that should be started, now a commandexecutor can be reused */ - void setCommand( const QString& command ); - + void setCommand(const QString& command); + /** * whether the commands are executed from a shell */ bool useShell() const; /** * if @p shell is true, the command is executed from a shell */ - void setUseShell( bool shell ); - + void setUseShell(bool shell); + /** * @returns the arguments */ QStringList arguments() const; - + /** * @returns the command */ QString command() const; - + /** * @returns the working directory */ QString workingDirectory() const; - + Q_SIGNALS: - void receivedStandardError( const QStringList& ); - void receivedStandardOutput( const QStringList& ); + void receivedStandardError(const QStringList&); + void receivedStandardOutput(const QStringList&); /** * Emitted when there was a severe problem executing the process, for example it * could not be started or crashed during execution. */ - void failed( QProcess::ProcessError ); + void failed(QProcess::ProcessError); /** * Emitted when the process was successfully started and finished without crashing * The @p code parameter indicates the return value from executing the process */ void completed(int code); private: const QScopedPointer d; friend class CommandExecutorPrivate; }; } #endif diff --git a/kdevplatform/util/convenientfreelist.h b/kdevplatform/util/convenientfreelist.h index 5143f0df2c..f55accc7ba 100644 --- a/kdevplatform/util/convenientfreelist.h +++ b/kdevplatform/util/convenientfreelist.h @@ -1,743 +1,831 @@ /* This file is part of KDevelop Copyright 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. */ #ifndef KDEVPLATFORM_CONVENIENTFREELIST_H #define KDEVPLATFORM_CONVENIENTFREELIST_H #include #include #include "embeddedfreetree.h" #include "kdevvarlengtharray.h" namespace KDevelop { - template - class ConvenientEmbeddedSetIterator; - template - class ConvenientEmbeddedSetFilterIterator; - - ///A convenience-class for accessing the data in a set managed by the EmbeddedFreeTree algorithms. - template - class ConstantConvenientEmbeddedSet { - public: - ConstantConvenientEmbeddedSet() : m_data(nullptr) { - } - ConstantConvenientEmbeddedSet(const Data* data, uint count, int centralFreeItem) : m_data(data), m_dataSize(count), m_centralFreeItem(centralFreeItem) { - } - - ///Returns whether the item is contained in this set - bool contains(const Data& data) const { - return indexOf(data) != -1; - } +template +class ConvenientEmbeddedSetIterator; +template +class ConvenientEmbeddedSetFilterIterator; + +///A convenience-class for accessing the data in a set managed by the EmbeddedFreeTree algorithms. +template +class ConstantConvenientEmbeddedSet +{ +public: + ConstantConvenientEmbeddedSet() : m_data(nullptr) + { + } + ConstantConvenientEmbeddedSet(const Data* data, uint count, int centralFreeItem) : m_data(data) + , m_dataSize(count) + , m_centralFreeItem(centralFreeItem) + { + } - ///Returns the position of the item in the underlying array, or -1 if it is not contained - int indexOf(const Data& data) const { - EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); - return alg.indexOf(data); - } + ///Returns whether the item is contained in this set + bool contains(const Data& data) const + { + return indexOf(data) != -1; + } - ///Returns the size of the underlying array - uint dataSize() const { - return m_dataSize; - } + ///Returns the position of the item in the underlying array, or -1 if it is not contained + int indexOf(const Data& data) const + { + EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); + return alg.indexOf(data); + } - uint countFreeItems() { - EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); - return alg.countFreeItems(); - } + ///Returns the size of the underlying array + uint dataSize() const + { + return m_dataSize; + } - void verify() { - EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); - alg.verifyTreeConsistent(); - alg.verifyOrder(); - } + uint countFreeItems() + { + EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); + return alg.countFreeItems(); + } - ///Returns the underlying array. That array may contain invalid/free items. - const Data* data() const { - return m_data; - } + void verify() + { + EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); + alg.verifyTreeConsistent(); + alg.verifyOrder(); + } - ///Returns the first valid index that has a data-value larger or equal to @p data. - ///Returns -1 if nothing is found. - int lowerBound(const Data& data) const { - EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); - return alg.lowerBound(data, 0, m_dataSize); - } + ///Returns the underlying array. That array may contain invalid/free items. + const Data* data() const + { + return m_data; + } - ///Returns the first valid index that has a data-value larger or equal to @p data, - ///and that is in the range [start, end). - ///Returns -1 if nothing is found. - int lowerBound(const Data& data, uint start, uint end) const { - EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); - return alg.lowerBound(data, start, end); - } + ///Returns the first valid index that has a data-value larger or equal to @p data. + ///Returns -1 if nothing is found. + int lowerBound(const Data& data) const + { + EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); + return alg.lowerBound(data, 0, m_dataSize); + } - ///Finds a valid most central in the range [start, end). - ///Returns -1 if no such item exists. - int validMiddle(uint start, uint end) { - if(end <= start) - return -1; + ///Returns the first valid index that has a data-value larger or equal to @p data, + ///and that is in the range [start, end). + ///Returns -1 if nothing is found. + int lowerBound(const Data& data, uint start, uint end) const + { + EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); + return alg.lowerBound(data, start, end); + } - int firstTry = ((end-start)/2) + start; + ///Finds a valid most central in the range [start, end). + ///Returns -1 if no such item exists. + int validMiddle(uint start, uint end) + { + if (end <= start) + return -1; - int thisTry = firstTry; - while(thisTry < (int)end && Handler::isFree(m_data[thisTry])) - ++thisTry; + int firstTry = ((end - start) / 2) + start; - if(thisTry != (int)end) - return thisTry; + int thisTry = firstTry; + while (thisTry < ( int )end && Handler::isFree(m_data[thisTry])) + ++thisTry; - //Nothing find on right side of middle, try the other direction - thisTry = firstTry-1; - while(thisTry >= (int)start && Handler::isFree(m_data[thisTry])) - --thisTry; + if (thisTry != ( int )end) + return thisTry; - if(thisTry >= (int)start) - return thisTry; - else - return -1; - } + //Nothing find on right side of middle, try the other direction + thisTry = firstTry - 1; + while (thisTry >= ( int )start && Handler::isFree(m_data[thisTry])) + --thisTry; - ///Returns the first valid item in the range [pos, end), or -1 - int firstValidItem(int start, int end = -1) const { - if(end == -1) - end = (int)m_dataSize; - for(; start < end; ++start) - if(!Handler::isFree(m_data[start])) - return start; + if (thisTry >= ( int )start) + return thisTry; + else + return -1; + } - return -1; - } + ///Returns the first valid item in the range [pos, end), or -1 + int firstValidItem(int start, int end = -1) const + { + if (end == -1) + end = ( int )m_dataSize; + for (; start < end; ++start) + if (!Handler::isFree(m_data[start])) + return start; - ///Returns the last valid item in the range [pos, end), or -1 - int lastValidItem(int start = 0, int end = -1) const { - if(end == -1) - end = (int)m_dataSize; - --end; - for(; end >= start; --end) - if(!Handler::isFree(m_data[end])) - return end; + return -1; + } - return -1; - } + ///Returns the last valid item in the range [pos, end), or -1 + int lastValidItem(int start = 0, int end = -1) const + { + if (end == -1) + end = ( int )m_dataSize; + --end; + for (; end >= start; --end) + if (!Handler::isFree(m_data[end])) + return end; + + return -1; + } - typedef ConvenientEmbeddedSetIterator Iterator; + typedef ConvenientEmbeddedSetIterator Iterator; - ConvenientEmbeddedSetIterator iterator() const; + ConvenientEmbeddedSetIterator iterator() const; // protected: - const Data* m_data; - uint m_dataSize = 0; - int m_centralFreeItem = -1; - }; - - ///Convenient iterator that automatically skips invalid/free items in the array. - template - class ConvenientEmbeddedSetIterator : public ConstantConvenientEmbeddedSet { - public: - explicit ConvenientEmbeddedSetIterator(const Data* data = nullptr, uint count = 0, int centralFreeItem = -1) : ConstantConvenientEmbeddedSet(data, count, centralFreeItem) { - //Move to first valid position - moveToValid(); - } - - ///Returns true of this iterator has a value to return - operator bool() const { - return m_pos != this->m_dataSize; - } - - const Data* operator->() const { - return &this->m_data[m_pos]; - } - - const Data& operator*() const { - return this->m_data[m_pos]; - } - - ConvenientEmbeddedSetIterator& operator++() { - ++m_pos; - moveToValid(); - return *this; - } - - protected: - inline void moveToValid() { - while(this->m_pos < this->m_dataSize && (Handler::isFree(this->m_data[this->m_pos]))) - ++this->m_pos; - } - uint m_pos = 0; - }; + const Data* m_data; + uint m_dataSize = 0; + int m_centralFreeItem = -1; +}; + +///Convenient iterator that automatically skips invalid/free items in the array. +template +class ConvenientEmbeddedSetIterator : public ConstantConvenientEmbeddedSet +{ +public: + explicit ConvenientEmbeddedSetIterator(const Data* data = nullptr, uint count = 0, + int centralFreeItem = -1) : ConstantConvenientEmbeddedSet( + data, count, centralFreeItem) + { + //Move to first valid position + moveToValid(); + } + ///Returns true of this iterator has a value to return + operator bool() const { + return m_pos != this->m_dataSize; + } - ///An iterator that allows efficient matching between two lists with different data type. - ///Important: It must be possible to extract the data-type of the second list from the items in the first list - ///The second list must be sorted by that data. - ///The first list must primarily be sorted by that data, but is allowed to - ///be sub-ordered by something else, and multiple items in the first list are allowed to match one item in the second. - ///This iterator iterates through all items in the first list that have extracted key-data that is in represented in the second. - template - class ConvenientEmbeddedSetFilterIterator : public ConvenientEmbeddedSetIterator { - public: - ConvenientEmbeddedSetFilterIterator() { - } - ConvenientEmbeddedSetFilterIterator(const ConvenientEmbeddedSetIterator& base, const ConvenientEmbeddedSetIterator& rhs) : ConvenientEmbeddedSetIterator(base), m_rhs(rhs), m_match(-1) { - boundStack.append( qMakePair( qMakePair(0u, this->m_dataSize), qMakePair(0u, rhs.m_dataSize) ) ); - go(); - } + const Data* operator->() const + { + return &this->m_data[m_pos]; + } - operator bool() const { - return m_match != -1; - } + const Data& operator*() const + { + return this->m_data[m_pos]; + } - const Data* operator->() const { - Q_ASSERT(m_match != -1); - return &this->m_data[m_match]; - } + ConvenientEmbeddedSetIterator& operator++() + { + ++m_pos; + moveToValid(); + return *this; + } - const Data& operator*() const { - Q_ASSERT(m_match != -1); - return this->m_data[m_match]; - } +protected: + inline void moveToValid() + { + while (this->m_pos < this->m_dataSize && (Handler::isFree(this->m_data[this->m_pos]))) + ++this->m_pos; + } + uint m_pos = 0; +}; + +///An iterator that allows efficient matching between two lists with different data type. +///Important: It must be possible to extract the data-type of the second list from the items in the first list +///The second list must be sorted by that data. +///The first list must primarily be sorted by that data, but is allowed to +///be sub-ordered by something else, and multiple items in the first list are allowed to match one item in the second. +///This iterator iterates through all items in the first list that have extracted key-data that is in represented in the second. +template +class ConvenientEmbeddedSetFilterIterator : public ConvenientEmbeddedSetIterator +{ +public: + ConvenientEmbeddedSetFilterIterator() + { + } + ConvenientEmbeddedSetFilterIterator(const ConvenientEmbeddedSetIterator& base, + const ConvenientEmbeddedSetIterator& rhs) : ConvenientEmbeddedSetIterator(base) + , m_rhs(rhs) + , m_match(-1) + { + boundStack.append(qMakePair(qMakePair(0u, this->m_dataSize), qMakePair(0u, rhs.m_dataSize))); + go(); + } - ConvenientEmbeddedSetFilterIterator& operator++() { - Q_ASSERT(m_match != -1); - go(); - return *this; - } - #define CHECK_BOUNDS Q_ASSERT(boundStack.back().first.first < 100000 && boundStack.back().first.second < 10000 && boundStack.back().second.first < 100000 && boundStack.back().second.second < 10000 ); + operator bool() const { + return m_match != -1; + } - private: - void go() { - m_match = -1; + const Data* operator->() const + { + Q_ASSERT(m_match != -1); + return &this->m_data[m_match]; + } - boundsUp: - if(boundStack.isEmpty()) - return; - CHECK_BOUNDS - QPair, QPair > currentBounds = boundStack.back(); - boundStack.pop_back(); + const Data& operator*() const + { + Q_ASSERT(m_match != -1); + return this->m_data[m_match]; + } - uint ownStart = currentBounds.first.first, ownEnd = currentBounds.first.second; - uint rhsStart = currentBounds.second.first, rhsEnd = currentBounds.second.second; + ConvenientEmbeddedSetFilterIterator& operator++() + { + Q_ASSERT(m_match != -1); + go(); + return *this; + } + #define CHECK_BOUNDS Q_ASSERT( \ + boundStack.back().first.first < 100000 && boundStack.back().first.second < 10000 && boundStack.back().second.first < 100000 && \ + boundStack.back().second.second < 10000); + +private: + void go() + { + m_match = -1; + +boundsUp: + if (boundStack.isEmpty()) + return; + CHECK_BOUNDS + QPair, QPair> currentBounds = boundStack.back(); + boundStack.pop_back(); + + uint ownStart = currentBounds.first.first, ownEnd = currentBounds.first.second; + uint rhsStart = currentBounds.second.first, rhsEnd = currentBounds.second.second; #if 0 - //This code works, but it doesn't give a speedup - int ownFirstValid = this->firstValidItem(ownStart, ownEnd), ownLastValid = this->lastValidItem(ownStart, ownEnd); - int rhsFirstValid = m_rhs.firstValidItem(rhsStart, rhsEnd), rhsLastValid = m_rhs.lastValidItem(rhsStart, rhsEnd); + //This code works, but it doesn't give a speedup + int ownFirstValid = this->firstValidItem(ownStart, ownEnd), + ownLastValid = this->lastValidItem(ownStart, ownEnd); + int rhsFirstValid = m_rhs.firstValidItem(rhsStart, rhsEnd), + rhsLastValid = m_rhs.lastValidItem(rhsStart, rhsEnd); - if(ownFirstValid == -1 || rhsFirstValid == -1) - goto boundsUp; + if (ownFirstValid == -1 || rhsFirstValid == -1) + goto boundsUp; + Data2 ownFirstValidData = KeyExtractor::extract(this->m_data[ownFirstValid]); + Data2 ownLastValidData = KeyExtractor::extract(this->m_data[ownLastValid]); - Data2 ownFirstValidData = KeyExtractor::extract(this->m_data[ownFirstValid]); - Data2 ownLastValidData = KeyExtractor::extract(this->m_data[ownLastValid]); + Data2 commonStart = ownFirstValidData; + Data2 commonLast = ownLastValidData; //commonLast is also still valid - Data2 commonStart = ownFirstValidData; - Data2 commonLast = ownLastValidData; //commonLast is also still valid + if (commonStart < m_rhs.m_data[rhsFirstValid]) + commonStart = m_rhs.m_data[rhsFirstValid]; - if(commonStart < m_rhs.m_data[rhsFirstValid]) - commonStart = m_rhs.m_data[rhsFirstValid]; + if (m_rhs.m_data[rhsLastValid] < commonLast) + commonLast = m_rhs.m_data[rhsLastValid]; - if(m_rhs.m_data[rhsLastValid] < commonLast) - commonLast = m_rhs.m_data[rhsLastValid]; - - if(commonLast < commonStart) - goto boundsUp; + if (commonLast < commonStart) + goto boundsUp; #endif - while(true) { - if(ownStart == ownEnd) - goto boundsUp; + while (true) { + if (ownStart == ownEnd) + goto boundsUp; - int ownMiddle = this->validMiddle(ownStart, ownEnd); - Q_ASSERT(ownMiddle < 100000); - if(ownMiddle == -1) - goto boundsUp; //No valid items in the range + int ownMiddle = this->validMiddle(ownStart, ownEnd); + Q_ASSERT(ownMiddle < 100000); + if (ownMiddle == -1) + goto boundsUp; //No valid items in the range - Data2 currentData2 = KeyExtractor::extract(this->m_data[ownMiddle]); - Q_ASSERT(!Handler2::isFree(currentData2)); + Data2 currentData2 = KeyExtractor::extract(this->m_data[ownMiddle]); + Q_ASSERT(!Handler2::isFree(currentData2)); - int bound = m_rhs.lowerBound(currentData2, rhsStart, rhsEnd); - if(bound == -1) { - //Release second half of the own range + int bound = m_rhs.lowerBound(currentData2, rhsStart, rhsEnd); + if (bound == -1) { + //Release second half of the own range // Q_ASSERT(ownEnd > ownMiddle); - ownEnd = ownMiddle; - continue; - } - - if(currentData2 == m_rhs.m_data[bound]) { - //We have a match - this->m_match = ownMiddle; - //Append the ranges that need to be matched next, without the matched item - boundStack.append( qMakePair( qMakePair( (uint)ownMiddle+1, ownEnd ), qMakePair((uint)bound, rhsEnd)) ); - if(ownMiddle) - boundStack.append( qMakePair( qMakePair( ownStart, (uint)ownMiddle ), qMakePair(rhsStart, (uint)bound+1)) ); - return; - } + ownEnd = ownMiddle; + continue; + } + + if (currentData2 == m_rhs.m_data[bound]) { + //We have a match + this->m_match = ownMiddle; + //Append the ranges that need to be matched next, without the matched item + boundStack.append(qMakePair(qMakePair(( uint )ownMiddle + 1, ownEnd), + qMakePair(( uint )bound, rhsEnd))); + if (ownMiddle) + boundStack.append(qMakePair(qMakePair(ownStart, ( uint )ownMiddle), + qMakePair(rhsStart, ( uint )bound + 1))); + return; + } - if(bound == m_rhs.firstValidItem(rhsStart)) { - //The bound is the first valid item of the second range. - //Discard left side and the matched left item, and continue. + if (bound == m_rhs.firstValidItem(rhsStart)) { + //The bound is the first valid item of the second range. + //Discard left side and the matched left item, and continue. - ownStart = ownMiddle+1; - rhsStart = bound; - continue; - } + ownStart = ownMiddle + 1; + rhsStart = bound; + continue; + } - //Standard: Split both sides into 2 ranges that will be checked next - boundStack.append( qMakePair( qMakePair( (uint)ownMiddle+1, ownEnd ), qMakePair((uint)bound, rhsEnd)) ); + //Standard: Split both sides into 2 ranges that will be checked next + boundStack.append(qMakePair(qMakePair(( uint )ownMiddle + 1, ownEnd), qMakePair(( uint )bound, rhsEnd))); // Q_ASSERT(ownMiddle <= ownEnd); - ownEnd = ownMiddle; //We loose the item at 'middle' here, but that's fine, since it hasn't found a match. - rhsEnd = bound+1; - } + ownEnd = ownMiddle; //We loose the item at 'middle' here, but that's fine, since it hasn't found a match. + rhsEnd = bound + 1; } + } - //Bounds that yet need to be matched. - KDevVarLengthArray, QPair > > boundStack; - ConvenientEmbeddedSetIterator m_rhs; - int m_match = -1; - }; - - ///Filters a list-embedded set by a binary tree set as managed by the SetRepository data structures - template - class ConvenientEmbeddedSetTreeFilterIterator : public ConvenientEmbeddedSetIterator { - public: - ConvenientEmbeddedSetTreeFilterIterator() { - } - ///@param noFiltering whether the given input is pre-filtered. If this is true, base will be iterated without skipping any items. - ConvenientEmbeddedSetTreeFilterIterator(const ConvenientEmbeddedSetIterator& base, const TreeSet& rhs, bool noFiltering = false) : ConvenientEmbeddedSetIterator(base), m_rhs(rhs), m_match(-1), m_noFiltering(noFiltering) { - if(rhs.node().isValid()) { - //Correctly initialize the initial bounds - int ownStart = lowerBound(rhs.node().firstItem(), 0, this->m_dataSize); - if(ownStart == -1) - return; - int ownEnd = lowerBound(rhs.node().lastItem(), ownStart, this->m_dataSize); - if(ownEnd == -1) - ownEnd = this->m_dataSize; - else - ownEnd += 1; - boundStack.append( qMakePair( qMakePair((uint)ownStart, (uint)ownEnd), rhs.node() ) ); - } - go(); + //Bounds that yet need to be matched. + KDevVarLengthArray, QPair>> boundStack; + ConvenientEmbeddedSetIterator m_rhs; + int m_match = -1; +}; + +///Filters a list-embedded set by a binary tree set as managed by the SetRepository data structures +template +class ConvenientEmbeddedSetTreeFilterIterator : public ConvenientEmbeddedSetIterator +{ +public: + ConvenientEmbeddedSetTreeFilterIterator() + { + } + ///@param noFiltering whether the given input is pre-filtered. If this is true, base will be iterated without skipping any items. + ConvenientEmbeddedSetTreeFilterIterator(const ConvenientEmbeddedSetIterator& base, + const TreeSet& rhs, + bool noFiltering = false) : ConvenientEmbeddedSetIterator( + base) + , m_rhs(rhs) + , m_match(-1) + , m_noFiltering(noFiltering) + { + if (rhs.node().isValid()) { + //Correctly initialize the initial bounds + int ownStart = lowerBound(rhs.node().firstItem(), 0, this->m_dataSize); + if (ownStart == -1) + return; + int ownEnd = lowerBound(rhs.node().lastItem(), ownStart, this->m_dataSize); + if (ownEnd == -1) + ownEnd = this->m_dataSize; + else + ownEnd += 1; + boundStack.append(qMakePair(qMakePair(( uint )ownStart, ( uint )ownEnd), rhs.node())); } + go(); + } - operator bool() const { - return m_match != -1; - } + operator bool() const { + return m_match != -1; + } - const Data* operator->() const { - Q_ASSERT(m_match != -1); - return &this->m_data[m_match]; - } + const Data* operator->() const + { + Q_ASSERT(m_match != -1); + return &this->m_data[m_match]; + } - const Data& operator*() const { - Q_ASSERT(m_match != -1); - return this->m_data[m_match]; - } + const Data& operator*() const + { + Q_ASSERT(m_match != -1); + return this->m_data[m_match]; + } - ConvenientEmbeddedSetTreeFilterIterator& operator++() { - Q_ASSERT(m_match != -1); - go(); - return *this; + ConvenientEmbeddedSetTreeFilterIterator& operator++() + { + Q_ASSERT(m_match != -1); + go(); + return *this; + } + #define CHECK_BOUNDS Q_ASSERT( \ + boundStack.back().first.first < 100000 && boundStack.back().first.second < 10000 && boundStack.back().second.first < 100000 && \ + boundStack.back().second.second < 10000); + +private: + void go() + { + if (m_noFiltering) { + ++m_match; + if (( uint )m_match >= this->m_dataSize) + m_match = -1; + return; } - #define CHECK_BOUNDS Q_ASSERT(boundStack.back().first.first < 100000 && boundStack.back().first.second < 10000 && boundStack.back().second.first < 100000 && boundStack.back().second.second < 10000 ); - - private: - void go() { - if(m_noFiltering) { - ++m_match; - if((uint)m_match >= this->m_dataSize) - m_match = -1; + + if (m_match != -1) { + //Match multiple items in this list to one in the tree + m_match = this->firstValidItem(m_match + 1, this->m_dataSize); + if (m_match != -1 && KeyExtractor::extract(this->m_data[m_match]) == m_matchingTo) return; - } + } + m_match = -1; - if(m_match != -1) { - //Match multiple items in this list to one in the tree - m_match = this->firstValidItem(m_match+1, this->m_dataSize); - if(m_match != -1 && KeyExtractor::extract(this->m_data[m_match]) == m_matchingTo) - return; - } - m_match = -1; +boundsUp: + if (boundStack.isEmpty()) + return; + QPair, typename TreeSet::Node> currentBounds = boundStack.back(); + boundStack.pop_back(); - boundsUp: - if(boundStack.isEmpty()) - return; - QPair, typename TreeSet::Node > currentBounds = boundStack.back(); - boundStack.pop_back(); + uint ownStart = currentBounds.first.first, ownEnd = currentBounds.first.second; + typename TreeSet::Node currentNode = currentBounds.second; - uint ownStart = currentBounds.first.first, ownEnd = currentBounds.first.second; - typename TreeSet::Node currentNode = currentBounds.second; + if (ownStart >= ownEnd) + goto boundsUp; + if (!currentNode.isValid()) + goto boundsUp; - if(ownStart >= ownEnd) + while (true) { + if (ownStart == ownEnd) goto boundsUp; - if(!currentNode.isValid()) - goto boundsUp; - - while(true) { - if(ownStart == ownEnd) - goto boundsUp; - if(currentNode.isFinalNode()) { + if (currentNode.isFinalNode()) { // qCDebug(UTIL) << ownStart << ownEnd << "final node" << currentNode.start() * extractor_div_with << currentNode.end() * extractor_div_with; - //Check whether the item is contained - int bound = lowerBound(*currentNode, ownStart, ownEnd); + //Check whether the item is contained + int bound = lowerBound(*currentNode, ownStart, ownEnd); // qCDebug(UTIL) << "bound:" << bound << (KeyExtractor::extract(this->m_data[bound]) == *currentNode); - if(bound != -1 && KeyExtractor::extract(this->m_data[bound]) == *currentNode) { - //Got a match - m_match = bound; - m_matchingTo = *currentNode; - m_matchBound = ownEnd; - return; - }else{ - //Mismatch - goto boundsUp; - } - }else{ + if (bound != -1 && KeyExtractor::extract(this->m_data[bound]) == *currentNode) { + //Got a match + m_match = bound; + m_matchingTo = *currentNode; + m_matchBound = ownEnd; + return; + } else { + //Mismatch + goto boundsUp; + } + } else { // qCDebug(UTIL) << ownStart << ownEnd << "node" << currentNode.start() * extractor_div_with << currentNode.end() * extractor_div_with; - //This is not a final node, split up the search into the sub-nodes - typename TreeSet::Node leftNode = currentNode.leftChild(); - typename TreeSet::Node rightNode = currentNode.rightChild(); - Q_ASSERT(leftNode.isValid()); - Q_ASSERT(rightNode.isValid()); - - - Data2 leftLastItem = leftNode.lastItem(); - - int rightSearchStart = lowerBound(rightNode.firstItem(), ownStart, ownEnd); - if(rightSearchStart == -1) - rightSearchStart = ownEnd; - int leftSearchLast = lowerBound(leftLastItem, ownStart, rightSearchStart != -1 ? rightSearchStart : ownEnd); - if(leftSearchLast == -1) - leftSearchLast = rightSearchStart-1; - - bool recurseLeft = false; - if(leftSearchLast > (int)ownStart) { - recurseLeft = true; //There must be something in the range ownStart -> leftSearchLast that matches the range - }else if((int)ownStart == leftSearchLast) { - //Check if the one item item under leftSearchStart is contained in the range - Data2 leftFoundStartData = KeyExtractor::extract(this->m_data[ownStart]); - recurseLeft = leftFoundStartData < leftLastItem || leftFoundStartData == leftLastItem; - } + //This is not a final node, split up the search into the sub-nodes + typename TreeSet::Node leftNode = currentNode.leftChild(); + typename TreeSet::Node rightNode = currentNode.rightChild(); + Q_ASSERT(leftNode.isValid()); + Q_ASSERT(rightNode.isValid()); + + Data2 leftLastItem = leftNode.lastItem(); + + int rightSearchStart = lowerBound(rightNode.firstItem(), ownStart, ownEnd); + if (rightSearchStart == -1) + rightSearchStart = ownEnd; + int leftSearchLast = lowerBound(leftLastItem, ownStart, + rightSearchStart != -1 ? rightSearchStart : ownEnd); + if (leftSearchLast == -1) + leftSearchLast = rightSearchStart - 1; + + bool recurseLeft = false; + if (leftSearchLast > ( int )ownStart) { + recurseLeft = true; //There must be something in the range ownStart -> leftSearchLast that matches the range + } else if (( int )ownStart == leftSearchLast) { + //Check if the one item item under leftSearchStart is contained in the range + Data2 leftFoundStartData = KeyExtractor::extract(this->m_data[ownStart]); + recurseLeft = leftFoundStartData < leftLastItem || leftFoundStartData == leftLastItem; + } - bool recurseRight = false; - if(rightSearchStart < (int)ownEnd) - recurseRight = true; + bool recurseRight = false; + if (rightSearchStart < ( int )ownEnd) + recurseRight = true; - if(recurseLeft && recurseRight) { - //Push the right branch onto the stack, and work in the left one - boundStack.append( qMakePair( qMakePair( (uint)rightSearchStart, ownEnd ), rightNode) ); - } + if (recurseLeft && recurseRight) { + //Push the right branch onto the stack, and work in the left one + boundStack.append(qMakePair(qMakePair(( uint )rightSearchStart, ownEnd), rightNode)); + } - if(recurseLeft) { - currentNode = leftNode; - if(leftSearchLast != -1) - ownEnd = leftSearchLast+1; - }else if(recurseRight) { - currentNode = rightNode; - ownStart = rightSearchStart; - }else{ - goto boundsUp; - } + if (recurseLeft) { + currentNode = leftNode; + if (leftSearchLast != -1) + ownEnd = leftSearchLast + 1; + } else if (recurseRight) { + currentNode = rightNode; + ownStart = rightSearchStart; + } else { + goto boundsUp; } } } + } - ///Returns the first valid index that has an extracted data-value larger or equal to @p data. - ///Returns -1 if nothing is found. - int lowerBound(const Data2& data, int start, int end) { - int currentBound = -1; - while(1) { - if(start >= end) - return currentBound; - - int center = (start + end)/2; - - //Skip free items, since they cannot be used for ordering - for(; center < end; ) { - if(!Handler::isFree(this->m_data[center])) - break; - ++center; - } - - if(center == end) { - end = (start + end)/2; //No non-free items found in second half, so continue search in the other - }else{ - Data2 centerData = KeyExtractor::extract(this->m_data[center]); - //Even if the data equals we must continue searching to the left, since there may be multiple matching - if(data == centerData || data < centerData) { - currentBound = center; - end = (start + end)/2; - }else{ - //Continue search in second half - start = center+1; - } + ///Returns the first valid index that has an extracted data-value larger or equal to @p data. + ///Returns -1 if nothing is found. + int lowerBound(const Data2& data, int start, int end) + { + int currentBound = -1; + while (1) { + if (start >= end) + return currentBound; + + int center = (start + end) / 2; + + //Skip free items, since they cannot be used for ordering + for (; center < end;) { + if (!Handler::isFree(this->m_data[center])) + break; + ++center; + } + + if (center == end) { + end = (start + end) / 2; //No non-free items found in second half, so continue search in the other + } else { + Data2 centerData = KeyExtractor::extract(this->m_data[center]); + //Even if the data equals we must continue searching to the left, since there may be multiple matching + if (data == centerData || data < centerData) { + currentBound = center; + end = (start + end) / 2; + } else { + //Continue search in second half + start = center + 1; } } } + } - //Bounds that yet need to be matched. Always a range in the own vector, and a node that all items in the range are contained in - KDevVarLengthArray, typename TreeSet::Node > > boundStack; - TreeSet m_rhs; - int m_match = -1, m_matchBound; - Data2 m_matchingTo; - bool m_noFiltering; - }; - - ///Same as above, except that it visits all filtered items with a visitor, instead of iterating over them. - ///This is more efficient. The visiting is done directly from within the constructor. - template - class ConvenientEmbeddedSetTreeFilterVisitor : public ConvenientEmbeddedSetIterator { - public: - ConvenientEmbeddedSetTreeFilterVisitor() { - } + //Bounds that yet need to be matched. Always a range in the own vector, and a node that all items in the range are contained in + KDevVarLengthArray, typename TreeSet::Node>> boundStack; + TreeSet m_rhs; + int m_match = -1, m_matchBound; + Data2 m_matchingTo; + bool m_noFiltering; +}; + +///Same as above, except that it visits all filtered items with a visitor, instead of iterating over them. +///This is more efficient. The visiting is done directly from within the constructor. +template +class ConvenientEmbeddedSetTreeFilterVisitor : public ConvenientEmbeddedSetIterator +{ +public: + ConvenientEmbeddedSetTreeFilterVisitor() + { + } - typedef QPair, typename TreeSet::Node > Bounds; + typedef QPair, typename TreeSet::Node> Bounds; - struct Bound { - inline Bound(uint s, uint e, const typename TreeSet::Node& n) : start(s), end(e), node(n) { - } - Bound() { - } - uint start; - uint end; - typename TreeSet::Node node; - }; + struct Bound + { + inline Bound(uint s, uint e, const typename TreeSet::Node& n) : start(s) + , end(e) + , node(n) + { + } + Bound() + { + } + uint start; + uint end; + typename TreeSet::Node node; + }; - ///@param noFiltering whether the given input is pre-filtered. If this is true, base will be iterated without skipping any items. - ConvenientEmbeddedSetTreeFilterVisitor(Visitor& visitor, const ConvenientEmbeddedSetIterator& base, const TreeSet& rhs, bool noFiltering = false) : ConvenientEmbeddedSetIterator(base), m_visitor(visitor), m_rhs(rhs), m_noFiltering(noFiltering) { + ///@param noFiltering whether the given input is pre-filtered. If this is true, base will be iterated without skipping any items. + ConvenientEmbeddedSetTreeFilterVisitor(Visitor& visitor, const ConvenientEmbeddedSetIterator& base, + const TreeSet& rhs, + bool noFiltering = false) : ConvenientEmbeddedSetIterator(base) + , m_visitor(visitor) + , m_rhs(rhs) + , m_noFiltering(noFiltering) + { + + if (m_noFiltering) { + for (uint a = 0; a < this->m_dataSize; ++a) + visitor(this->m_data[a]); + + return; + } - if(m_noFiltering) { - for(uint a = 0; a < this->m_dataSize; ++a) - visitor(this->m_data[a]); + if (rhs.node().isValid()) { + //Correctly initialize the initial bounds + int ownStart = lowerBound(rhs.node().firstItem(), 0, this->m_dataSize); + if (ownStart == -1) return; - } + int ownEnd = lowerBound(rhs.node().lastItem(), ownStart, this->m_dataSize); + if (ownEnd == -1) + ownEnd = this->m_dataSize; + else + ownEnd += 1; - if(rhs.node().isValid()) { - //Correctly initialize the initial bounds - int ownStart = lowerBound(rhs.node().firstItem(), 0, this->m_dataSize); - if(ownStart == -1) - return; - int ownEnd = lowerBound(rhs.node().lastItem(), ownStart, this->m_dataSize); - if(ownEnd == -1) - ownEnd = this->m_dataSize; - else - ownEnd += 1; - - go( Bound((uint)ownStart, (uint)ownEnd, rhs.node()) ); - } + go(Bound(( uint )ownStart, ( uint )ownEnd, rhs.node())); } + } - private: - void go( Bound bound ) { - - KDevVarLengthArray bounds; - - while(true) { - if(bound.start >= bound.end) - goto nextBound; - - if(bound.node.isFinalNode()) { - //Check whether the item is contained - int b = lowerBound(*bound.node, bound.start, bound.end); - if(b != -1) { - const Data2& matchTo(*bound.node); - - if(KeyExtractor::extract(this->m_data[b]) == matchTo) { - while(1) { - m_visitor(this->m_data[b]); - b = this->firstValidItem(b+1, this->m_dataSize); - if(b < (int)this->m_dataSize && b != -1 && KeyExtractor::extract(this->m_data[b]) == matchTo) - continue; - else - break; - } +private: + void go(Bound bound) + { + + KDevVarLengthArray bounds; + + while (true) { + if (bound.start >= bound.end) + goto nextBound; + + if (bound.node.isFinalNode()) { + //Check whether the item is contained + int b = lowerBound(*bound.node, bound.start, bound.end); + if (b != -1) { + const Data2& matchTo(*bound.node); + + if (KeyExtractor::extract(this->m_data[b]) == matchTo) { + while (1) { + m_visitor(this->m_data[b]); + b = this->firstValidItem(b + 1, this->m_dataSize); + if (b < ( int )this->m_dataSize && b != -1 && + KeyExtractor::extract(this->m_data[b]) == matchTo) + continue; + else + break; } } - goto nextBound; - }else{ - //This is not a final node, split up the search into the sub-nodes - typename TreeSet::Node leftNode = bound.node.leftChild(); - typename TreeSet::Node rightNode = bound.node.rightChild(); - Q_ASSERT(leftNode.isValid()); - Q_ASSERT(rightNode.isValid()); - - - Data2 leftLastItem = leftNode.lastItem(); - - int rightSearchStart = lowerBound(rightNode.firstItem(), bound.start, bound.end); - if(rightSearchStart == -1) - rightSearchStart = bound.end; - int leftSearchLast = lowerBound(leftLastItem, bound.start, rightSearchStart != -1 ? rightSearchStart : bound.end); - if(leftSearchLast == -1) - leftSearchLast = rightSearchStart-1; - - bool recurseLeft = false; - if(leftSearchLast > (int)bound.start) { - recurseLeft = true; //There must be something in the range bound.start -> leftSearchLast that matches the range - }else if((int)bound.start == leftSearchLast) { - //Check if the one item item under leftSearchStart is contained in the range - Data2 leftFoundStartData = KeyExtractor::extract(this->m_data[bound.start]); - recurseLeft = leftFoundStartData < leftLastItem || leftFoundStartData == leftLastItem; - } + } + goto nextBound; + } else { + //This is not a final node, split up the search into the sub-nodes + typename TreeSet::Node leftNode = bound.node.leftChild(); + typename TreeSet::Node rightNode = bound.node.rightChild(); + Q_ASSERT(leftNode.isValid()); + Q_ASSERT(rightNode.isValid()); + + Data2 leftLastItem = leftNode.lastItem(); + + int rightSearchStart = lowerBound(rightNode.firstItem(), bound.start, bound.end); + if (rightSearchStart == -1) + rightSearchStart = bound.end; + int leftSearchLast = lowerBound(leftLastItem, bound.start, + rightSearchStart != -1 ? rightSearchStart : bound.end); + if (leftSearchLast == -1) + leftSearchLast = rightSearchStart - 1; + + bool recurseLeft = false; + if (leftSearchLast > ( int )bound.start) { + recurseLeft = true; //There must be something in the range bound.start -> leftSearchLast that matches the range + } else if (( int )bound.start == leftSearchLast) { + //Check if the one item item under leftSearchStart is contained in the range + Data2 leftFoundStartData = KeyExtractor::extract(this->m_data[bound.start]); + recurseLeft = leftFoundStartData < leftLastItem || leftFoundStartData == leftLastItem; + } - bool recurseRight = false; - if(rightSearchStart < (int)bound.end) - recurseRight = true; - - if(recurseLeft && recurseRight) - bounds.append( Bound(rightSearchStart, bound.end, rightNode) ); - - if(recurseLeft) { - bound.node = leftNode; - if(leftSearchLast != -1) - bound.end = leftSearchLast+1; - }else if(recurseRight) { - bound.node = rightNode; - bound.start = rightSearchStart; - }else{ - goto nextBound; - } - continue; + bool recurseRight = false; + if (rightSearchStart < ( int )bound.end) + recurseRight = true; + + if (recurseLeft && recurseRight) + bounds.append(Bound(rightSearchStart, bound.end, rightNode)); + + if (recurseLeft) { + bound.node = leftNode; + if (leftSearchLast != -1) + bound.end = leftSearchLast + 1; + } else if (recurseRight) { + bound.node = rightNode; + bound.start = rightSearchStart; + } else { + goto nextBound; } - nextBound: - if(bounds.isEmpty()) { - return; - }else{ - bound = bounds.back(); - bounds.pop_back(); - } + continue; + } +nextBound: + if (bounds.isEmpty()) { + return; + } else { + bound = bounds.back(); + bounds.pop_back(); } } + } - ///Returns the first valid index that has an extracted data-value larger or equal to @p data. - ///Returns -1 if nothing is found. - int lowerBound(const Data2& data, int start, int end) { - int currentBound = -1; - while(1) { - if(start >= end) - return currentBound; - - int center = (start + end)/2; - - //Skip free items, since they cannot be used for ordering - for(; center < end; ) { - if(!Handler::isFree(this->m_data[center])) - break; - ++center; - } - - if(center == end) { - end = (start + end)/2; //No non-free items found in second half, so continue search in the other - }else{ - Data2 centerData = KeyExtractor::extract(this->m_data[center]); - //Even if the data equals we must continue searching to the left, since there may be multiple matching - if(data == centerData || data < centerData) { - currentBound = center; - end = (start + end)/2; - }else{ - //Continue search in second half - start = center+1; - } + ///Returns the first valid index that has an extracted data-value larger or equal to @p data. + ///Returns -1 if nothing is found. + int lowerBound(const Data2& data, int start, int end) + { + int currentBound = -1; + while (1) { + if (start >= end) + return currentBound; + + int center = (start + end) / 2; + + //Skip free items, since they cannot be used for ordering + for (; center < end;) { + if (!Handler::isFree(this->m_data[center])) + break; + ++center; + } + + if (center == end) { + end = (start + end) / 2; //No non-free items found in second half, so continue search in the other + } else { + Data2 centerData = KeyExtractor::extract(this->m_data[center]); + //Even if the data equals we must continue searching to the left, since there may be multiple matching + if (data == centerData || data < centerData) { + currentBound = center; + end = (start + end) / 2; + } else { + //Continue search in second half + start = center + 1; } } } - - //Bounds that yet need to be matched. Always a range in the own vector, and a node that all items in the range are contained in - Visitor& m_visitor; - TreeSet m_rhs; - bool m_noFiltering; - }; - - template - ConvenientEmbeddedSetIterator ConstantConvenientEmbeddedSet::iterator() const { - return ConvenientEmbeddedSetIterator(m_data, m_dataSize, m_centralFreeItem); } - ///This is a simple set implementation based on the embedded free tree algorithms. - ///The core advantage of the whole thing is that the wole set is represented by a consecutive - ///memory-area, and thus can be stored or copied using a simple memcpy. - ///However in many cases it's better using the algorithms directly in such cases. - /// - ///However even for normal tasks this implementation does have some advantages over std::set: - ///- Many times faster iteration through contained data - ///- Lower memory-usage if the objects are small, since there is no heap allocation overhead - ///- Can be combined with other embedded-free-list based sets using algorithms in ConstantConvenientEmbeddedSet - ///Disadvantages: - ///- Significantly slower insertion - - template - class ConvenientFreeListSet { - public: - - typedef ConvenientEmbeddedSetIterator Iterator; + //Bounds that yet need to be matched. Always a range in the own vector, and a node that all items in the range are contained in + Visitor& m_visitor; + TreeSet m_rhs; + bool m_noFiltering; +}; - ConvenientFreeListSet() { - } +template +ConvenientEmbeddedSetIterator ConstantConvenientEmbeddedSet::iterator() const +{ + return ConvenientEmbeddedSetIterator(m_data, m_dataSize, m_centralFreeItem); +} - ///Re-construct a set from its components - ConvenientFreeListSet(int centralFreeItem, QVector data) : m_data(data), m_centralFree(centralFreeItem) { - } +///This is a simple set implementation based on the embedded free tree algorithms. +///The core advantage of the whole thing is that the wole set is represented by a consecutive +///memory-area, and thus can be stored or copied using a simple memcpy. +///However in many cases it's better using the algorithms directly in such cases. +/// +///However even for normal tasks this implementation does have some advantages over std::set: +///- Many times faster iteration through contained data +///- Lower memory-usage if the objects are small, since there is no heap allocation overhead +///- Can be combined with other embedded-free-list based sets using algorithms in ConstantConvenientEmbeddedSet +///Disadvantages: +///- Significantly slower insertion + +template +class ConvenientFreeListSet +{ +public: + + typedef ConvenientEmbeddedSetIterator Iterator; + + ConvenientFreeListSet() + { + } - ///You can use this to store the set to disk and later give it together with data() to the constructor, thus reconstructing it. - int centralFreeItem() const { - return m_centralFree; - } + ///Re-construct a set from its components + ConvenientFreeListSet(int centralFreeItem, QVector data) : m_data(data) + , m_centralFree(centralFreeItem) + { + } - const QVector& data() const { - return m_data; - } + ///You can use this to store the set to disk and later give it together with data() to the constructor, thus reconstructing it. + int centralFreeItem() const + { + return m_centralFree; + } - void insert(const Data& item) { - if(contains(item)) - return; - KDevelop::EmbeddedTreeAddItem add(m_data.data(), m_data.size(), m_centralFree, item); + const QVector& data() const + { + return m_data; + } - if((int)add.newItemCount() != (int)m_data.size()) { - QVector newData; - newData.resize(add.newItemCount()); - add.transferData(newData.data(), newData.size()); - m_data = newData; - } - } + void insert(const Data& item) + { + if (contains(item)) + return; + KDevelop::EmbeddedTreeAddItem add(m_data.data(), m_data.size(), m_centralFree, item); + + if (( int )add.newItemCount() != ( int )m_data.size()) { + QVector newData; + newData.resize(add.newItemCount()); + add.transferData(newData.data(), newData.size()); + m_data = newData; + } + } - Iterator iterator() const { - return Iterator(m_data.data(), m_data.size(), m_centralFree); - } + Iterator iterator() const + { + return Iterator(m_data.data(), m_data.size(), m_centralFree); + } - bool contains(const Data& item) const { - KDevelop::EmbeddedTreeAlgorithms alg(m_data.data(), m_data.size(), m_centralFree); - return alg.indexOf(Data(item)) != -1; - } + bool contains(const Data& item) const + { + KDevelop::EmbeddedTreeAlgorithms alg(m_data.data(), m_data.size(), m_centralFree); + return alg.indexOf(Data(item)) != -1; + } - void remove(const Data& item) { - KDevelop::EmbeddedTreeRemoveItem remove(m_data.data(), m_data.size(), m_centralFree, item); + void remove(const Data& item) + { + KDevelop::EmbeddedTreeRemoveItem remove(m_data.data(), m_data.size(), m_centralFree, item); - if((int)remove.newItemCount() != (int)m_data.size()) { - QVector newData; - newData.resize(remove.newItemCount()); - remove.transferData(newData.data(), newData.size()); - m_data = newData; - } - } + if (( int )remove.newItemCount() != ( int )m_data.size()) { + QVector newData; + newData.resize(remove.newItemCount()); + remove.transferData(newData.data(), newData.size()); + m_data = newData; + } + } - private: - int m_centralFree = -1; - QVector m_data; - }; +private: + int m_centralFree = -1; + QVector m_data; +}; } #endif diff --git a/kdevplatform/util/dbus_socket_transformer/main.cpp b/kdevplatform/util/dbus_socket_transformer/main.cpp index 28247c3eae..2c6f2c3e84 100644 --- a/kdevplatform/util/dbus_socket_transformer/main.cpp +++ b/kdevplatform/util/dbus_socket_transformer/main.cpp @@ -1,415 +1,393 @@ /*************************************************************************** * Copyright 2011 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 Library 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 #include #include #include #include #include -#include +#include #include #include #include #include #include #include #ifndef HAVE_MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif /** * The goal of this utility is transforming the abstract unix-socket which is used by dbus * into a TCP socket which can be forwarded to a target machine by ssh tunneling, and then on * the target machine back into an abstract unix socket. - * + * * This tool basically works similar to the "socat" utility, except that it works properly * for this special case. It is merely responsible for the transformation between abstract unix * sockets and tcp sockets. - * + * * Furthermore, this tool makes the 'EXTERNAL' dbus authentication mechanism work even across * machines with different user IDs. - * + * * This is how the EXTERNAL mechanism works (I found this in a comment of some ruby dbus library): * Take the user id (eg integer 1000) make a string out of it "1000", take * each character and determin hex value "1" => 0x31, "0" => 0x30. You * obtain for "1000" => 31303030 This is what the server is expecting. * Why? I dunno. How did I come to that conclusion? by looking at rbus * code. I have no idea how he found that out. - * + * * The dbus client performs the EXTERNAL authentication by sending "AUTH EXTERNAL 31303030\r\n" once * after opening the connection, so we can "repair" the authentication by overwriting the token in that * string through the correct one. * */ const bool debug = false; /** * Returns the valid dbus EXTERNAL authentication token for the current user (see above) * */ -std::string getAuthToken() { +std::string getAuthToken() +{ // Get uid int uid = getuid(); - + std::ostringstream uidStream; uidStream << uid; - + std::string uidStr = uidStream.str(); - + const char hexdigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; std::ostringstream hexStream; for (unsigned int i = 0; i < uidStr.size(); ++i) { - auto byte = (unsigned char)uidStr[i]; + auto byte = ( unsigned char )uidStr[i]; hexStream << hexdigits[byte >> 4] << hexdigits[byte & 0x0f]; } - + return hexStream.str(); } /** * Shuffles all data between the two file-descriptors until one of them fails or reaches EOF. * */ void shuffleBetweenStreams(int side1, int side2, bool fixSide1AuthToken) { char buffer[1000]; char buffer2[1000]; - + // Set non-blocking mode - int opts = fcntl(side1,F_GETFL); + int opts = fcntl(side1, F_GETFL); opts |= O_NONBLOCK; fcntl(side1, F_SETFL, opts); - opts = fcntl(side2,F_GETFL); - opts |= O_NONBLOCK; + opts = fcntl(side2, F_GETFL); + opts |= O_NONBLOCK; fcntl(side2, F_SETFL, opts); - - while(true) - { + + while (true) { int r1 = read(side1, buffer, 500); // We read less than 1000, so we have same additional space when changing the auth token int r2 = read(side2, buffer2, 500); - if(r1 < -1 || r1 == 0) - { - if(debug) + if (r1 < -1 || r1 == 0) { + if (debug) std::cerr << "stream 1 failed: " << r1 << std::endl; return; } - if(r2 < -1 || r2 == 0) - { - if(debug) + if (r2 < -1 || r2 == 0) { + if (debug) std::cerr << "stream 2 failed: " << r2 << std::endl; return; } - - if(r1 > 0) - { - if(debug) + + if (r1 > 0) { + if (debug) std::cerr << "transferring " << r1 << " from 1 to 2" << std::endl; - if(fixSide1AuthToken) - { - if(r1 > 15 && memcmp(buffer, "\0AUTH EXTERNAL ", 15) == 0) - { + if (fixSide1AuthToken) { + if (r1 > 15 && memcmp(buffer, "\0AUTH EXTERNAL ", 15) == 0) { int endPos = -1; - for(int i = 15; i < r1; ++i) - { - if(buffer[i] == '\r') - { + for (int i = 15; i < r1; ++i) { + if (buffer[i] == '\r') { endPos = i; break; } } - if(endPos != -1) - { + + if (endPos != -1) { std::string oldToken = std::string(buffer + 15, endPos - 15); std::string newToken = getAuthToken(); - + int difference = newToken.size() - oldToken.size(); r1 += difference; assert(r1 > 0 && r1 <= 1000); memmove(buffer + endPos + difference, buffer + endPos, r1 - difference - endPos); memcpy(buffer + 15, newToken.data(), newToken.size()); assert(buffer[endPos + difference] == '\r'); - assert(buffer[endPos + difference - 1] == newToken[newToken.size()-1]); - }else{ + assert(buffer[endPos + difference - 1] == newToken[newToken.size() - 1]); + } else { std::cout << "could not fix auth token, not enough data available" << std::endl; } - }else{ + } else { std::cout << "could not fix auth token" << std::endl; } fixSide1AuthToken = false; } - - opts = fcntl(side2,F_GETFL); - opts ^= O_NONBLOCK; + + opts = fcntl(side2, F_GETFL); + opts ^= O_NONBLOCK; fcntl(side2, F_SETFL, opts); - + int w2 = send(side2, buffer, r1, MSG_NOSIGNAL); - - if(w2 < 0) - { - if(debug) + + if (w2 < 0) { + if (debug) std::cerr << "writing to side 2 failed, ending: " << w2 << std::endl; return; } assert(w2 == r1); - - opts = fcntl(side2,F_GETFL); - opts |= O_NONBLOCK; + + opts = fcntl(side2, F_GETFL); + opts |= O_NONBLOCK; fcntl(side2, F_SETFL, opts); } - if(r2 > 0) - { - if(debug) + if (r2 > 0) { + if (debug) std::cerr << "transferring " << r2 << " from 2 to 1" << std::endl; - opts = fcntl(side1,F_GETFL); - opts ^= O_NONBLOCK; + opts = fcntl(side1, F_GETFL); + opts ^= O_NONBLOCK; fcntl(side1, F_SETFL, opts); - + int w1 = send(side1, buffer2, r2, MSG_NOSIGNAL); - - if(w1 < 0) - { - if(debug) + + if (w1 < 0) { + if (debug) std::cerr << "writing to side 1 failed, ending: " << w1 << std::endl; return; } assert(w1 == r2); - - opts = fcntl(side1,F_GETFL); - opts |= O_NONBLOCK; + + opts = fcntl(side1, F_GETFL); + opts |= O_NONBLOCK; fcntl(side1, F_SETFL, opts); } usleep(1000); } } int main(int argc, char** argv) { int serverfd; - - if(argc < 2) - { + + if (argc < 2) { std::cerr << "need arguments:" << std::endl; - std::cerr << "[port] - Open a server on this TCP port and forward connections to the local DBUS session" - " (the DBUS_SESSION_BUS_ADDRESS environment variable must be set)"; - std::cerr << "[port] [fake dbus path] - Open a server on the fake dbus path and forward connections to the given local TCP port"; + std::cerr << + "[port] - Open a server on this TCP port and forward connections to the local DBUS session" + " (the DBUS_SESSION_BUS_ADDRESS environment variable must be set)"; + std::cerr << + "[port] [fake dbus path] - Open a server on the fake dbus path and forward connections to the given local TCP port"; std::cerr << "" - "The last argument may be the --bind-only option, in which case the application only tries to" - "open the server, but does not wait for clients to connect. This is useful to test whether the" - "server port/path is available."; + "The last argument may be the --bind-only option, in which case the application only tries to" + "open the server, but does not wait for clients to connect. This is useful to test whether the" + "server port/path is available."; return 10; } - + bool waitForClients = true; - - if(std::string(argv[argc-1]) == "--bind-only") - { + + if (std::string(argv[argc - 1]) == "--bind-only") { waitForClients = false; argc -= 1; } std::string dbusAddress(getenv("DBUS_SESSION_BUS_ADDRESS")); std::string path; - - if(argc == 2) - { - if(waitForClients && debug) - std::cout << "forwarding from the local TCP port " << argv[1] << " to the local DBUS session at " << dbusAddress.data() << std::endl; - - if(dbusAddress.empty()) - { + + if (argc == 2) { + if (waitForClients && debug) + std::cout << "forwarding from the local TCP port " << argv[1] << " to the local DBUS session at " << + dbusAddress.data() << std::endl; + + if (dbusAddress.empty()) { std::cerr << "The DBUS_SESSION_BUS_ADDRESS environment variable is not set" << std::endl; return 1; } - + // Open a TCP server - + std::string abstractPrefix("unix:abstract="); - - if(dbusAddress.substr(0, abstractPrefix.size()) != abstractPrefix) - { - std::cerr << "DBUS_SESSION_BUS_ADDRESS does not seem to use an abstract unix domain socket as expected" << std::endl; + + if (dbusAddress.substr(0, abstractPrefix.size()) != abstractPrefix) { + std::cerr << "DBUS_SESSION_BUS_ADDRESS does not seem to use an abstract unix domain socket as expected" << + std::endl; return 2; } - + path = dbusAddress.substr(abstractPrefix.size(), dbusAddress.size() - abstractPrefix.size()); - if(path.find(",guid=") != std::string::npos) + if (path.find(",guid=") != std::string::npos) path = path.substr(0, path.find(",guid=")); - + // Mark it as an abstract unix domain socket path = path; - + serverfd = socket(AF_INET, SOCK_STREAM, 0); - - if (serverfd < 0) - { - if(waitForClients) + + if (serverfd < 0) { + if (waitForClients) std::cerr << "ERROR opening server socket" << std::endl; return 3; } - + int portno = atoi(argv[1]); - + sockaddr_in server_addr; - + memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(portno); - - if (bind(serverfd, (struct sockaddr *) &server_addr, - sizeof(server_addr)) < 0) - { - if(waitForClients) + + if (bind(serverfd, ( struct sockaddr* ) &server_addr, + sizeof(server_addr)) < 0) { + if (waitForClients) std::cerr << "ERROR opening the server" << std::endl; return 7; } - }else if(argc == 3) - { - if(waitForClients && debug) - std::cout << "forwarding from the local abstract unix domain socket " << argv[2] << " to the local TCP port " << argv[1] << std::endl; + } else if (argc == 3) { + if (waitForClients && debug) + std::cout << "forwarding from the local abstract unix domain socket " << argv[2] << + " to the local TCP port " << argv[1] << std::endl; // Open a unix domain socket server serverfd = socket(AF_UNIX, SOCK_STREAM, 0); - - if (serverfd < 0) - { - if(waitForClients) + + if (serverfd < 0) { + if (waitForClients) std::cerr << "ERROR opening server socket" << std::endl; return 3; } path = std::string(argv[2]); sockaddr_un serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; serv_addr.sun_path[0] = '\0'; // Mark as an abstract socket - strcpy(serv_addr.sun_path+1, path.data()); - - if(debug) + strcpy(serv_addr.sun_path + 1, path.data()); + + if (debug) std::cout << "opening at " << path.data() << std::endl; - - if (bind(serverfd,(sockaddr *) &serv_addr, sizeof (serv_addr.sun_family) + 1 + path.length()) < 0) - { - if(waitForClients) + + if (bind(serverfd, ( sockaddr* ) &serv_addr, sizeof (serv_addr.sun_family) + 1 + path.length()) < 0) { + if (waitForClients) std::cerr << "ERROR opening the server" << std::endl; return 7; } - }else{ + } else { std::cerr << "Wrong arguments"; return 1; } - + listen(serverfd, 10); - while(waitForClients) - { - if(debug) + while (waitForClients) { + if (debug) std::cerr << "waiting for client" << std::endl; sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); - int connectedclientsockfd = accept(serverfd, - (struct sockaddr *) &cli_addr, - &clilen); - - if(connectedclientsockfd < 0) - { + int connectedclientsockfd = accept(serverfd, + ( struct sockaddr* ) &cli_addr, + &clilen); + + if (connectedclientsockfd < 0) { std::cerr << "ERROR on accept" << std::endl; return 8; } - - if(debug) + + if (debug) std::cerr << "got client" << std::endl; int sockfd; - + int addrSize; sockaddr* useAddr = nullptr; sockaddr_un serv_addru; sockaddr_in serv_addrin; - - if(argc == 2) - { + + if (argc == 2) { sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sockfd < 0) - { + if (sockfd < 0) { std::cerr << "ERROR opening socket" << std::endl; return 3; } memset(&serv_addru, 0, sizeof(serv_addru)); serv_addru.sun_family = AF_UNIX; serv_addru.sun_path[0] = '\0'; // Mark as an abstract socket - strcpy(serv_addru.sun_path+1, path.data()); + strcpy(serv_addru.sun_path + 1, path.data()); addrSize = sizeof (serv_addru.sun_family) + 1 + path.size(); - useAddr = (sockaddr*)&serv_addru; - - if(debug) + useAddr = ( sockaddr* )&serv_addru; + + if (debug) std::cout << "connecting to " << path.data() << std::endl; - }else{ + } else { sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) - { + if (sockfd < 0) { std::cerr << "ERROR opening socket" << std::endl; return 3; } int port = atoi(argv[1]); - hostent *server = gethostbyname("localhost"); - if(server == nullptr) - { + hostent* server = gethostbyname("localhost"); + if (server == nullptr) { std::cerr << "failed to get server" << std::endl; return 5; } memset(&serv_addrin, 0, sizeof(serv_addrin)); serv_addrin.sin_family = AF_INET; serv_addrin.sin_addr.s_addr = INADDR_ANY; serv_addrin.sin_port = htons(port); memcpy(&serv_addrin.sin_addr.s_addr, server->h_addr, server->h_length); addrSize = sizeof (serv_addrin); - useAddr = (sockaddr*)&serv_addrin; - - if(debug) + useAddr = ( sockaddr* )&serv_addrin; + + if (debug) std::cout << "connecting to port " << port << std::endl; } - - if (connect(sockfd, useAddr, addrSize) < 0) - { + + if (connect(sockfd, useAddr, addrSize) < 0) { int res = errno; - if(res == ECONNREFUSED) + if (res == ECONNREFUSED) std::cerr << "ERROR while connecting: connection refused" << std::endl; - else if(res == ENOENT) + else if (res == ENOENT) std::cerr << "ERROR while connecting: no such file or directory" << std::endl; else std::cerr << "ERROR while connecting" << std::endl; return 5; } shuffleBetweenStreams(connectedclientsockfd, sockfd, argc == 2); close(sockfd); close(connectedclientsockfd); } return 0; } diff --git a/kdevplatform/util/duchainify/main.cpp b/kdevplatform/util/duchainify/main.cpp index e236107c4d..ed49d09f57 100644 --- a/kdevplatform/util/duchainify/main.cpp +++ b/kdevplatform/util/duchainify/main.cpp @@ -1,319 +1,326 @@ /*************************************************************************** * Copyright 2009 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 Library 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 "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include - -bool verbose=false, warnings=false; +bool verbose = false, warnings = false; using namespace KDevelop; void messageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { Q_UNUSED(context); switch (type) { - case QtInfoMsg: // fall-through - case QtDebugMsg: - if(verbose) - std::cerr << qPrintable(msg) << std::endl; - break; - case QtWarningMsg: - if(warnings) - std::cerr << qPrintable(msg) << std::endl; - break; - case QtCriticalMsg: + case QtInfoMsg: // fall-through + case QtDebugMsg: + if (verbose) std::cerr << qPrintable(msg) << std::endl; - break; - case QtFatalMsg: + break; + case QtWarningMsg: + if (warnings) std::cerr << qPrintable(msg) << std::endl; - abort(); + break; + case QtCriticalMsg: + std::cerr << qPrintable(msg) << std::endl; + break; + case QtFatalMsg: + std::cerr << qPrintable(msg) << std::endl; + abort(); } } - -Manager::Manager(QCommandLineParser* args) : m_total(0), m_args(args), m_allFilesAdded(0) +Manager::Manager(QCommandLineParser* args) : m_total(0) + , m_args(args) + , m_allFilesAdded(0) { } void Manager::init() { - if(m_args->positionalArguments().isEmpty()) { + if (m_args->positionalArguments().isEmpty()) { std::cerr << "Need file or directory to duchainify" << std::endl; QCoreApplication::exit(1); } uint features = TopDUContext::VisibleDeclarationsAndContexts; - if(m_args->isSet(QStringLiteral("features"))) - { + if (m_args->isSet(QStringLiteral("features"))) { QString featuresStr = m_args->value(QStringLiteral("features")); - if(featuresStr == QLatin1String("visible-declarations")) - { + if (featuresStr == QLatin1String("visible-declarations")) { features = TopDUContext::VisibleDeclarationsAndContexts; - } - else if(featuresStr == QLatin1String("all-declarations")) - { + } else if (featuresStr == QLatin1String("all-declarations")) { features = TopDUContext::AllDeclarationsAndContexts; - } - else if(featuresStr == QLatin1String("all-declarations-and-uses")) - { + } else if (featuresStr == QLatin1String("all-declarations-and-uses")) { features = TopDUContext::AllDeclarationsContextsAndUses; - } - else if(featuresStr == QLatin1String("all-declarations-and-uses-and-AST")) - { + } else if (featuresStr == QLatin1String("all-declarations-and-uses-and-AST")) { features = TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST; - } - else if(featuresStr == QLatin1String("empty")) - { + } else if (featuresStr == QLatin1String("empty")) { features = TopDUContext::Empty; - } - else if(featuresStr == QLatin1String("simplified-visible-declarations")) - { + } else if (featuresStr == QLatin1String("simplified-visible-declarations")) { features = TopDUContext::SimplifiedVisibleDeclarationsAndContexts; - } - else{ + } else { std::cerr << "Wrong feature-string given\n"; QCoreApplication::exit(2); return; } } - if(m_args->isSet(QStringLiteral("force-update"))) + if (m_args->isSet(QStringLiteral("force-update"))) features |= TopDUContext::ForceUpdate; - if(m_args->isSet(QStringLiteral("force-update-recursive"))) + if (m_args->isSet(QStringLiteral("force-update-recursive"))) features |= TopDUContext::ForceUpdateRecursive; - if(m_args->isSet(QStringLiteral("threads"))) - { + if (m_args->isSet(QStringLiteral("threads"))) { bool ok = false; int count = m_args->value(QStringLiteral("threads")).toInt(&ok); ICore::self()->languageController()->backgroundParser()->setThreadCount(count); - if(!ok) { + if (!ok) { std::cerr << "bad thread count\n"; QCoreApplication::exit(3); return; } } // quit when everything is done // background parser emits hideProgress() signal in two situations: // when everything is done and when bgparser is suspended // later doesn't happen in duchain, so just rely on hideProgress() // and quit when it's emitted - connect(ICore::self()->languageController()->backgroundParser(), &BackgroundParser::hideProgress, this, &Manager::finish); + connect( + ICore::self()->languageController()->backgroundParser(), &BackgroundParser::hideProgress, this, + &Manager::finish); foreach (const auto& file, m_args->positionalArguments()) { - addToBackgroundParser(file, (TopDUContext::Features)features); + addToBackgroundParser(file, ( TopDUContext::Features )features); } + m_allFilesAdded = 1; - if ( m_total ) { + if (m_total) { std::cerr << "Added " << m_total << " files to the background parser" << std::endl; const int threads = ICore::self()->languageController()->backgroundParser()->threadCount(); std::cerr << "parsing with " << threads << " threads" << std::endl; ICore::self()->languageController()->backgroundParser()->parseDocuments(); } else { std::cerr << "no files added to the background parser" << std::endl; QCoreApplication::exit(0); return; } } void Manager::updateReady(const IndexedString& url, const ReferencedTopDUContext& topContext) { - qDebug() << "finished" << url.toUrl().toLocalFile() << "success: " << (bool)topContext; + qDebug() << "finished" << url.toUrl().toLocalFile() << "success: " << ( bool )topContext; m_waiting.remove(url.toUrl()); std::cerr << "processed " << (m_total - m_waiting.size()) << " out of " << m_total << "\n"; dump(topContext); } void Manager::dump(const ReferencedTopDUContext& topContext) { if (!topContext) { return; } QTextStream stream(stdout); std::cerr << "\n"; if (m_args->isSet(QStringLiteral("dump-definitions"))) { DUChainReadLocker lock; std::cerr << "Definitions:" << std::endl; DUChain::definitions()->dump(stream); std::cerr << std::endl; } if (m_args->isSet(QStringLiteral("dump-symboltable"))) { DUChainReadLocker lock; std::cerr << "PersistentSymbolTable:" << std::endl; PersistentSymbolTable::self().dump(stream); std::cerr << std::endl; } DUChainDumper::Features features; if (m_args->isSet(QStringLiteral("dump-context"))) { features |= DUChainDumper::DumpContext; } if (m_args->isSet(QStringLiteral("dump-errors"))) { features |= DUChainDumper::DumpProblems; } if (auto depth = m_args->value(QStringLiteral("dump-depth")).toInt()) { DUChainReadLocker lock; std::cerr << "Context:" << std::endl; DUChainDumper dumpChain(features); dumpChain.dump(topContext, depth); } if (m_args->isSet(QStringLiteral("dump-graph"))) { DUChainReadLocker lock; DumpDotGraph dumpGraph; const QString dotOutput = dumpGraph.dotGraph(topContext); std::cout << qPrintable(dotOutput) << std::endl; } if (m_args->isSet(QStringLiteral("dump-imported-errors"))) { DUChainReadLocker lock; - foreach(const auto& import, topContext->importedParentContexts()) { + foreach (const auto& import, topContext->importedParentContexts()) { auto top = dynamic_cast(import.indexedContext().context()); if (top && top != topContext && !top->problems().isEmpty()) { DUChainDumper dumpChain(DUChainDumper::DumpProblems); dumpChain.dump(top, 0); } } } } void Manager::addToBackgroundParser(const QString& path, TopDUContext::Features features) { QFileInfo info(path); - if(info.isFile()) - { + if (info.isFile()) { qDebug() << "adding file" << path; QUrl pathUrl = QUrl::fromLocalFile(info.canonicalFilePath()); m_waiting << pathUrl; ++m_total; KDevelop::DUChain::self()->updateContextForUrl(KDevelop::IndexedString(pathUrl), features, this); - }else if(info.isDir()) - { + } else if (info.isDir()) { QDirIterator contents(path); - while(contents.hasNext()) { + while (contents.hasNext()) { QString newPath = contents.next(); - if(!newPath.endsWith(QLatin1Char('.'))) + if (!newPath.endsWith(QLatin1Char('.'))) addToBackgroundParser(newPath, features); } } } -QSet< QUrl > Manager::waiting() +QSet Manager::waiting() { return m_waiting; } void Manager::finish() { std::cerr << "ready" << std::endl; QApplication::quit(); } using namespace KDevelop; int main(int argc, char** argv) { QApplication app(argc, argv); - KAboutData aboutData( QStringLiteral("duchainify"), i18n( "duchainify" ), - QStringLiteral("1"), i18n("DUChain builder application"), KAboutLicense::GPL, - i18n( "(c) 2009 David Nolden" ), QString(), QStringLiteral("http://www.kdevelop.org") ); + KAboutData aboutData(QStringLiteral("duchainify"), i18n("duchainify"), + QStringLiteral("1"), i18n("DUChain builder application"), KAboutLicense::GPL, + i18n("(c) 2009 David Nolden"), QString(), QStringLiteral("http://www.kdevelop.org")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addPositionalArgument(QStringLiteral("paths"), i18n("file or directory"), QStringLiteral("[PATH...]")); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("w"), QStringLiteral("warnings")}, i18n("Show warnings")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("V"), QStringLiteral("verbose")}, i18n("Show warnings and debug output")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("u"), QStringLiteral("force-update")}, i18n("Enforce an update of the top-contexts corresponding to the given files")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("r"), QStringLiteral("force-update-recursive")}, i18n("Enforce an update of the top-contexts corresponding to the given files and all included files")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("t"), QStringLiteral("threads")}, i18n("Number of threads to use"), QStringLiteral("count")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("f"), QStringLiteral("features")}, i18n("Features to build. Options: empty, simplified-visible-declarations, visible-declarations (default), all-declarations, all-declarations-and-uses, all-declarations-and-uses-and-AST"), QStringLiteral("features")}); - - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-context")}, i18n("Print complete Definition-Use Chain on successful parse")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-definitions")}, i18n("Print complete DUChain Definitions repository on successful parse")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-symboltable")}, i18n("Print complete DUChain PersistentSymbolTable repository on successful parse")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-depth")}, i18n("Number defining the maximum depth where declaration details are printed"), QStringLiteral("depth")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-graph")}, i18n("Dump DUChain graph (in .dot format)")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("d"), QStringLiteral("dump-errors")}, i18n("Print problems encountered during parsing")}); - parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-imported-errors")}, i18n("Recursively dump errors from imported contexts.")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("w"), QStringLiteral("warnings")}, + i18n("Show warnings")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("V"), QStringLiteral("verbose")}, + i18n("Show warnings and debug output")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("u"), QStringLiteral("force-update")}, + i18n("Enforce an update of the top-contexts corresponding to the given files")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("r"), QStringLiteral( + "force-update-recursive")}, + i18n( + "Enforce an update of the top-contexts corresponding to the given files and all included files")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("t"), QStringLiteral("threads")}, + i18n("Number of threads to use"), QStringLiteral("count")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("f"), QStringLiteral("features")}, + i18n( + "Features to build. Options: empty, simplified-visible-declarations, visible-declarations (default), all-declarations, all-declarations-and-uses, all-declarations-and-uses-and-AST"), + QStringLiteral("features")}); + + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-context")}, + i18n("Print complete Definition-Use Chain on successful parse")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-definitions")}, + i18n("Print complete DUChain Definitions repository on successful parse")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-symboltable")}, + i18n( + "Print complete DUChain PersistentSymbolTable repository on successful parse")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-depth")}, + i18n( + "Number defining the maximum depth where declaration details are printed"), + QStringLiteral("depth")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-graph")}, + i18n("Dump DUChain graph (in .dot format)")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("d"), QStringLiteral("dump-errors")}, + i18n("Print problems encountered during parsing")}); + parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-imported-errors")}, + i18n("Recursively dump errors from imported contexts.")}); parser.process(app); aboutData.processCommandLine(&parser); verbose = parser.isSet(QStringLiteral("verbose")); warnings = parser.isSet(QStringLiteral("warnings")); qInstallMessageHandler(messageOutput); AutoTestShell::init(); TestCore::initialize(Core::NoUi, QStringLiteral("duchainify")); Manager manager(&parser); QTimer::singleShot(0, &manager, &Manager::init); int ret = app.exec(); TestCore::shutdown(); return ret; } diff --git a/kdevplatform/util/duchainify/main.h b/kdevplatform/util/duchainify/main.h index 5188a17d88..12454d65b4 100644 --- a/kdevplatform/util/duchainify/main.h +++ b/kdevplatform/util/duchainify/main.h @@ -1,52 +1,55 @@ /*************************************************************************** * Copyright 2009 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 Library 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_MAIN_H #define KDEVPLATFORM_MAIN_H #include #include #include #include #include class QCommandLineParser; -class Manager : public QObject { +class Manager : public QObject +{ Q_OBJECT - public: - explicit Manager(QCommandLineParser* args); - void addToBackgroundParser(const QString& path, KDevelop::TopDUContext::Features features); - QSet waiting(); - private: - QSet m_waiting; - uint m_total; - QCommandLineParser* m_args; - QAtomicInt m_allFilesAdded; - - public Q_SLOTS: - // delay init into event loop so the DUChain can always shutdown gracefully - void init(); - void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); - void finish(); - void dump(const KDevelop::ReferencedTopDUContext& topContext); + +public: + explicit Manager(QCommandLineParser* args); + void addToBackgroundParser(const QString& path, KDevelop::TopDUContext::Features features); + QSet waiting(); + +private: + QSet m_waiting; + uint m_total; + QCommandLineParser* m_args; + QAtomicInt m_allFilesAdded; + +public Q_SLOTS: + // delay init into event loop so the DUChain can always shutdown gracefully + void init(); + void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); + void finish(); + void dump(const KDevelop::ReferencedTopDUContext& topContext); }; #endif diff --git a/kdevplatform/util/embeddedfreetree.h b/kdevplatform/util/embeddedfreetree.h index 3b15957e21..eb852d5cdb 100644 --- a/kdevplatform/util/embeddedfreetree.h +++ b/kdevplatform/util/embeddedfreetree.h @@ -1,896 +1,959 @@ /* This file is part of KDevelop Copyright 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. */ #ifndef EMBEDDED_FREE_TREE #define EMBEDDED_FREE_TREE #include "kdevvarlengtharray.h" #include #include #include #include //Uncomment this to search for tree-inconsistencies, however it's very expensive // #define DEBUG_FREEITEM_COUNT debugFreeItemCount(); verifyTreeConsistent(*m_centralFreeItem, 0, m_itemCount); #define DEBUG_FREEITEM_COUNT /** * This file implements algorithms that allow managing a sorted list of items, and managing "free" items * for reuse efficiently in that list. Among those free items a tree is built, and they are traversed * on insertion/removal to manage free items in the tree. * * There is specific needs on the embedded items: * - They must be markable "invalid", so after they are deleted they can stay in their place as invalid items. * - While they are invalid, they still must be able to hold 2 integers, needed for managing the tree of free items. * - One integer is needed for each list to hold a pointer to the central free item. * * Only these functions must be used to manipulate the lists, from the beginning up. First create an empty list * and initialize centralFreeItem with -1, and then you start adding items. * * Since the list is sorted, and each item can be contained only once, these lists actually represent a set. * * EmbeddedTreeAlgorithms implements an efficient "contains" function that uses binary search within the list. */ namespace KDevelop { - ///Responsible for handling the items in the list - ///This is an example. ItemHandler::rightChild(..) and ItemHandler::leftChild(..) must be values that must be able to hold the count of positive - ///values that will be the maximum size of the list, and additionally -1. +///Responsible for handling the items in the list +///This is an example. ItemHandler::rightChild(..) and ItemHandler::leftChild(..) must be values that must be able to hold the count of positive +///values that will be the maximum size of the list, and additionally -1. // template // class ExampleItemHandler { // public: // ExampleItemHandler(const Data& data) : m_data(data) { // } // int ItemHandler::rightChild() const { // Q_ASSERT(0); // } // int ItemHandler::leftChild() const { // Q_ASSERT(0); // } // void ItemHandler::setLeftChild(int child) { // Q_ASSERT(0); // } // void setRightChild(int child) { // Q_ASSERT(0); // } // bool operator<(const StandardItemHandler& rhs) const { // Q_ASSERT(0); // } // //Copies this item into the given one // void copyTo(Data& data) const { // data = m_data; // } // // static void createFreeItem(Data& data) { // data = Data(); // } // // bool isFree() const { // Q_ASSERT(0); // } // // const Data& data() { // } // // private: // const Data& m_data; // }; - /** - * Use this for several constant algorithms on sorted lists with free-trees - * */ - template - class EmbeddedTreeAlgorithms { - - public: +/** + * Use this for several constant algorithms on sorted lists with free-trees + * */ +template +class EmbeddedTreeAlgorithms +{ + +public: + + EmbeddedTreeAlgorithms(const Data* items, uint itemCount, const int& centralFreeItem) : m_items(items) + , m_itemCount(itemCount) + , m_centralFreeItem(¢ralFreeItem) + { + } + ~EmbeddedTreeAlgorithms() + { + } + + ///Efficiently checks whether the item is contained in the set. + ///If it is contained, returns the index. Else, returns -1. + + int indexOf(const Data& data) + { + return indexOf(data, 0, m_itemCount); + } + + ///Searches the given item within the specified bounds. + int indexOf(const Data& data, uint start, uint end) + { + while (1) { + if (start >= end) + return -1; + + int center = (start + end) / 2; + + //Skip free items, since they cannot be used for ordering + for (; center < ( int )end;) { + if (!ItemHandler::isFree(m_items[center])) + break; + ++center; + } - EmbeddedTreeAlgorithms(const Data* items, uint itemCount, const int& centralFreeItem) : m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem) { + if (center == ( int )end) { + end = (start + end) / 2; //No non-free items found in second half, so continue search in the other + } else { + if (ItemHandler::equals(data, m_items[center])) { + return center; + } else if (data < m_items[center]) { + end = (start + end) / 2; + } else { + //Continue search in second half + start = center + 1; + } } - ~EmbeddedTreeAlgorithms() { + } + } + + ///Returns the first valid index that has a data-value larger or equal to @p data. + ///Returns -1 if nothing is found. + int lowerBound(const Data& data, int start, int end) + { + int currentBound = -1; + while (1) { + if (start >= end) + return currentBound; + + int center = (start + end) / 2; + + //Skip free items, since they cannot be used for ordering + for (; center < end;) { + if (!ItemHandler::isFree(m_items[center])) + break; + ++center; } - ///Efficiently checks whether the item is contained in the set. - ///If it is contained, returns the index. Else, returns -1. - - int indexOf(const Data& data) { - return indexOf(data, 0, m_itemCount); + if (center == end) { + end = (start + end) / 2; //No non-free items found in second half, so continue search in the other + } else { + if (ItemHandler::equals(data, m_items[center])) { + return center; + } else if (data < m_items[center]) { + currentBound = center; + end = (start + end) / 2; + } else { + //Continue search in second half + start = center + 1; + } } + } + } + + uint countFreeItems() const + { + return countFreeItemsInternal(*m_centralFreeItem); + } + uint countFreeItemsNaive() const + { + uint ret = 0; + for (uint a = 0; a < m_itemCount; ++a) { + if (ItemHandler::isFree(m_items[a])) + ++ret; + } - ///Searches the given item within the specified bounds. - int indexOf(const Data& data, uint start, uint end) { - while(1) { - if(start >= end) - return -1; - - int center = (start + end)/2; + return ret; + } - //Skip free items, since they cannot be used for ordering - for(; center < (int)end; ) { - if(!ItemHandler::isFree(m_items[center])) - break; - ++center; - } + void verifyOrder() + { + Data last; - if(center == (int)end) { - end = (start + end)/2; //No non-free items found in second half, so continue search in the other - }else{ - if(ItemHandler::equals(data, m_items[center])) { - return center; - }else if(data < m_items[center]) { - end = (start + end)/2; - }else{ - //Continue search in second half - start = center+1; - } - } - } + for (uint a = 0; a < m_itemCount; ++a) { + if (!ItemHandler::isFree(m_items[a])) { + if (!ItemHandler::isFree(last)) + Q_ASSERT(last < m_items[a]); + last = m_items[a]; } + } + } + + void verifyTreeConsistent() + { + verifyTreeConsistentInternal(*m_centralFreeItem, 0, m_itemCount); + Q_ASSERT(countFreeItems() == countFreeItemsNaive()); + } + +private: + void verifyTreeConsistentInternal(int position, int lowerBound, int upperBound) + { + if (position == -1) + return; + Q_ASSERT(lowerBound <= position && position < upperBound); + verifyTreeConsistentInternal(ItemHandler::leftChild(m_items[position]), lowerBound, position); + verifyTreeConsistentInternal(ItemHandler::rightChild(m_items[position]), position + 1, upperBound); + } + + uint countFreeItemsInternal(int item) const + { + if (item == -1) + return 0; + + return 1 + countFreeItemsInternal(ItemHandler::leftChild(m_items[item])) + countFreeItemsInternal(ItemHandler::rightChild( + m_items[ + item])); + } + + const Data* m_items; + uint m_itemCount; + const int* m_centralFreeItem; +}; + +/**Use this to add items. + * The added item must not be in the set yet! + * General usage: + * - Construct the object + * - Check if newItemCount() equals the previous item-count. If not, construct + * a new list as large as newItemCount, and call object.transferData to transfer the data + * into the new list. The new size must match the returned newItemCount. + * - Either call object.apply(), or let it be called automatically by the destructor. + * @tparam increaseFraction By what fraction the list is increased when it needs to. For example the size will be increased by 1/5 if it's 5. + * @tparam rebuildIfInsertionMoreExpensive The structure is rebuilt completely when an insertion needs a moving around of more than rebuildIfInsertionMoreExpensive times + the count of items needed to be moved in worst case in a fresh tree. + * After rebuilding the tree, the free space is evenly distributed, and thus insertions require much less moving. + * */ +template +class EmbeddedTreeAddItem +{ + +public: + + EmbeddedTreeAddItem(Data* items, uint itemCount, int& centralFreeItem, const Data& add) : m_add(add) + , m_items(items) + , m_itemCount(itemCount) + , m_centralFreeItem(¢ralFreeItem) + , m_applied(false) + , m_needResize(false) + { + m_needResize = !apply(); + } + ~EmbeddedTreeAddItem() + { + if (!m_applied) + apply(true); + } + + ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then + ///the data needs to be transferred into a new list using transferData + uint newItemCount() const + { + if (!m_applied) { + if (*m_centralFreeItem == -1) { + uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); + uint newItemCount = realItemCount + (realItemCount / increaseFraction); + if (newItemCount <= m_itemCount) + newItemCount = m_itemCount + 1; + + return newItemCount; + } else if (m_needResize) { + uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); + uint newItemCount = realItemCount + (realItemCount / increaseFraction); + + return newItemCount; + } + } + return m_itemCount; + } - ///Returns the first valid index that has a data-value larger or equal to @p data. - ///Returns -1 if nothing is found. - int lowerBound(const Data& data, int start, int end) { - int currentBound = -1; - while(1) { - if(start >= end) - return currentBound; - - int center = (start + end)/2; - - //Skip free items, since they cannot be used for ordering - for(; center < end; ) { - if(!ItemHandler::isFree(m_items[center])) - break; - ++center; - } + ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() + void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) + { + DEBUG_FREEITEM_COUNT - if(center == end) { - end = (start + end)/2; //No non-free items found in second half, so continue search in the other - }else{ - if(ItemHandler::equals(data, m_items[center])) { - return center; - }else if(data < m_items[center]) { - currentBound = center; - end = (start + end)/2; - }else{ - //Continue search in second half - start = center+1; - } - } - } - } + uint currentRealCount = m_itemCount - countFreeItems(*m_centralFreeItem); +// Q_ASSERT(currentRealCount + (currentRealCount/increaseFraction) == newCount); - uint countFreeItems() const { - return countFreeItemsInternal(*m_centralFreeItem); + //Create a new list where the items from m_items are put into newItems, with the free items evenly + //distributed, and a clean balanced free-tree. + uint newFreeCount = newCount - currentRealCount; + volatile uint freeItemRaster; + if (newFreeCount) + freeItemRaster = newCount / newFreeCount; + else { + freeItemRaster = newCount + 1; //No free items } - uint countFreeItemsNaive() const { - uint ret = 0; - for(uint a = 0; a < m_itemCount; ++a) { - if(ItemHandler::isFree(m_items[a])) - ++ret; + + ///@todo Do not iterate through all items, instead use the free-tree and memcpy for the ranges between free items. + ///Ideally, even the free-tree would be built on-the-fly. + Q_ASSERT(freeItemRaster); + uint offset = 0; + uint insertedValidCount = 0; + for (uint a = 0; a < newCount; ++a) { + //Create new free items at the end of their raster range + if (a % freeItemRaster == (freeItemRaster - 1)) { + //We need to insert a free item + ItemHandler::createFreeItem(newItems[a]); + ++offset; + } else { + ++insertedValidCount; + while (ItemHandler::isFree(m_items[a - offset]) && a - offset < m_itemCount) + --offset; + Q_ASSERT(a - offset < m_itemCount); + newItems[a] = m_items[a - offset]; +// Q_ASSERT(!ItemHandler::isFree(newItems[a])); } - return ret; } - void verifyOrder() { - Data last; + Q_ASSERT(insertedValidCount == m_itemCount - countFreeItems(*m_centralFreeItem)); +// qCDebug(UTIL) << m_itemCount << newCount << offset; +// Q_ASSERT(m_itemCount == newCount-offset); - for(uint a = 0; a < m_itemCount; ++a) { - if(!ItemHandler::isFree(m_items[a])) { - if(!ItemHandler::isFree(last)) - Q_ASSERT(last < m_items[a]); - last = m_items[a]; - } - } - } + m_items = newItems; + m_itemCount = newCount; - void verifyTreeConsistent() { - verifyTreeConsistentInternal(*m_centralFreeItem, 0, m_itemCount); - Q_ASSERT(countFreeItems() == countFreeItemsNaive()); - } + if (newCentralFree) + m_centralFreeItem = newCentralFree; - private: - void verifyTreeConsistentInternal(int position, int lowerBound, int upperBound) { - if(position == -1) - return; - Q_ASSERT(lowerBound <= position && position < upperBound); - verifyTreeConsistentInternal(ItemHandler::leftChild(m_items[position]), lowerBound, position); - verifyTreeConsistentInternal(ItemHandler::rightChild(m_items[position]), position+1, upperBound); - } + *m_centralFreeItem = buildFreeTree(newFreeCount, freeItemRaster, freeItemRaster - 1); - uint countFreeItemsInternal(int item) const { - if(item == -1) - return 0; +// qCDebug(UTIL) << "count of new free items:" << newFreeCount; - return 1 + countFreeItemsInternal(ItemHandler::leftChild(m_items[item])) + countFreeItemsInternal(ItemHandler::rightChild(m_items[item])); - } +// Q_ASSERT(countFreeItems( *m_centralFreeItem ) == newFreeCount); - const Data* m_items; - uint m_itemCount; - const int* m_centralFreeItem; - }; - - /**Use this to add items. - * The added item must not be in the set yet! - * General usage: - * - Construct the object - * - Check if newItemCount() equals the previous item-count. If not, construct - * a new list as large as newItemCount, and call object.transferData to transfer the data - * into the new list. The new size must match the returned newItemCount. - * - Either call object.apply(), or let it be called automatically by the destructor. - * @tparam increaseFraction By what fraction the list is increased when it needs to. For example the size will be increased by 1/5 if it's 5. - * @tparam rebuildIfInsertionMoreExpensive The structure is rebuilt completely when an insertion needs a moving around of more than rebuildIfInsertionMoreExpensive times - the count of items needed to be moved in worst case in a fresh tree. - * After rebuilding the tree, the free space is evenly distributed, and thus insertions require much less moving. - * */ - template - class EmbeddedTreeAddItem { - - public: - - EmbeddedTreeAddItem(Data* items, uint itemCount, int& centralFreeItem, const Data& add) : m_add(add), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_applied(false), m_needResize(false) { - m_needResize = !apply(); - } - ~EmbeddedTreeAddItem() { - if(!m_applied) - apply(true); - } + DEBUG_FREEITEM_COUNT + } - ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then - ///the data needs to be transferred into a new list using transferData - uint newItemCount() const { - if(!m_applied) { - if(*m_centralFreeItem == -1) { - uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); - uint newItemCount = realItemCount + (realItemCount/increaseFraction); - if(newItemCount <= m_itemCount) - newItemCount = m_itemCount+1; - - return newItemCount; - }else if(m_needResize) { - uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); - uint newItemCount = realItemCount + (realItemCount/increaseFraction); - - return newItemCount; - } + ///Tries to put the item into the list. If the insertion would be too inefficient or is not possible, returns false, unless @param force is true + bool apply(bool force = false) + { + if (m_applied) + return true; + + if (*m_centralFreeItem == -1) { + Q_ASSERT(!force); + return false; + } + + //Find the free item that is nearest to the target position in the item order + int previousItem = -1; + int currentItem = *m_centralFreeItem; + int replaceCurrentWith = -1; + + //In currentLowerBound and currentUpperBound, we count the smallest contiguous range between free + //items that the added items needs to be sorted into. If the range is empty, the item can just be inserted. + int currentLowerBound = 0; + int currentUpperBound = m_itemCount; + + //Now go down the chain, always into the items direction + + while (1) { + QPair freeBounds = leftAndRightRealItems(currentItem); + const Data& current(m_items[currentItem]); + if (freeBounds.first != -1 && m_add < m_items[freeBounds.first]) { + //Follow left child + currentUpperBound = freeBounds.first + 1; + + if (ItemHandler::leftChild(current) != -1) { + //Continue traversing + previousItem = currentItem; + currentItem = ItemHandler::leftChild(current); + } else { + replaceCurrentWith = ItemHandler::rightChild(current); + break; } - return m_itemCount; - } + } else if (freeBounds.second != -1 && m_items[freeBounds.second] < m_add) { + //Follow right child + currentLowerBound = freeBounds.second; + + if (ItemHandler::rightChild(current) != -1) { + //Continue traversing + previousItem = currentItem; + currentItem = ItemHandler::rightChild(current); + } else { + replaceCurrentWith = ItemHandler::leftChild(current); + break; + } + } else { + //We will use this item! So find a replacement for it in the tree, and update the structure + force = true; + currentLowerBound = currentUpperBound = currentItem; + + int leftReplaceCandidate = -1, rightReplaceCandidate = -1; + if (ItemHandler::leftChild(current) != -1) + leftReplaceCandidate = rightMostChild(ItemHandler::leftChild(current)); + if (ItemHandler::rightChild(current) != -1) + rightReplaceCandidate = leftMostChild(ItemHandler::rightChild(current)); + + ///@todo it's probably better using lowerBound and upperBound like in the "remove" version + //Left and right bounds of all children of current + int leftChildBound = leftMostChild(currentItem), rightChildBound = rightMostChild(currentItem); + Q_ASSERT(leftChildBound != -1 && rightChildBound != -1); + int childCenter = (leftChildBound + rightChildBound) / 2; + + if (leftReplaceCandidate == -1 && rightReplaceCandidate == -1) { + //We don't have a replace candidate, since there is no children + Q_ASSERT(ItemHandler::leftChild(current) == -1); + Q_ASSERT(ItemHandler::rightChild(current) == -1); + } else if (rightReplaceCandidate == -1 || + abs(leftReplaceCandidate - childCenter) < abs(rightReplaceCandidate - childCenter)) { + //pick the left replacement, since it's more central + Q_ASSERT(leftReplaceCandidate != -1); + replaceCurrentWith = leftReplaceCandidate; + + Data& replaceWith(m_items[replaceCurrentWith]); + + if (replaceCurrentWith == ItemHandler::leftChild(current)) { + //The left child of replaceWith can just stay as it is, and we just need to add the right child + Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); + } else { + takeRightMostChild(ItemHandler::leftChild(current)); - ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() - void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) { - DEBUG_FREEITEM_COUNT + //Since we'll be clearing the item, we have to put this childsomewhere else. + // Either make it our new "left" child, or make it the new left children "rightmost" child. + int addRightMostLeftChild = ItemHandler::leftChild(replaceWith); - uint currentRealCount = m_itemCount - countFreeItems(*m_centralFreeItem); -// Q_ASSERT(currentRealCount + (currentRealCount/increaseFraction) == newCount); + ItemHandler::setLeftChild(replaceWith, -1); - //Create a new list where the items from m_items are put into newItems, with the free items evenly - //distributed, and a clean balanced free-tree. - uint newFreeCount = newCount - currentRealCount; - volatile uint freeItemRaster; - if(newFreeCount) - freeItemRaster = newCount / newFreeCount; - else { - freeItemRaster = newCount+1; //No free items - } + Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); + Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); - ///@todo Do not iterate through all items, instead use the free-tree and memcpy for the ranges between free items. - ///Ideally, even the free-tree would be built on-the-fly. - Q_ASSERT(freeItemRaster); - uint offset = 0; - uint insertedValidCount = 0; - for(uint a = 0; a < newCount; ++a) { - //Create new free items at the end of their raster range - if(a % freeItemRaster == (freeItemRaster-1)) { - //We need to insert a free item - ItemHandler::createFreeItem(newItems[a]); - ++offset; - }else{ - ++insertedValidCount; - while(ItemHandler::isFree(m_items[a-offset]) && a-offset < m_itemCount) - --offset; - Q_ASSERT(a-offset < m_itemCount); - newItems[a] = m_items[a-offset]; -// Q_ASSERT(!ItemHandler::isFree(newItems[a])); + if (ItemHandler::leftChild(current) != -1) { + Q_ASSERT(rightMostChild(ItemHandler::leftChild(current)) != replaceCurrentWith); + Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild( + current) < replaceCurrentWith); + ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); + + if (addRightMostLeftChild != -1) { + int rightMostLeft = rightMostChild(ItemHandler::leftChild(replaceWith)); + Q_ASSERT(rightMostLeft != -1); +// Q_ASSERT(item(rightMostLeft).ItemHandler::rightChild() == -1); + Q_ASSERT(rightMostLeft < addRightMostLeftChild); + ItemHandler::setRightChild(m_items[rightMostLeft], addRightMostLeftChild); + } + } else { + Q_ASSERT(addRightMostLeftChild == -1 || addRightMostLeftChild < replaceCurrentWith); + ItemHandler::setLeftChild(replaceWith, addRightMostLeftChild); + } } - } - Q_ASSERT(insertedValidCount == m_itemCount - countFreeItems(*m_centralFreeItem)); -// qCDebug(UTIL) << m_itemCount << newCount << offset; -// Q_ASSERT(m_itemCount == newCount-offset); - m_items = newItems; - m_itemCount = newCount; + Q_ASSERT(ItemHandler::rightChild(current) == -1 || + replaceCurrentWith < ItemHandler::rightChild(current)); + ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); + } else { + //pick the right replacement, since it's more central + Q_ASSERT(rightReplaceCandidate != -1); + replaceCurrentWith = rightReplaceCandidate; - if(newCentralFree) - m_centralFreeItem = newCentralFree; + Data& replaceWith(m_items[replaceCurrentWith]); - *m_centralFreeItem = buildFreeTree(newFreeCount, freeItemRaster, freeItemRaster-1); + if (replaceCurrentWith == ItemHandler::rightChild(current)) { + //The right child of replaceWith can just stay as it is, and we just need to add the left child + Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); + } else { + takeLeftMostChild(ItemHandler::rightChild(current)); -// qCDebug(UTIL) << "count of new free items:" << newFreeCount; + //Since we'll be clearing the item, we have to put this childsomewhere else. + // Either make it our new "right" child, or make it the new right children "leftmost" child. + int addLeftMostRightChild = ItemHandler::rightChild(replaceWith); -// Q_ASSERT(countFreeItems( *m_centralFreeItem ) == newFreeCount); + ItemHandler::setRightChild(replaceWith, -1); - DEBUG_FREEITEM_COUNT - } + Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); + Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); - ///Tries to put the item into the list. If the insertion would be too inefficient or is not possible, returns false, unless @param force is true - bool apply(bool force = false) { - if(m_applied) - return true; - - if(*m_centralFreeItem == -1) { - Q_ASSERT(!force); - return false; - } - - //Find the free item that is nearest to the target position in the item order - int previousItem = -1; - int currentItem = *m_centralFreeItem; - int replaceCurrentWith = -1; - - //In currentLowerBound and currentUpperBound, we count the smallest contiguous range between free - //items that the added items needs to be sorted into. If the range is empty, the item can just be inserted. - int currentLowerBound = 0; - int currentUpperBound = m_itemCount; - - //Now go down the chain, always into the items direction - - while(1) { - QPair freeBounds = leftAndRightRealItems(currentItem); - const Data& current(m_items[currentItem]); - if(freeBounds.first != -1 && m_add < m_items[freeBounds.first]) { - //Follow left child - currentUpperBound = freeBounds.first+1; - - if(ItemHandler::leftChild(current) != -1) { - //Continue traversing - previousItem = currentItem; - currentItem = ItemHandler::leftChild(current); - }else{ - replaceCurrentWith = ItemHandler::rightChild(current); - break; - } - }else if(freeBounds.second != -1 && m_items[freeBounds.second] < m_add) { - //Follow right child - currentLowerBound = freeBounds.second; - - if(ItemHandler::rightChild(current) != -1) { - //Continue traversing - previousItem = currentItem; - currentItem = ItemHandler::rightChild(current); - }else{ - replaceCurrentWith = ItemHandler::leftChild(current); - break; - } - }else{ - //We will use this item! So find a replacement for it in the tree, and update the structure - force = true; - currentLowerBound = currentUpperBound = currentItem; - - int leftReplaceCandidate = -1, rightReplaceCandidate = -1; - if(ItemHandler::leftChild(current) != -1) - leftReplaceCandidate = rightMostChild(ItemHandler::leftChild(current)); - if(ItemHandler::rightChild(current) != -1) - rightReplaceCandidate = leftMostChild(ItemHandler::rightChild(current)); - - ///@todo it's probably better using lowerBound and upperBound like in the "remove" version - //Left and right bounds of all children of current - int leftChildBound = leftMostChild(currentItem), rightChildBound = rightMostChild(currentItem); - Q_ASSERT(leftChildBound != -1 && rightChildBound != -1); - int childCenter = (leftChildBound + rightChildBound)/2; - - if(leftReplaceCandidate == -1 && rightReplaceCandidate == -1) { - //We don't have a replace candidate, since there is no children - Q_ASSERT(ItemHandler::leftChild(current) == -1); - Q_ASSERT(ItemHandler::rightChild(current) == -1); - }else if(rightReplaceCandidate == -1 || abs(leftReplaceCandidate - childCenter) < abs(rightReplaceCandidate - childCenter)) { - //pick the left replacement, since it's more central - Q_ASSERT(leftReplaceCandidate != -1); - replaceCurrentWith = leftReplaceCandidate; - - Data& replaceWith(m_items[replaceCurrentWith]); - - if(replaceCurrentWith == ItemHandler::leftChild(current)) { - //The left child of replaceWith can just stay as it is, and we just need to add the right child - Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); - }else{ - takeRightMostChild(ItemHandler::leftChild(current)); - - //Since we'll be clearing the item, we have to put this childsomewhere else. - // Either make it our new "left" child, or make it the new left children "rightmost" child. - int addRightMostLeftChild = ItemHandler::leftChild(replaceWith); - - ItemHandler::setLeftChild(replaceWith, -1); - - Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); - Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); - - if(ItemHandler::leftChild(current) != -1) - { - Q_ASSERT(rightMostChild(ItemHandler::leftChild(current)) != replaceCurrentWith); - Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); - ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); - - if(addRightMostLeftChild != -1) { - int rightMostLeft = rightMostChild(ItemHandler::leftChild(replaceWith)); - Q_ASSERT(rightMostLeft != -1); -// Q_ASSERT(item(rightMostLeft).ItemHandler::rightChild() == -1); - Q_ASSERT(rightMostLeft < addRightMostLeftChild); - ItemHandler::setRightChild(m_items[rightMostLeft], addRightMostLeftChild); - } - }else{ - Q_ASSERT(addRightMostLeftChild == -1 || addRightMostLeftChild < replaceCurrentWith); - ItemHandler::setLeftChild(replaceWith, addRightMostLeftChild); - } - } - - Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); - ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); - }else{ - //pick the right replacement, since it's more central - Q_ASSERT(rightReplaceCandidate != -1); - replaceCurrentWith = rightReplaceCandidate; - - Data& replaceWith(m_items[replaceCurrentWith]); - - if(replaceCurrentWith == ItemHandler::rightChild(current)) { - //The right child of replaceWith can just stay as it is, and we just need to add the left child - Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); - }else{ - takeLeftMostChild(ItemHandler::rightChild(current)); - - //Since we'll be clearing the item, we have to put this childsomewhere else. - // Either make it our new "right" child, or make it the new right children "leftmost" child. - int addLeftMostRightChild = ItemHandler::rightChild(replaceWith); - - ItemHandler::setRightChild(replaceWith, -1); - - Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); - Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); - - if(ItemHandler::rightChild(current) != -1) - { - Q_ASSERT(leftMostChild(ItemHandler::rightChild(current)) != replaceCurrentWith); - Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); - ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); - - if(addLeftMostRightChild != -1) { - int leftMostRight = leftMostChild(ItemHandler::rightChild(replaceWith)); - Q_ASSERT(leftMostRight != -1); - Q_ASSERT(ItemHandler::leftChild(m_items[leftMostRight]) == -1); - Q_ASSERT(addLeftMostRightChild < leftMostRight); - ItemHandler::setLeftChild(m_items[leftMostRight], addLeftMostRightChild); - } - }else{ - Q_ASSERT(addLeftMostRightChild == -1 || replaceCurrentWith < addLeftMostRightChild); - ItemHandler::setRightChild(replaceWith, addLeftMostRightChild); + if (ItemHandler::rightChild(current) != -1) { + Q_ASSERT(leftMostChild(ItemHandler::rightChild(current)) != replaceCurrentWith); + Q_ASSERT(ItemHandler::rightChild( + current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); + ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); + + if (addLeftMostRightChild != -1) { + int leftMostRight = leftMostChild(ItemHandler::rightChild(replaceWith)); + Q_ASSERT(leftMostRight != -1); + Q_ASSERT(ItemHandler::leftChild(m_items[leftMostRight]) == -1); + Q_ASSERT(addLeftMostRightChild < leftMostRight); + ItemHandler::setLeftChild(m_items[leftMostRight], addLeftMostRightChild); } - } + } else { + Q_ASSERT(addLeftMostRightChild == -1 || replaceCurrentWith < addLeftMostRightChild); + ItemHandler::setRightChild(replaceWith, addLeftMostRightChild); + } + } - Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); - ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); - } - break; - } - } + Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild( + current) < replaceCurrentWith); + ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); + } + break; + } + } - //We can insert now - //currentItem and previousItem are the two items that best enclose the target item + //We can insert now + //currentItem and previousItem are the two items that best enclose the target item // for(int a = currentLowerBound; a < currentUpperBound; ++a) { // Q_ASSERT(!ItemHandler::isFree(m_items[a])); // } - Q_ASSERT(currentItem < currentLowerBound || currentItem >= currentUpperBound); - - //If the current item is on a border of the bounds, it needs to be inserted in the right position. - //Else, the current position already is right, and we only need to copy it in. - if(currentLowerBound < currentUpperBound && (currentItem == currentLowerBound-1 || currentItem == currentUpperBound)) { - if(!insertSorted(m_add, currentItem, currentLowerBound, currentUpperBound, force)) { - return false; - } - }else{ - ItemHandler::copyTo(m_add, m_items[currentItem]); - } - - m_applied = true; - - //First, take currentItem out of the chain, by replacing it with current.rightChild in the parent - if(previousItem != -1) { - Data& previous(m_items[previousItem]); - if(ItemHandler::leftChild(previous) == currentItem) { - Q_ASSERT(replaceCurrentWith == -1 || replaceCurrentWith < previousItem); - ItemHandler::setLeftChild(previous, replaceCurrentWith); - } else if(ItemHandler::rightChild(previous) == currentItem) { - Q_ASSERT(replaceCurrentWith == -1 || previousItem < replaceCurrentWith); - ItemHandler::setRightChild(previous, replaceCurrentWith); - } else { - Q_ASSERT(0); - } - } else { - *m_centralFreeItem = replaceCurrentWith; - } - - return true; - - DEBUG_FREEITEM_COUNT - } - - private: - void verifyTreeConsistent(int position, int lowerBound, int upperBound) { - if(position == -1) - return; - Q_ASSERT(lowerBound <= position && position < upperBound); - verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); - verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); - } - - void debugFreeItemCount() { - uint count = 0; - for(uint a = 0; a < m_itemCount; ++a) { - if(isFree(m_items[a])) - ++count; - } - uint counted = countFreeItems(*m_centralFreeItem); - Q_ASSERT(count == counted); - Q_UNUSED(counted); + Q_ASSERT(currentItem < currentLowerBound || currentItem >= currentUpperBound); + + //If the current item is on a border of the bounds, it needs to be inserted in the right position. + //Else, the current position already is right, and we only need to copy it in. + if (currentLowerBound < currentUpperBound && + (currentItem == currentLowerBound - 1 || currentItem == currentUpperBound)) { + if (!insertSorted(m_add, currentItem, currentLowerBound, currentUpperBound, force)) { + return false; } + } else { + ItemHandler::copyTo(m_add, m_items[currentItem]); + } - QPair leftAndRightRealItems(int pos) { - Q_ASSERT(ItemHandler::isFree(m_items[pos])); - int left = -1, right = -1; - for(int a = pos-1; a >= 0; --a) { - if(!ItemHandler::isFree(m_items[a])) { - left = a; - break; - } - } - for(uint a = pos+1; a < m_itemCount; ++a) { - if(!ItemHandler::isFree(m_items[a])) { - right = a; - break; - } - } - return qMakePair(left, right); + m_applied = true; + + //First, take currentItem out of the chain, by replacing it with current.rightChild in the parent + if (previousItem != -1) { + Data& previous(m_items[previousItem]); + if (ItemHandler::leftChild(previous) == currentItem) { + Q_ASSERT(replaceCurrentWith == -1 || replaceCurrentWith < previousItem); + ItemHandler::setLeftChild(previous, replaceCurrentWith); + } else if (ItemHandler::rightChild(previous) == currentItem) { + Q_ASSERT(replaceCurrentWith == -1 || previousItem < replaceCurrentWith); + ItemHandler::setRightChild(previous, replaceCurrentWith); + } else { + Q_ASSERT(0); } + } else { + *m_centralFreeItem = replaceCurrentWith; + } - int buildFreeTree(int count, uint raster, int start) { - Q_ASSERT((start % raster) == (raster-1)); - Q_ASSERT(count != 0); - Q_ASSERT(count <= (int)m_itemCount); - if(count == 1) { - ItemHandler::createFreeItem(m_items[start]); - return start; - }else{ - int central = start + (count / 2) * raster; - int leftCount = count / 2; - int midCount = 1; - int rightCount = count - leftCount - midCount; - Q_ASSERT(leftCount + midCount <= count); - ItemHandler::createFreeItem(m_items[central]); - Q_ASSERT(ItemHandler::isFree(m_items[central])); - - int leftFreeTree = buildFreeTree(leftCount, raster, start); - Q_ASSERT(leftFreeTree == -1 || leftFreeTree < central); - ItemHandler::setLeftChild(m_items[central], leftFreeTree ); - - if(rightCount > 0) { - int rightFreeTree = buildFreeTree(rightCount, raster, central+raster); - Q_ASSERT(rightFreeTree == -1 || central < rightFreeTree); - ItemHandler::setRightChild(m_items[central], rightFreeTree ); - } + return true; + + DEBUG_FREEITEM_COUNT + } + +private: + void verifyTreeConsistent(int position, int lowerBound, int upperBound) + { + if (position == -1) + return; + Q_ASSERT(lowerBound <= position && position < upperBound); + verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); + verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position + 1, upperBound); + } + + void debugFreeItemCount() + { + uint count = 0; + for (uint a = 0; a < m_itemCount; ++a) { + if (isFree(m_items[a])) + ++count; + } - return central; - } + uint counted = countFreeItems(*m_centralFreeItem); + Q_ASSERT(count == counted); + Q_UNUSED(counted); + } + + QPair leftAndRightRealItems(int pos) + { + Q_ASSERT(ItemHandler::isFree(m_items[pos])); + int left = -1, right = -1; + for (int a = pos - 1; a >= 0; --a) { + if (!ItemHandler::isFree(m_items[a])) { + left = a; + break; } + } - uint countFreeItems(int item) const { - if(item == -1) - return 0; - const Data& current(m_items[item]); + for (uint a = pos + 1; a < m_itemCount; ++a) { + if (!ItemHandler::isFree(m_items[a])) { + right = a; + break; + } + } - return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); + return qMakePair(left, right); + } + + int buildFreeTree(int count, uint raster, int start) + { + Q_ASSERT((start % raster) == (raster - 1)); + Q_ASSERT(count != 0); + Q_ASSERT(count <= ( int )m_itemCount); + if (count == 1) { + ItemHandler::createFreeItem(m_items[start]); + return start; + } else { + int central = start + (count / 2) * raster; + int leftCount = count / 2; + int midCount = 1; + int rightCount = count - leftCount - midCount; + Q_ASSERT(leftCount + midCount <= count); + ItemHandler::createFreeItem(m_items[central]); + Q_ASSERT(ItemHandler::isFree(m_items[central])); + + int leftFreeTree = buildFreeTree(leftCount, raster, start); + Q_ASSERT(leftFreeTree == -1 || leftFreeTree < central); + ItemHandler::setLeftChild(m_items[central], leftFreeTree); + + if (rightCount > 0) { + int rightFreeTree = buildFreeTree(rightCount, raster, central + raster); + Q_ASSERT(rightFreeTree == -1 || central < rightFreeTree); + ItemHandler::setRightChild(m_items[central], rightFreeTree); } - int leftMostChild(int pos) const { - while(1) { - if(ItemHandler::leftChild(m_items[pos]) != -1) - pos = ItemHandler::leftChild(m_items[pos]); - else - return pos; - } - } - - int takeLeftMostChild(int pos) const { - int parent = -1; - while(1) { - if(ItemHandler::leftChild(m_items[pos]) != -1) { - parent = pos; - pos = ItemHandler::leftChild(m_items[pos]); - } else { - ItemHandler::setLeftChild(m_items[parent], -1); - return pos; - } - } - } - - int rightMostChild(int pos) const { - while(1) { - if(ItemHandler::rightChild(m_items[pos]) != -1) - pos = ItemHandler::rightChild(m_items[pos]); - else - return pos; - } - } - - int takeRightMostChild(int pos) const { - int parent = -1; - while(1) { - if(ItemHandler::rightChild(m_items[pos]) != -1) { - parent = pos; - pos = ItemHandler::rightChild(m_items[pos]); - } else { - ItemHandler::setRightChild(m_items[parent], -1); - return pos; - } - } - } + return central; + } + } + + uint countFreeItems(int item) const + { + if (item == -1) + return 0; + const Data& current(m_items[item]); + + return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); + } + + int leftMostChild(int pos) const + { + while (1) { + if (ItemHandler::leftChild(m_items[pos]) != -1) + pos = ItemHandler::leftChild(m_items[pos]); + else + return pos; + } + } + + int takeLeftMostChild(int pos) const + { + int parent = -1; + while (1) { + if (ItemHandler::leftChild(m_items[pos]) != -1) { + parent = pos; + pos = ItemHandler::leftChild(m_items[pos]); + } else { + ItemHandler::setLeftChild(m_items[parent], -1); + return pos; + } + } + } + + int rightMostChild(int pos) const + { + while (1) { + if (ItemHandler::rightChild(m_items[pos]) != -1) + pos = ItemHandler::rightChild(m_items[pos]); + else + return pos; + } + } + + int takeRightMostChild(int pos) const + { + int parent = -1; + while (1) { + if (ItemHandler::rightChild(m_items[pos]) != -1) { + parent = pos; + pos = ItemHandler::rightChild(m_items[pos]); + } else { + ItemHandler::setRightChild(m_items[parent], -1); + return pos; + } + } + } - ///Maximum "moving" out of the way of items without forcing a complete rebuild of the list - inline int maxMoveAround() const { - return increaseFraction * rebuildIfInsertionMoreExpensive; - } + ///Maximum "moving" out of the way of items without forcing a complete rebuild of the list + inline int maxMoveAround() const + { + return increaseFraction * rebuildIfInsertionMoreExpensive; + } - ///Inserts the given data item into position @p pos, and updates the sorting - ///@param data can be another empty item, that together with @p pos represents the closest enclosure of the target position - ///@return Whether the item could be inserted. It is not inserted if - bool insertSorted(const Data& data, int pos, int start, int end, bool force) { + ///Inserts the given data item into position @p pos, and updates the sorting + ///@param data can be another empty item, that together with @p pos represents the closest enclosure of the target position + ///@return Whether the item could be inserted. It is not inserted if + bool insertSorted(const Data& data, int pos, int start, int end, bool force) + { - if(pos < start) - start = pos; - if(pos >= end) - end = pos+1; + if (pos < start) + start = pos; + if (pos >= end) + end = pos + 1; /* for(int a = start; a < end; ++a) { if(a != pos) { Q_ASSERT(!ItemHandler::isFree(m_items[a])); } }*/ - EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); - int bound = alg.lowerBound(data, start, end); - //Now find the position that should be taken - if(bound == -1) - bound = end; + EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); + int bound = alg.lowerBound(data, start, end); + //Now find the position that should be taken + if (bound == -1) + bound = end; - //Now our item should end up right before bound + //Now our item should end up right before bound - int target; - //bound cannot be pos, because pos is invalid - Q_ASSERT(bound != pos); + int target; + //bound cannot be pos, because pos is invalid + Q_ASSERT(bound != pos); #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif - //Shuffle around the item at the free pos, so reference counting in constructors/destructors is not screwed up - char backup[sizeof(Data)]; - memcpy(backup, m_items+pos, sizeof(Data)); + //Shuffle around the item at the free pos, so reference counting in constructors/destructors is not screwed up + char backup[sizeof(Data)]; + memcpy(backup, m_items + pos, sizeof(Data)); - if(bound < pos) { - if(!force && pos-bound > maxMoveAround()) { + if (bound < pos) { + if (!force && pos - bound > maxMoveAround()) { // qCDebug(UTIL) << "increasing because" << pos-bound << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem) << "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; - return false; - } - //Move [bound, pos) one to right, and insert at bound - memmove(m_items+bound+1, m_items+bound, sizeof(Data)*(pos-bound)); - target = bound; - }else { - Q_ASSERT(bound > pos); - if(!force && bound-pos-1 > maxMoveAround()) { + return false; + } + //Move [bound, pos) one to right, and insert at bound + memmove(m_items + bound + 1, m_items + bound, sizeof(Data) * (pos - bound)); + target = bound; + } else { + Q_ASSERT(bound > pos); + if (!force && bound - pos - 1 > maxMoveAround()) { // qCDebug(UTIL) << "increasing because" << bound-pos-1 << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem)<< "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; - return false; - } - //Move (pos, bound) one to left, and insert at bound-1 - memmove(m_items+pos, m_items+pos+1, sizeof(Data)*(bound-pos-1)); - target = bound-1; - } - memcpy(m_items+target, backup, sizeof(Data)); + return false; + } + //Move (pos, bound) one to left, and insert at bound-1 + memmove(m_items + pos, m_items + pos + 1, sizeof(Data) * (bound - pos - 1)); + target = bound - 1; + } + memcpy(m_items + target, backup, sizeof(Data)); #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 800) #pragma GCC diagnostic pop #endif - ItemHandler::copyTo(data, m_items[target]); - return true; - } - - const Data& m_add; - Data* m_items; - uint m_itemCount; - int* m_centralFreeItem; - bool m_applied, m_needResize; - }; - - /**Use this to add items. - * The removed item must be in the set! - * General usage: - * - Construct the object - * - Check if newItemCount() equals the previous item-count. If not, construct - * a new list as large as newItemCount, and call object.transferData to transfer the data - * into the new list. The new size must match the returned newItemCount. - * However this may also be ignored if the memory-saving is not wanted in that moment. - * */ - template - class EmbeddedTreeRemoveItem { - - public: - - EmbeddedTreeRemoveItem(Data* items, uint itemCount, int& centralFreeItem, const Data& remove) : m_remove(remove), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_insertedAtDepth(0) { - apply(); + ItemHandler::copyTo(data, m_items[target]); + return true; + } + + const Data& m_add; + Data* m_items; + uint m_itemCount; + int* m_centralFreeItem; + bool m_applied, m_needResize; +}; + +/**Use this to add items. + * The removed item must be in the set! + * General usage: + * - Construct the object + * - Check if newItemCount() equals the previous item-count. If not, construct + * a new list as large as newItemCount, and call object.transferData to transfer the data + * into the new list. The new size must match the returned newItemCount. + * However this may also be ignored if the memory-saving is not wanted in that moment. + * */ +template +class EmbeddedTreeRemoveItem +{ + +public: + + EmbeddedTreeRemoveItem(Data* items, uint itemCount, int& centralFreeItem, const Data& remove) : m_remove(remove) + , m_items(items) + , m_itemCount(itemCount) + , m_centralFreeItem(¢ralFreeItem) + , m_insertedAtDepth(0) + { + apply(); + } + + ~EmbeddedTreeRemoveItem() + { + } + + ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then + ///the data needs to be transferred into a new list using transferData + uint newItemCount() const + { + uint maxFreeItems = ((m_itemCount / increaseFraction) * 3) / 2 + 1; + //First we approximate the count of free items using the insertion depth + if ((1u << m_insertedAtDepth) >= maxFreeItems) { + uint freeCount = countFreeItems(*m_centralFreeItem); + if (freeCount > maxFreeItems || freeCount == m_itemCount) { + return m_itemCount - freeCount; } + } - ~EmbeddedTreeRemoveItem() { + return m_itemCount; + } + + ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() + void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) + { + DEBUG_FREEITEM_COUNT + //We only transfer into a new list when all the free items are used up + + //Create a list where only the non-free items exist + uint offset = 0; + for (uint a = 0; a < m_itemCount; ++a) { + if (!ItemHandler::isFree(m_items[a])) { + newItems[offset] = m_items[a]; + ++offset; } + } - ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then - ///the data needs to be transferred into a new list using transferData - uint newItemCount() const { - uint maxFreeItems = ((m_itemCount / increaseFraction)*3)/2 + 1; - //First we approximate the count of free items using the insertion depth - if((1u << m_insertedAtDepth) >= maxFreeItems) { - uint freeCount = countFreeItems(*m_centralFreeItem); - if(freeCount > maxFreeItems || freeCount == m_itemCount) { - return m_itemCount - freeCount; - } - } - - return m_itemCount; - } + Q_ASSERT(offset == newCount); + + if (newCentralFree) + m_centralFreeItem = newCentralFree; + *m_centralFreeItem = -1; + m_items = newItems; + m_itemCount = newCount; + DEBUG_FREEITEM_COUNT + } + +private: + void verifyTreeConsistent(int position, int lowerBound, int upperBound) + { + if (position == -1) + return; + Q_ASSERT(lowerBound <= position && position < upperBound); + verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); + verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position + 1, upperBound); + } + + uint countFreeItems(int item) const + { + if (item == -1) + return 0; + const Data& current(m_items[item]); + + return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); + } + + int findItem(const Data& data, uint start, uint end) + { + EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); + return alg.indexOf(data, start, end); + } + + void apply() + { + DEBUG_FREEITEM_COUNT + + int removeIndex = findItem(m_remove, 0, m_itemCount); + Q_ASSERT(removeIndex != -1); + Q_ASSERT(!ItemHandler::isFree(m_items[removeIndex])); + + //Find the free item that is nearest to the target position in the item order + int currentItem = *m_centralFreeItem; + + int lowerBound = 0; //The minimum position the searched item can have + int upperBound = m_itemCount; //The lowest known position the searched item can _not_ have + + if (*m_centralFreeItem == -1) { + *m_centralFreeItem = removeIndex; + Q_ASSERT(*m_centralFreeItem != -1); + ItemHandler::createFreeItem(m_items[*m_centralFreeItem]); + return; + } - ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() - void transferData(Data* newItems, uint newCount, int* newCentralFree = nullptr) { - DEBUG_FREEITEM_COUNT - //We only transfer into a new list when all the free items are used up - - //Create a list where only the non-free items exist - uint offset = 0; - for(uint a = 0; a < m_itemCount; ++a) { - if(!ItemHandler::isFree(m_items[a])) { - newItems[offset] = m_items[a]; - ++offset; - } + //Now go down the chain, always into the items direction + ///@todo make the structure better: Don't just put left/right child, but also swap when neede + /// to balance the tree + while (1) { + Q_ASSERT(removeIndex != currentItem); + Data& current(m_items[currentItem]); + ++m_insertedAtDepth; + if (removeIndex < currentItem) { + upperBound = currentItem; + //Follow left child + if (ItemHandler::leftChild(current) != -1) { + //Continue traversing + currentItem = ItemHandler::leftChild(current); + Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); + } else { + //The to-be deleted item is before current, and can be added as left child to current + int item = findItem(m_remove, lowerBound, upperBound); + Q_ASSERT(item == removeIndex); + ItemHandler::createFreeItem(m_items[item]); + Q_ASSERT(item == -1 || item < currentItem); + ItemHandler::setLeftChild(current, item); + Q_ASSERT(item >= lowerBound && item < upperBound); + break; + } + } else { + lowerBound = currentItem + 1; + //Follow right child + if (ItemHandler::rightChild(current) != -1) { + //Continue traversing + currentItem = ItemHandler::rightChild(current); + Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); + } else { + //The to-be deleted item is behind current, and can be added as right child to current + int item = findItem(m_remove, lowerBound, upperBound); + Q_ASSERT(item == removeIndex); + ItemHandler::createFreeItem(m_items[item]); + Q_ASSERT(item == -1 || currentItem < item); + ItemHandler::setRightChild(current, item); + Q_ASSERT(item >= lowerBound && item < upperBound); + break; } - Q_ASSERT(offset == newCount); - - if(newCentralFree) - m_centralFreeItem = newCentralFree; - *m_centralFreeItem = -1; - m_items = newItems; - m_itemCount = newCount; - DEBUG_FREEITEM_COUNT } + } - private: - void verifyTreeConsistent(int position, int lowerBound, int upperBound) { - if(position == -1) - return; - Q_ASSERT(lowerBound <= position && position < upperBound); - verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); - verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); - } - - uint countFreeItems(int item) const { - if(item == -1) - return 0; - const Data& current(m_items[item]); - - return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); - } + DEBUG_FREEITEM_COUNT + } - int findItem(const Data& data, uint start, uint end) { - EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); - return alg.indexOf(data, start, end); - } + void debugFreeItemCount() + { + uint count = 0; + for (uint a = 0; a < m_itemCount; ++a) { + if (ItemHandler::isFree(m_items[a])) + ++count; + } - void apply() { - DEBUG_FREEITEM_COUNT - - int removeIndex = findItem(m_remove, 0, m_itemCount); - Q_ASSERT(removeIndex != -1); - Q_ASSERT(!ItemHandler::isFree(m_items[removeIndex])); - - //Find the free item that is nearest to the target position in the item order - int currentItem = *m_centralFreeItem; - - int lowerBound = 0; //The minimum position the searched item can have - int upperBound = m_itemCount; //The lowest known position the searched item can _not_ have - - if(*m_centralFreeItem == -1) { - *m_centralFreeItem = removeIndex; - Q_ASSERT(*m_centralFreeItem != -1); - ItemHandler::createFreeItem(m_items[*m_centralFreeItem]); - return; - } - - //Now go down the chain, always into the items direction - ///@todo make the structure better: Don't just put left/right child, but also swap when neede - /// to balance the tree - while(1) { - Q_ASSERT(removeIndex != currentItem); - Data& current(m_items[currentItem]); - ++m_insertedAtDepth; - if(removeIndex < currentItem) { - upperBound = currentItem; - //Follow left child - if(ItemHandler::leftChild(current) != -1) { - //Continue traversing - currentItem = ItemHandler::leftChild(current); - Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); - }else{ - //The to-be deleted item is before current, and can be added as left child to current - int item = findItem(m_remove, lowerBound, upperBound); - Q_ASSERT(item == removeIndex); - ItemHandler::createFreeItem(m_items[item]); - Q_ASSERT(item == -1 || item < currentItem); - ItemHandler::setLeftChild(current, item); - Q_ASSERT(item >= lowerBound && item < upperBound); - break; - } - }else{ - lowerBound = currentItem+1; - //Follow right child - if(ItemHandler::rightChild(current) != -1) { - //Continue traversing - currentItem = ItemHandler::rightChild(current); - Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); - }else{ - //The to-be deleted item is behind current, and can be added as right child to current - int item = findItem(m_remove, lowerBound, upperBound); - Q_ASSERT(item == removeIndex); - ItemHandler::createFreeItem(m_items[item]); - Q_ASSERT(item == -1 || currentItem < item); - ItemHandler::setRightChild(current, item); - Q_ASSERT(item >= lowerBound && item < upperBound); - break; - } - } - } - - DEBUG_FREEITEM_COUNT - } - - void debugFreeItemCount() { - uint count = 0; - for(uint a = 0; a < m_itemCount; ++a) { - if(ItemHandler::isFree(m_items[a])) - ++count; - } - uint counted = countFreeItems(*m_centralFreeItem); - Q_ASSERT(count == counted); - Q_UNUSED(counted); - } - - const Data& m_remove; - Data* m_items; - uint m_itemCount; - int* m_centralFreeItem; - int m_insertedAtDepth; - }; + uint counted = countFreeItems(*m_centralFreeItem); + Q_ASSERT(count == counted); + Q_UNUSED(counted); + } + + const Data& m_remove; + Data* m_items; + uint m_itemCount; + int* m_centralFreeItem; + int m_insertedAtDepth; +}; } #endif diff --git a/kdevplatform/util/environmentprofilelist.cpp b/kdevplatform/util/environmentprofilelist.cpp index 12c81368e2..2b933f9487 100644 --- a/kdevplatform/util/environmentprofilelist.cpp +++ b/kdevplatform/util/environmentprofilelist.cpp @@ -1,219 +1,219 @@ /* This file is part of KDevelop Copyright 2007 Andreas Pakulat 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 "environmentprofilelist.h" #include #include #include #include #include #include namespace KDevelop { class EnvironmentProfileListPrivate { public: - QMap > m_profiles; + QMap> m_profiles; QString m_defaultProfileName; }; } using namespace KDevelop; namespace { namespace Strings { // TODO: migrate to more consistent key term "Default Environment Profile" inline QString defaultEnvProfileKey() { return QStringLiteral("Default Environment Group"); } inline QString envGroup() { return QStringLiteral("Environment Settings"); } // TODO: migrate to more consistent key term "Profile List" inline QString profileListKey() { return QStringLiteral("Group List"); } inline QString defaultProfileName() { return QStringLiteral("default"); } } void decode(KConfig* config, EnvironmentProfileListPrivate* d) { - KConfigGroup cfg( config, Strings::envGroup() ); + KConfigGroup cfg(config, Strings::envGroup()); d->m_defaultProfileName = cfg.readEntry(Strings::defaultEnvProfileKey(), Strings::defaultProfileName()); - const QStringList profileNames = cfg.readEntry(Strings::profileListKey(), QStringList{Strings::defaultProfileName()}); + const QStringList profileNames = + cfg.readEntry(Strings::profileListKey(), QStringList{Strings::defaultProfileName()}); for (const auto& profileName : profileNames) { KConfigGroup envgrp(&cfg, profileName); - QMap variables; - foreach( const QString &varname, envgrp.keyList() ) - { - variables[varname] = envgrp.readEntry( varname, QString() ); + QMap variables; + foreach (const QString& varname, envgrp.keyList()) { + variables[varname] = envgrp.readEntry(varname, QString()); } + d->m_profiles.insert(profileName, variables); } } void encode(KConfig* config, EnvironmentProfileListPrivate* d) { - KConfigGroup cfg( config, Strings::envGroup() ); + KConfigGroup cfg(config, Strings::envGroup()); cfg.writeEntry(Strings::defaultEnvProfileKey(), d->m_defaultProfileName); cfg.writeEntry(Strings::profileListKey(), d->m_profiles.keys()); - foreach( const QString &group, cfg.groupList() ) - { + foreach (const QString& group, cfg.groupList()) { if (!d->m_profiles.contains(group)) { - cfg.deleteGroup( group ); + cfg.deleteGroup(group); } } - for (auto it = d->m_profiles.cbegin(), itEnd = d->m_profiles.cend(); it!=itEnd; ++it) { - KConfigGroup envgrp( &cfg, it.key() ); + + for (auto it = d->m_profiles.cbegin(), itEnd = d->m_profiles.cend(); it != itEnd; ++it) { + KConfigGroup envgrp(&cfg, it.key()); envgrp.deleteGroup(); const auto val = it.value(); - for(auto it2 = val.cbegin(), it2End = val.cend(); it2!=it2End; ++it2) - { - envgrp.writeEntry( it2.key(), *it2 ); + for (auto it2 = val.cbegin(), it2End = val.cend(); it2 != it2End; ++it2) { + envgrp.writeEntry(it2.key(), *it2); } } + cfg.sync(); } } EnvironmentProfileList::EnvironmentProfileList(const EnvironmentProfileList& rhs) : d(new EnvironmentProfileListPrivate(*rhs.d)) { } EnvironmentProfileList& EnvironmentProfileList::operator=(const EnvironmentProfileList& rhs) { *d = *rhs.d; return *this; } EnvironmentProfileList::EnvironmentProfileList(const KSharedConfigPtr& config) - : d( new EnvironmentProfileListPrivate ) + : d(new EnvironmentProfileListPrivate) { decode(config.data(), d.data()); } EnvironmentProfileList::EnvironmentProfileList(KConfig* config) : d(new EnvironmentProfileListPrivate) { decode(config, d.data()); } EnvironmentProfileList::~EnvironmentProfileList() = default; QMap EnvironmentProfileList::variables(const QString& profileName) const { return d->m_profiles[profileName.isEmpty() ? d->m_defaultProfileName : profileName]; } QMap& EnvironmentProfileList::variables(const QString& profileName) { return d->m_profiles[profileName.isEmpty() ? d->m_defaultProfileName : profileName]; } - QString EnvironmentProfileList::defaultProfileName() const { return d->m_defaultProfileName; } void EnvironmentProfileList::setDefaultProfile(const QString& profileName) { if (profileName.isEmpty() || !d->m_profiles.contains(profileName)) { return; } d->m_defaultProfileName = profileName; } void EnvironmentProfileList::saveSettings(KConfig* config) const { encode(config, d.data()); config->sync(); } void EnvironmentProfileList::loadSettings(KConfig* config) { d->m_profiles.clear(); decode(config, d.data()); } QStringList EnvironmentProfileList::profileNames() const { return d->m_profiles.keys(); } void EnvironmentProfileList::removeProfile(const QString& profileName) { d->m_profiles.remove(profileName); } EnvironmentProfileList::EnvironmentProfileList() : d(new EnvironmentProfileListPrivate) { } -QStringList EnvironmentProfileList::createEnvironment(const QString& profileName, const QStringList& defaultEnvironment) const +QStringList EnvironmentProfileList::createEnvironment(const QString& profileName, + const QStringList& defaultEnvironment) const { QMap retMap; for (const QString& line : defaultEnvironment) { QString varName = line.section(QLatin1Char('='), 0, 0); QString varValue = line.section(QLatin1Char('='), 1); - retMap.insert( varName, varValue ); + retMap.insert(varName, varValue); } if (!profileName.isEmpty()) { - const auto userMap = variables(profileName); + const auto userMap = variables(profileName); - for( QMap::const_iterator it = userMap.constBegin(); - it != userMap.constEnd(); ++it ) - { - retMap.insert( it.key(), it.value() ); - } + for (QMap::const_iterator it = userMap.constBegin(); + it != userMap.constEnd(); ++it) { + retMap.insert(it.key(), it.value()); + } } QStringList env; env.reserve(retMap.size()); - for( QMap::const_iterator it = retMap.constBegin(); - it != retMap.constEnd(); ++it ) - { + for (QMap::const_iterator it = retMap.constBegin(); + it != retMap.constEnd(); ++it) { env << it.key() + QLatin1Char('=') + it.value(); } return env; } void KDevelop::expandVariables(QMap& variables, const QProcessEnvironment& environment) { QRegularExpression rVar(QStringLiteral("(? 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. */ #ifndef KDEVPLATFORM_ENVIRONMENTPROFILELIST_H #define KDEVPLATFORM_ENVIRONMENTPROFILELIST_H #include "utilexport.h" #include class QProcessEnvironment; class KConfig; -template class QMap; +template class QMap; class QString; class QStringList; -namespace KDevelop -{ +namespace KDevelop { /** * This class manages a list of environment profiles, each profile containing a number * of environment variables and their values. * * The class is constructed from a KConfig object for easy usage in the plugins. * * The methods to change the environments is protected to disallow access to those methods * from plugins, only the environment widget is allowed to change them. * * Example Usage * \code * KSharedConfigPtr config = KSharedConfig::openConfig(); * EnvironmentProfileList env(config); * KConfigGroup cfg(config, "QMake Builder"); * QMap myenvVars = env.variables( cfg.readEntry("QMake Environment") ); * \endcode * * Two entries are used by this class: * "Default Environment Group" and "Environment Variables". * * "Default Environment Variables" stores the default profile that should be used if the * user didn't select a profile via a plugins configuration dialog. * * "Environment Variables" entry stores the actual list of * . The profilename can't contain '%' or '='. * For example, suppose that two configuration, say "release" and "debug" exist. * Then the actual contents of .kdev4 project file will be * * \code * [Environment Settings] * Default Environment Group=debug * Environment Variables=debug_PATH=/home/kde-devel/usr/bin,release_PATH=/usr/bin * \endcode * */ class KDEVPLATFORMUTIL_EXPORT EnvironmentProfileList { public: EnvironmentProfileList(const EnvironmentProfileList& rhs); EnvironmentProfileList& operator=(const EnvironmentProfileList& rhs); /** * Creates an a list of environment profiles from a KConfig object * @param config the KConfig object to read the environment profiles from */ explicit EnvironmentProfileList(const KSharedConfigPtr& config); - explicit EnvironmentProfileList(KConfig* config ); + explicit EnvironmentProfileList(KConfig* config); ~EnvironmentProfileList(); /** * Creates a merged environment between the defaults specified by * \a defaultEnvironment and those saved in \a profileName */ QStringList createEnvironment(const QString& profileName, const QStringList& defaultEnvironment) const; /** * returns the variables that are set for a given profile. * This function provides read-only access to the environment * @param profileName the name of the profile for which the environment should be returned * @return a map containing the environment variables for this profile, or an empty map if the profile doesn't exist in this list */ QMap variables(const QString& profileName) const; /** * returns the name of the default profile * The default profile should be used by plugins unless the user chooses a different profile * @return the name of the default profile, defaults to "default" */ QString defaultProfileName() const; /** * Fetch the list of names of known profiles from the list * @return the list of profile names */ QStringList profileNames() const; protected: EnvironmentProfileList(); /** * returns the variables that are set for a given profile. * This function provides write access to the environment, so new variables can be inserted, existing ones changed or deleted * * If a non-existing profile is specified this returns a new empty map and that way this function can be used to add a new profile * to the list of environment profiles * @param profileName the name of the profile for which the environment should be returned * @return a map containing the environment variables for this profile, or an empty map if the profile doesn't exist in this list */ QMap& variables(const QString& profileName); /** * Changes the default profile. * @param profileName the name of the new default profile, if a profile of this name doesn't exist the default profile is not changed */ void setDefaultProfile(const QString& profileName); /** * Stores the environment profiles in this list to the given KConfig object * @param config a KConfig object to which the environment settings should be stored */ - void saveSettings( KConfig* config ) const; + void saveSettings(KConfig* config) const; - void loadSettings( KConfig* config ); + void loadSettings(KConfig* config); void removeProfile(const QString& profileName); private: const QScopedPointer d; }; KDEVPLATFORMUTIL_EXPORT void expandVariables(QMap& variables, const QProcessEnvironment& environment); } #endif diff --git a/kdevplatform/util/environmentselectionmodel.cpp b/kdevplatform/util/environmentselectionmodel.cpp index f3fb0bb6c0..e37592f149 100644 --- a/kdevplatform/util/environmentselectionmodel.cpp +++ b/kdevplatform/util/environmentselectionmodel.cpp @@ -1,114 +1,111 @@ /* This file is part of KDevelop Copyright 2013 Ivan Shapovalov 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 "environmentselectionmodel.h" #include -namespace -{ +namespace { QStringList entriesFromEnv(const KDevelop::EnvironmentProfileList& env) { // We add an empty (i. e. default profile) entry to the front of the model's list. return QStringList(QString()) + env.profileNames(); } } -namespace KDevelop -{ +namespace KDevelop { -EnvironmentSelectionModel::EnvironmentSelectionModel( QObject* parent ) : - QStringListModel( parent ), - m_env( KSharedConfig::openConfig() ) +EnvironmentSelectionModel::EnvironmentSelectionModel(QObject* parent) : + QStringListModel(parent) + , m_env(KSharedConfig::openConfig()) { - setStringList( entriesFromEnv( m_env ) ); + setStringList(entriesFromEnv(m_env)); m_profilesLookupTable = stringList().toSet(); } -QVariant EnvironmentSelectionModel::headerData( int section, Qt::Orientation orientation, int role ) const +QVariant EnvironmentSelectionModel::headerData(int section, Qt::Orientation orientation, int role) const { - if( section != 0 || + if (section != 0 || orientation != Qt::Horizontal || - role != Qt::DisplayRole ) { + role != Qt::DisplayRole) { return QVariant(); } return i18nc("@title:column", "Profile"); } -QVariant EnvironmentSelectionModel::data( const QModelIndex& index, int role ) const +QVariant EnvironmentSelectionModel::data(const QModelIndex& index, int role) const { - QVariant nativeData = QStringListModel::data( index, Qt::DisplayRole ); + QVariant nativeData = QStringListModel::data(index, Qt::DisplayRole); QString profileName = nativeData.toString(); - switch( role ) { + switch (role) { case Qt::DisplayRole: - if( profileName.isEmpty() ) { + if (profileName.isEmpty()) { return i18nc("@item:inlistbox", "Use default profile (currently: %1)", m_env.defaultProfileName()); } if (!m_profilesLookupTable.contains(profileName)) { - return i18nc( "@item:inlistbox", "%1 (does not exist)", profileName ); + return i18nc("@item:inlistbox", "%1 (does not exist)", profileName); } return nativeData; case EffectiveNameRole: - if( profileName.isEmpty() ) { + if (profileName.isEmpty()) { return m_env.defaultProfileName(); } return nativeData; default: - return QStringListModel::data( index, role ); + return QStringListModel::data(index, role); } } -bool EnvironmentSelectionModel::setData( const QModelIndex& /*index*/, const QVariant& /*value*/, int /*role*/ ) +bool EnvironmentSelectionModel::setData(const QModelIndex& /*index*/, const QVariant& /*value*/, int /*role*/) { return false; } EnvironmentProfileList EnvironmentSelectionModel::environmentProfiles() const { return m_env; } void EnvironmentSelectionModel::reload() { m_env = EnvironmentProfileList(KSharedConfig::openConfig()); - setStringList( entriesFromEnv( m_env ) ); + setStringList(entriesFromEnv(m_env)); m_profilesLookupTable = stringList().toSet(); } -QString EnvironmentSelectionModel::reloadSelectedItem( const QString& currentProfile ) +QString EnvironmentSelectionModel::reloadSelectedItem(const QString& currentProfile) { if (m_profilesLookupTable.contains(currentProfile)) { return currentProfile; } else { return QString(); } } } // namespace KDevelop - diff --git a/kdevplatform/util/environmentselectionmodel.h b/kdevplatform/util/environmentselectionmodel.h index 25917ee5bb..78dc761955 100644 --- a/kdevplatform/util/environmentselectionmodel.h +++ b/kdevplatform/util/environmentselectionmodel.h @@ -1,74 +1,73 @@ /* This file is part of KDevelop Copyright 2013 Ivan Shapovalov 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. */ #ifndef ENVIRONMENTSELECTIONMODEL_H #define ENVIRONMENTSELECTIONMODEL_H #include "environmentprofilelist.h" #include #include -namespace KDevelop -{ +namespace KDevelop { class EnvironmentSelectionModel : public QStringListModel { Q_OBJECT public: enum SpecialRoles { EffectiveNameRole = Qt::UserRole + 1 }; - explicit EnvironmentSelectionModel( QObject* parent = nullptr ); + explicit EnvironmentSelectionModel(QObject* parent = nullptr); - QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - QVariant data( const QModelIndex& index, int role ) const override; - bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) override; + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; /** * @returns The @ref EnvironmentProfileList which has been used to populate this * model. */ EnvironmentProfileList environmentProfiles() const; /** * Reloads the model from the global config. */ void reload(); /** * Shall be used by views to update selection (current item) after the model has been reloaded. * * @param currentProfile Previous selected item. * @returns The item which shall become selected. */ - QString reloadSelectedItem( const QString& currentProfile ); + QString reloadSelectedItem(const QString& currentProfile); private: EnvironmentProfileList m_env; QSet m_profilesLookupTable; }; } // namespace KDevelop #endif // ENVIRONMENTSELECTIONMODEL_H diff --git a/kdevplatform/util/environmentselectionwidget.cpp b/kdevplatform/util/environmentselectionwidget.cpp index 2076008e73..1b8cf67da7 100644 --- a/kdevplatform/util/environmentselectionwidget.cpp +++ b/kdevplatform/util/environmentselectionwidget.cpp @@ -1,100 +1,101 @@ /* This file is part of KDevelop Copyright 2007 Dukju Ahn 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 "environmentselectionwidget.h" #include "environmentprofilelist.h" #include "environmentselectionmodel.h" #include #include #include #include #include -namespace KDevelop -{ +namespace KDevelop { class EnvironmentSelectionWidgetPrivate { public: KComboBox* comboBox; EnvironmentSelectionModel* model; EnvironmentSelectionWidget* owner; - explicit EnvironmentSelectionWidgetPrivate( EnvironmentSelectionWidget* _owner ) - : comboBox( new KComboBox( _owner ) ) - , model( new EnvironmentSelectionModel( _owner ) ) - , owner( _owner ) + explicit EnvironmentSelectionWidgetPrivate(EnvironmentSelectionWidget* _owner) + : comboBox(new KComboBox(_owner)) + , model(new EnvironmentSelectionModel(_owner)) + , owner(_owner) { - comboBox->setModel( model ); - comboBox->setEditable( false ); + comboBox->setModel(model); + comboBox->setEditable(false); } }; -EnvironmentSelectionWidget::EnvironmentSelectionWidget( QWidget *parent ) - : QWidget( parent ), d( new EnvironmentSelectionWidgetPrivate( this ) ) +EnvironmentSelectionWidget::EnvironmentSelectionWidget(QWidget* parent) + : QWidget(parent) + , d(new EnvironmentSelectionWidgetPrivate(this)) { // since 5.32 the signal is by default taken as set for the used property -#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5,32,0) - KConfigDialogManager::changedMap()->insert(QStringLiteral("KDevelop::EnvironmentSelectionWidget"), SIGNAL(currentProfileChanged(QString))); +#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 32, 0) + KConfigDialogManager::changedMap()->insert(QStringLiteral("KDevelop::EnvironmentSelectionWidget"), + SIGNAL(currentProfileChanged(QString))); #endif - setLayout( new QHBoxLayout( this ) ); - layout()->addWidget( d->comboBox ); - layout()->setMargin( 0 ); + setLayout(new QHBoxLayout(this)); + layout()->addWidget(d->comboBox); + layout()->setMargin(0); - setCurrentProfile( QString() ); // select the default profile + setCurrentProfile(QString()); // select the default profile connect(d->comboBox, &QComboBox::currentTextChanged, this, &EnvironmentSelectionWidget::currentProfileChanged); } EnvironmentSelectionWidget::~EnvironmentSelectionWidget() = default; QString EnvironmentSelectionWidget::currentProfile() const { - return d->model->index( d->comboBox->currentIndex(), 0 ).data( Qt::EditRole ).toString(); + return d->model->index(d->comboBox->currentIndex(), 0).data(Qt::EditRole).toString(); } -void EnvironmentSelectionWidget::setCurrentProfile( const QString& profile ) +void EnvironmentSelectionWidget::setCurrentProfile(const QString& profile) { - d->comboBox->setCurrentIndex( d->comboBox->findData( profile, Qt::EditRole ) ); + d->comboBox->setCurrentIndex(d->comboBox->findData(profile, Qt::EditRole)); emit currentProfileChanged(profile); } void EnvironmentSelectionWidget::reconfigure() { QString selectedProfile = currentProfile(); d->model->reload(); - setCurrentProfile( d->model->reloadSelectedItem( selectedProfile ) ); + setCurrentProfile(d->model->reloadSelectedItem(selectedProfile)); } QString EnvironmentSelectionWidget::effectiveProfileName() const { - return d->model->index( d->comboBox->currentIndex(), 0 ).data( EnvironmentSelectionModel::EffectiveNameRole ).toString(); + return d->model->index(d->comboBox->currentIndex(), + 0).data(EnvironmentSelectionModel::EffectiveNameRole).toString(); } EnvironmentProfileList EnvironmentSelectionWidget::environmentProfiles() const { return d->model->environmentProfiles(); } } - diff --git a/kdevplatform/util/environmentselectionwidget.h b/kdevplatform/util/environmentselectionwidget.h index 424ceb0e9e..230e7c910a 100644 --- a/kdevplatform/util/environmentselectionwidget.h +++ b/kdevplatform/util/environmentselectionwidget.h @@ -1,89 +1,88 @@ /* This file is part of KDevelop Copyright 2007 Dukju Ahn 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. */ #ifndef KDEVPLATFORM_ENVIRONMENTSELECTIONWIDGET_H #define KDEVPLATFORM_ENVIRONMENTSELECTIONWIDGET_H #include #include "utilexport.h" -namespace KDevelop -{ +namespace KDevelop { class EnvironmentProfileList; /** * Simple combobox which allows each plugin to decide which environment * variable group to use. * * Can be used just like a KComboBox in Configuration dialogs including usage * with KConfigXT. * * @note The widget is populated and defaulted automatically. * */ class KDEVPLATFORMUTIL_EXPORT EnvironmentSelectionWidget : public QWidget { Q_OBJECT - Q_PROPERTY( QString currentProfile READ currentProfile WRITE setCurrentProfile NOTIFY currentProfileChanged USER true ) + Q_PROPERTY(QString currentProfile READ currentProfile WRITE setCurrentProfile NOTIFY currentProfileChanged USER true) public: - explicit EnvironmentSelectionWidget( QWidget *parent = nullptr ); + explicit EnvironmentSelectionWidget(QWidget* parent = nullptr); ~EnvironmentSelectionWidget() override; /** * @returns The currently selected environment profile name, as written to KConfigXT */ QString currentProfile() const; /** * Sets the environment profile to be written to KConfigXT and updates the combo-box. * * @param text The environment profile name to select */ - void setCurrentProfile( const QString& text ); + void setCurrentProfile(const QString& text); /** * @returns The currently effective environment profile name (like @ref currentProfile(), * but with empty value resolved to the default profile). */ QString effectiveProfileName() const; /** * @returns The @ref EnvironmentProfileList which has been used to populate this * widget. */ EnvironmentProfileList environmentProfiles() const; public Q_SLOTS: /** * Makes the widget re-read its environment group list. */ void reconfigure(); Q_SIGNALS: void currentProfileChanged(const QString& currentProfile); private: const QScopedPointer d; friend class EnvironmentSelectionWidgetPrivate; }; } #endif diff --git a/kdevplatform/util/executecompositejob.cpp b/kdevplatform/util/executecompositejob.cpp index a390056a3a..1518cdb400 100644 --- a/kdevplatform/util/executecompositejob.cpp +++ b/kdevplatform/util/executecompositejob.cpp @@ -1,146 +1,146 @@ /* This file is part of KDevelop Copyright 2007-2008 Hamish Rodda 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 "executecompositejob.h" #include "debug.h" -namespace KDevelop -{ +namespace KDevelop { class ExecuteCompositeJobPrivate { public: void startNextJob(KJob* job); bool m_killing = false; bool m_abortOnError = true; int m_jobIndex = -1; int m_jobCount = 0; }; ExecuteCompositeJob::ExecuteCompositeJob(QObject* parent, const QList& jobs) -: KCompositeJob(parent), d(new ExecuteCompositeJobPrivate) + : KCompositeJob(parent) + , d(new ExecuteCompositeJobPrivate) { setCapabilities(Killable); qCDebug(UTIL) << "execute composite" << jobs; for (KJob* job : jobs) { if (!job) { qCWarning(UTIL) << "Added null job!"; continue; } addSubjob(job); - if (objectName().isEmpty()) setObjectName(job->objectName()); + if (objectName().isEmpty()) + setObjectName(job->objectName()); } } ExecuteCompositeJob::~ExecuteCompositeJob() = default; void ExecuteCompositeJobPrivate::startNextJob(KJob* job) { ++m_jobIndex; qCDebug(UTIL) << "starting:" << job; job->start(); } void ExecuteCompositeJob::start() { - if(hasSubjobs()) { + if (hasSubjobs()) { d->startNextJob(subjobs().first()); } else { emitResult(); } } bool ExecuteCompositeJob::addSubjob(KJob* job) { const bool success = KCompositeJob::addSubjob(job); if (!success) return false; ++d->m_jobCount; connect(job, SIGNAL(percent(KJob*,ulong)), this, SLOT(slotPercent(KJob*,ulong))); return true; } void ExecuteCompositeJob::slotPercent(KJob* job, unsigned long percent) { Q_UNUSED(job); Q_ASSERT(d->m_jobCount > 0); Q_ASSERT(d->m_jobIndex >= 0 && d->m_jobIndex < d->m_jobCount); - const float ratio = (float)d->m_jobIndex / d->m_jobCount; - const unsigned long totalPercent = ratio * 100 + ((float)percent / d->m_jobCount); + const float ratio = ( float )d->m_jobIndex / d->m_jobCount; + const unsigned long totalPercent = ratio * 100 + (( float )percent / d->m_jobCount); emitPercent(totalPercent, 100); } void ExecuteCompositeJob::slotResult(KJob* job) { disconnect(job, SIGNAL(percent(KJob*,ulong)), this, SLOT(slotPercent(KJob*,ulong))); // jobIndex + 1 because this job just finished const float ratio = d->m_jobIndex != -1 ? (d->m_jobIndex + 1.0) / d->m_jobCount : 1.0; emitPercent(ratio * 100, 100); - qCDebug(UTIL) << "finished: "<< job << job->error() << "percent:" << ratio * 100; + qCDebug(UTIL) << "finished: " << job << job->error() << "percent:" << ratio * 100; bool emitDone = false; if (d->m_abortOnError && job->error()) { qCDebug(UTIL) << "JOB ERROR:" << job->error() << job->errorString(); KCompositeJob::slotResult(job); // calls emitResult() emitDone = true; } else removeSubjob(job); if (hasSubjobs() && !error() && !d->m_killing) { qCDebug(UTIL) << "remaining: " << subjobs().count() << subjobs(); d->startNextJob(subjobs().first()); } else if (!emitDone) { setError(job->error()); setErrorText(job->errorString()); emitResult(); } } bool ExecuteCompositeJob::doKill() { qCDebug(UTIL) << "Killing subjobs:" << subjobs().size(); d->m_killing = true; - while(hasSubjobs()) { + while (hasSubjobs()) { KJob* j = subjobs().first(); if (!j || j->kill()) { removeSubjob(j); } else { return false; } } return true; } void ExecuteCompositeJob::setAbortOnError(bool abort) { d->m_abortOnError = abort; } } - diff --git a/kdevplatform/util/executecompositejob.h b/kdevplatform/util/executecompositejob.h index b268586e1e..f07e04a63e 100644 --- a/kdevplatform/util/executecompositejob.h +++ b/kdevplatform/util/executecompositejob.h @@ -1,57 +1,57 @@ /* This file is part of KDevelop Copyright 2007-2008 Hamish Rodda 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. */ #ifndef KDEVPLATFORM_EXECUTECOMPOSITEJOB_H #define KDEVPLATFORM_EXECUTECOMPOSITEJOB_H #include #include "utilexport.h" -template class QList; +template class QList; -namespace KDevelop -{ +namespace KDevelop { class KDEVPLATFORMUTIL_EXPORT ExecuteCompositeJob : public KCompositeJob { -Q_OBJECT + Q_OBJECT + public: explicit ExecuteCompositeJob(QObject* parent = nullptr, const QList& jobs = {}); ~ExecuteCompositeJob() override; void start() override; void setAbortOnError(bool abort); public Q_SLOTS: bool addSubjob(KJob* job) override; void slotResult(KJob* job) override; protected Q_SLOTS: virtual void slotPercent(KJob* job, unsigned long percent); protected: bool doKill() override; private: const QScopedPointer d; }; } #endif diff --git a/kdevplatform/util/focusedtreeview.cpp b/kdevplatform/util/focusedtreeview.cpp index 919a1515b5..ba0b319e32 100644 --- a/kdevplatform/util/focusedtreeview.cpp +++ b/kdevplatform/util/focusedtreeview.cpp @@ -1,143 +1,144 @@ /* 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 "focusedtreeview.h" #include #include namespace KDevelop { class FocusedTreeViewPrivate { public: bool autoScrollAtEnd = false; QTimer timer; bool wasAtEnd = false; int insertedBegin = -1; int insertedEnd = -1; }; FocusedTreeView::FocusedTreeView(QWidget* parent) : QTreeView(parent) , d(new FocusedTreeViewPrivate) { setVerticalScrollMode(ScrollPerItem); d->timer.setInterval(200); d->timer.setSingleShot(true); connect(&d->timer, &QTimer::timeout, this, &FocusedTreeView::delayedAutoScrollAndResize); - connect(verticalScrollBar(), &QScrollBar::valueChanged, &d->timer, static_cast(&QTimer::start)); + connect(verticalScrollBar(), &QScrollBar::valueChanged, &d->timer, + static_cast(&QTimer::start)); } FocusedTreeView::~FocusedTreeView() { } void FocusedTreeView::setModel(QAbstractItemModel* newModel) { if (QAbstractItemModel* oldModel = model()) { disconnect(oldModel, nullptr, this, nullptr); } QTreeView::setModel(newModel); if (newModel) { connect(newModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &FocusedTreeView::rowsAboutToBeInserted); connect(newModel, &QAbstractItemModel::rowsRemoved, this, &FocusedTreeView::rowsRemoved); } } void FocusedTreeView::setAutoScrollAtEnd(bool enable) { d->autoScrollAtEnd = enable; } -int FocusedTreeView::sizeHintForColumn(int column) const { +int FocusedTreeView::sizeHintForColumn(int column) const +{ QModelIndex i = indexAt(QPoint(0, 0)); - if(i.isValid()) { + if (i.isValid()) { QSize hint = sizeHintForIndex(i); int maxWidth = hint.width(); - if(hint.height()) { + if (hint.height()) { //Also consider one item above, because else we can get problems with //the vertical scroll-bar - for(int a = -1; a < (height() / hint.height())+1; ++a) { - QModelIndex current = i.sibling(i.row()+a, column); + for (int a = -1; a < (height() / hint.height()) + 1; ++a) { + QModelIndex current = i.sibling(i.row() + a, column); QSize tempHint = sizeHintForIndex(current); - if(tempHint.width() > maxWidth) + if (tempHint.width() > maxWidth) maxWidth = tempHint.width(); } } return maxWidth; } return columnWidth(column); } void FocusedTreeView::delayedAutoScrollAndResize() { if (!model()) { // might happen on shutdown return; } if (d->autoScrollAtEnd && d->insertedBegin != -1 && d->wasAtEnd && d->insertedEnd == model()->rowCount()) { scrollToBottom(); } - for(int a = 0; a < model()->columnCount(); ++a) + for (int a = 0; a < model()->columnCount(); ++a) resizeColumnToContents(a); d->insertedBegin = -1; // Timer is single-shot, but the code above may have recursively restarted the timer // (e.g. via the connection from the scroll-bar signal), so explicitly prevent a redundant // call here. d->timer.stop(); } void FocusedTreeView::rowsAboutToBeInserted(const QModelIndex&, int first, int last) { if (d->insertedBegin == -1) { d->insertedBegin = d->insertedEnd = first; d->wasAtEnd = true; QModelIndex last = model()->index(model()->rowCount() - 1, 0); if (last.isValid()) { auto rect = visualRect(last); d->wasAtEnd = rect.isValid() && viewport()->rect().intersects(rect); } } if (first == d->insertedEnd) { d->insertedEnd = last + 1; } if (!d->timer.isActive()) d->timer.start(); } // Removing rows can make longer rows move into the visible region, so we also must trigger a // column resize void FocusedTreeView::rowsRemoved(const QModelIndex& parent, int first, int last) { QTreeView::rowsRemoved(parent, first, last); if (!d->timer.isActive()) d->timer.start(); } - } diff --git a/kdevplatform/util/focusedtreeview.h b/kdevplatform/util/focusedtreeview.h index a8b5b5e73d..7dd7b1b97f 100644 --- a/kdevplatform/util/focusedtreeview.h +++ b/kdevplatform/util/focusedtreeview.h @@ -1,65 +1,67 @@ /* This file is part of KDevelop * * Copyright 2009 David Nolden * * 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. */ #ifndef KDEVPLATFORM_FOCUSEDTREEVIEW_H #define KDEVPLATFORM_FOCUSEDTREEVIEW_H #include #include "utilexport.h" namespace KDevelop { /** * Specialized version of QTreeView, that allows efficiently managing an extremely * long list of items, by focusing the size of the horizontal scroll-bars only on the currently * visible items. * * In addition, this class provides optional automatic scrolling when rows are inserted at the end. * * @warning Either the scroll-mode ScrollPerItem must be enabled, or the uniformRowHeight flag, for this to work efficiently. * @warning This currently only works with flat list models(todo). */ -class KDEVPLATFORMUTIL_EXPORT FocusedTreeView : public QTreeView { +class KDEVPLATFORMUTIL_EXPORT FocusedTreeView : public QTreeView +{ Q_OBJECT - public: - explicit FocusedTreeView(QWidget* parent) ; - ~FocusedTreeView() override; - /** - * When enabled, automatically scroll to bottom when new rows are inserted at the end - * and the end was previously visible. (Default: false) - */ - void setAutoScrollAtEnd(bool enable); +public: + explicit FocusedTreeView(QWidget* parent); + ~FocusedTreeView() override; - void setModel(QAbstractItemModel* model) override; - int sizeHintForColumn(int column) const override; + /** + * When enabled, automatically scroll to bottom when new rows are inserted at the end + * and the end was previously visible. (Default: false) + */ + void setAutoScrollAtEnd(bool enable); - private Q_SLOTS: - void rowsAboutToBeInserted(const QModelIndex& parent, int first, int last); - void rowsRemoved(const QModelIndex& parent, int first, int last); - void delayedAutoScrollAndResize(); + void setModel(QAbstractItemModel* model) override; + int sizeHintForColumn(int column) const override; - private: - const QScopedPointer d; +private Q_SLOTS: + void rowsAboutToBeInserted(const QModelIndex& parent, int first, int last); + void rowsRemoved(const QModelIndex& parent, int first, int last); + void delayedAutoScrollAndResize(); + +private: + const QScopedPointer d; }; } #endif // KDEVPLATFORM_FOCUSEDLISTVIEW_H diff --git a/kdevplatform/util/foregroundlock.cpp b/kdevplatform/util/foregroundlock.cpp index 05d93f8c8b..9f4658106f 100644 --- a/kdevplatform/util/foregroundlock.cpp +++ b/kdevplatform/util/foregroundlock.cpp @@ -1,242 +1,243 @@ /* Copyright 2010 David Nolden 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 "foregroundlock.h" #include #include #include - using namespace KDevelop; namespace { QMutex internalMutex; QMutex tryLockMutex; QMutex waitMutex; QMutex finishMutex; QWaitCondition condition; volatile QThread* holderThread = nullptr; volatile int recursion = 0; -void lockForegroundMutexInternal() { - if(holderThread == QThread::currentThread()) - { +void lockForegroundMutexInternal() +{ + if (holderThread == QThread::currentThread()) { // We already have the mutex ++recursion; - }else{ + } else { internalMutex.lock(); Q_ASSERT(recursion == 0 && holderThread == nullptr); holderThread = QThread::currentThread(); recursion = 1; } } -bool tryLockForegroundMutexInternal(int interval = 0) { - if(holderThread == QThread::currentThread()) - { +bool tryLockForegroundMutexInternal(int interval = 0) +{ + if (holderThread == QThread::currentThread()) { // We already have the mutex ++recursion; return true; - }else{ - if(internalMutex.tryLock(interval)) - { + } else { + if (internalMutex.tryLock(interval)) { Q_ASSERT(recursion == 0 && holderThread == nullptr); holderThread = QThread::currentThread(); recursion = 1; return true; - }else{ + } else { return false; } } } -void unlockForegroundMutexInternal(bool duringDestruction = false) { +void unlockForegroundMutexInternal(bool duringDestruction = false) +{ /// Note: QThread::currentThread() might already be invalid during destruction. if (!duringDestruction) { Q_ASSERT(holderThread == QThread::currentThread()); } Q_ASSERT(recursion > 0); recursion -= 1; - if(recursion == 0) - { + if (recursion == 0) { holderThread = nullptr; internalMutex.unlock(); } } } ForegroundLock::ForegroundLock(bool lock) { - if(lock) + if (lock) relock(); } void KDevelop::ForegroundLock::relock() { Q_ASSERT(!m_locked); - if(!QApplication::instance() || // Initialization isn't complete yet + if (!QApplication::instance() || // Initialization isn't complete yet QThread::currentThread() == QApplication::instance()->thread() || // We're the main thread (deadlock might happen if we'd enter the trylock loop) - holderThread == QThread::currentThread()) // We already have the foreground lock (deadlock might happen if we'd enter the trylock loop) - { + holderThread == QThread::currentThread()) { // We already have the foreground lock (deadlock might happen if we'd enter the trylock loop) lockForegroundMutexInternal(); - }else{ + } else { QMutexLocker lock(&tryLockMutex); - while(!tryLockForegroundMutexInternal(10)) - { + while (!tryLockForegroundMutexInternal(10)) { // In case an additional event-loop was started from within the foreground, we send // events to the foreground to temporarily release the lock. - class ForegroundReleaser : public DoInForeground { - public: - void doInternal() override { + class ForegroundReleaser : public DoInForeground + { +public: + void doInternal() override + { // By locking the mutex, we make sure that the requester is actually waiting for the condition waitMutex.lock(); // Now we release the foreground lock TemporarilyReleaseForegroundLock release; // And signalize to the requester that we've released it condition.wakeAll(); // Allow the requester to actually wake up, by unlocking m_waitMutex waitMutex.unlock(); // Now wait until the requester is ready QMutexLocker lock(&finishMutex); } }; static ForegroundReleaser releaser; QMutexLocker lockWait(&waitMutex); QMutexLocker lockFinish(&finishMutex); QMetaObject::invokeMethod(&releaser, "doInternalSlot", Qt::QueuedConnection); // We limit the waiting time here, because sometimes it may happen that the foreground-lock is released, // and the foreground is waiting without an event-loop running. (For example through TemporarilyReleaseForegroundLock) condition.wait(&waitMutex, 30); - if(tryLockForegroundMutexInternal()) - { + if (tryLockForegroundMutexInternal()) { //success break; - }else{ + } else { //Probably a third thread has creeped in and //got the foreground lock before us. Just try again. } } } m_locked = true; Q_ASSERT(holderThread == QThread::currentThread()); Q_ASSERT(recursion > 0); } bool KDevelop::ForegroundLock::isLockedForThread() { return QThread::currentThread() == holderThread; } bool KDevelop::ForegroundLock::tryLock() { - if(tryLockForegroundMutexInternal()) - { + if (tryLockForegroundMutexInternal()) { m_locked = true; return true; } return false; } void KDevelop::ForegroundLock::unlock() { Q_ASSERT(m_locked); unlockForegroundMutexInternal(); m_locked = false; } TemporarilyReleaseForegroundLock::TemporarilyReleaseForegroundLock() { Q_ASSERT(holderThread == QThread::currentThread()); m_recursion = 0; - while(holderThread == QThread::currentThread()) - { + while (holderThread == QThread::currentThread()) { unlockForegroundMutexInternal(); ++m_recursion; } } TemporarilyReleaseForegroundLock::~TemporarilyReleaseForegroundLock() { - for(int a = 0; a < m_recursion; ++a) + for (int a = 0; a < m_recursion; ++a) lockForegroundMutexInternal(); + Q_ASSERT(recursion == m_recursion && holderThread == QThread::currentThread()); } KDevelop::ForegroundLock::~ForegroundLock() { - if(m_locked) + if (m_locked) unlock(); } bool KDevelop::ForegroundLock::isLocked() const { return m_locked; } namespace KDevelop { - void DoInForeground::doIt() { - if(QThread::currentThread() == QApplication::instance()->thread()) - { - // We're already in the foreground, just call the handler code - doInternal(); - }else{ - QMutexLocker lock(&m_mutex); - QMetaObject::invokeMethod(this, "doInternalSlot", Qt::QueuedConnection); - m_wait.wait(&m_mutex); - } +void DoInForeground::doIt() +{ + if (QThread::currentThread() == QApplication::instance()->thread()) { + // We're already in the foreground, just call the handler code + doInternal(); + } else { + QMutexLocker lock(&m_mutex); + QMetaObject::invokeMethod(this, "doInternalSlot", Qt::QueuedConnection); + m_wait.wait(&m_mutex); } +} - DoInForeground::~DoInForeground() { - } +DoInForeground::~DoInForeground() +{ +} - DoInForeground::DoInForeground() { - moveToThread(QApplication::instance()->thread()); - } +DoInForeground::DoInForeground() +{ + moveToThread(QApplication::instance()->thread()); +} - void DoInForeground::doInternalSlot() - { - VERIFY_FOREGROUND_LOCKED +void DoInForeground::doInternalSlot() +{ + VERIFY_FOREGROUND_LOCKED doInternal(); - QMutexLocker lock(&m_mutex); - m_wait.wakeAll(); - } + QMutexLocker lock(&m_mutex); + m_wait.wakeAll(); +} } // Important: The foreground lock has to be held by default, so lock it during static initialization -static struct StaticLock { - StaticLock() { +static struct StaticLock +{ + StaticLock() + { lockForegroundMutexInternal(); } - ~StaticLock() { + ~StaticLock() + { unlockForegroundMutexInternal(true); } } staticLock; diff --git a/kdevplatform/util/foregroundlock.h b/kdevplatform/util/foregroundlock.h index 42f3fa77b6..f54c84365d 100644 --- a/kdevplatform/util/foregroundlock.h +++ b/kdevplatform/util/foregroundlock.h @@ -1,106 +1,109 @@ /* Copyright 2010 David Nolden 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. */ #ifndef KDEVPLATFORM_FOREGROUNDLOCK_H #define KDEVPLATFORM_FOREGROUNDLOCK_H #include "utilexport.h" #include #include #include namespace KDevelop { /** * A locking object that locks the resources that are associated to the main thread. When this lock is held, * you can call any thread-unsafe functions, because the foreground thread is locked in an event. * * The lock always becomes available when the foreground thread stops processing events. * * @warning There is one simple rule you must always follow to prevent deadlocks: * @em Never lock anything before locking the foreground mutex!! * That also means that you must not take this lock in contexts where * you don't know what other mutexes might be locked. * * @warning Objects that have QObject as base always get the thread they were created in assigned (see thread affinity, QObject::moveToThread), * which seriously affects the objects functionality regarding signals/slots. * The foreground lock does not change the thread affinity, so holding the foreground lock does not fully equal being in the foreground. * It may generally be unsafe to call foreground functions that create QObjects from within the background. */ class KDEVPLATFORMUTIL_EXPORT ForegroundLock { - public: - explicit ForegroundLock(bool lock = true); - ~ForegroundLock(); - void unlock(); - void relock(); - bool tryLock(); - - /// Returns whether the current thread holds the foreground lock - static bool isLockedForThread(); - - bool isLocked() const; - - private: - ForegroundLock(const ForegroundLock& rhs); - ForegroundLock& operator=(const ForegroundLock& rhs); - bool m_locked = false; +public: + explicit ForegroundLock(bool lock = true); + ~ForegroundLock(); + void unlock(); + void relock(); + bool tryLock(); + + /// Returns whether the current thread holds the foreground lock + static bool isLockedForThread(); + + bool isLocked() const; + +private: + ForegroundLock(const ForegroundLock& rhs); + ForegroundLock& operator=(const ForegroundLock& rhs); + bool m_locked = false; }; /** * Use this object if you want to temporarily release the foreground lock, * for example when sleeping in the foreground thread, or when waiting in the foreground * thread for a background thread which should get the chance to lock the foreground. * * While this object is alive, you _must not_ access any non-threadsafe resources * that belong to the foreground, and you must not start an event-loop. */ -class KDEVPLATFORMUTIL_EXPORT TemporarilyReleaseForegroundLock { +class KDEVPLATFORMUTIL_EXPORT TemporarilyReleaseForegroundLock +{ public: TemporarilyReleaseForegroundLock(); ~TemporarilyReleaseForegroundLock(); + private: TemporarilyReleaseForegroundLock(const TemporarilyReleaseForegroundLock&); TemporarilyReleaseForegroundLock& operator=(const TemporarilyReleaseForegroundLock& rhs); int m_recursion; }; #define VERIFY_FOREGROUND_LOCKED Q_ASSERT(KDevelop::ForegroundLock::isLockedForThread()); class KDEVPLATFORMUTIL_EXPORT DoInForeground : public QObject { -Q_OBJECT + Q_OBJECT + public: - DoInForeground() ; - ~DoInForeground() override ; + DoInForeground(); + ~DoInForeground() override; - void doIt() ; + void doIt(); private Q_SLOTS: void doInternalSlot(); private: virtual void doInternal() = 0; QMutex m_mutex; QWaitCondition m_wait; }; } #endif diff --git a/kdevplatform/util/formattinghelpers.cpp b/kdevplatform/util/formattinghelpers.cpp index 628722dac8..88c7a32a5c 100644 --- a/kdevplatform/util/formattinghelpers.cpp +++ b/kdevplatform/util/formattinghelpers.cpp @@ -1,194 +1,188 @@ /* This file is part of KDevelop * Copyright 2011 David Nolden 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "formattinghelpers.h" #include "debug.h" #include -namespace KDevelop -{ +namespace KDevelop { ///Matches the given prefix to the given text, ignoring all whitespace ///Returns -1 if mismatched, else the position in @p text where the @p prefix match ends int matchPrefixIgnoringWhitespace(const QString& text, const QString& prefix, const QString& fuzzyCharacters) { int prefixPos = 0; int textPos = 0; while (prefixPos < prefix.length() && textPos < text.length()) { - skipWhiteSpace: +skipWhiteSpace: while (prefixPos < prefix.length() && prefix[prefixPos].isSpace()) ++prefixPos; while (textPos < text.length() && text[textPos].isSpace()) ++textPos; - if(prefixPos == prefix.length() || textPos == text.length()) + if (prefixPos == prefix.length() || textPos == text.length()) break; - if(prefix[prefixPos] != text[textPos]) - { + if (prefix[prefixPos] != text[textPos]) { bool skippedFuzzy = false; - while( prefixPos < prefix.length() && fuzzyCharacters.indexOf(prefix[prefixPos]) != -1 ) - { + while (prefixPos < prefix.length() && fuzzyCharacters.indexOf(prefix[prefixPos]) != -1) { ++prefixPos; skippedFuzzy = true; } - while( textPos < text.length() && fuzzyCharacters.indexOf(text[textPos]) != -1 ) - { + while (textPos < text.length() && fuzzyCharacters.indexOf(text[textPos]) != -1) { ++textPos; skippedFuzzy = true; } - if( skippedFuzzy ) + if (skippedFuzzy) goto skipWhiteSpace; return -1; } ++prefixPos; ++textPos; } return textPos; } -static QString reverse( const QString& str ) { - QString ret; - ret.reserve(str.length()); - for(int a = str.length()-1; a >= 0; --a) - ret.append(str[a]); +static QString reverse(const QString& str) +{ + QString ret; + ret.reserve(str.length()); + for (int a = str.length() - 1; a >= 0; --a) + ret.append(str[a]); - return ret; + return ret; } // Returns the text start position with all whitespace that is redundant in the given context skipped int skipRedundantWhiteSpace(const QString& context, const QString& text, int tabWidth) { - if( context.isEmpty() || !context[context.size()-1].isSpace() || text.isEmpty() || !text[0].isSpace() ) + if (context.isEmpty() || !context[context.size() - 1].isSpace() || text.isEmpty() || !text[0].isSpace()) return 0; int textPosition = 0; // Extract trailing whitespace in the context - int contextPosition = context.size()-1; - while( contextPosition > 0 && context[contextPosition-1].isSpace() ) + int contextPosition = context.size() - 1; + while (contextPosition > 0 && context[contextPosition - 1].isSpace()) --contextPosition; - int textWhitespaceEnd = 0; - while(textWhitespaceEnd < text.size() && text[textWhitespaceEnd].isSpace()) + while (textWhitespaceEnd < text.size() && text[textWhitespaceEnd].isSpace()) ++textWhitespaceEnd; QString contextWhiteSpace = context.mid(contextPosition); contextPosition = 0; QString textWhiteSpace = text.left(textWhitespaceEnd); // Step 1: Remove redundant newlines - while(contextWhiteSpace.contains(QLatin1Char('\n')) && textWhiteSpace.contains(QLatin1Char('\n'))) - { - int contextOffset = contextWhiteSpace.indexOf(QLatin1Char('\n'))+1; - int textOffset = textWhiteSpace.indexOf(QLatin1Char('\n'))+1; + while (contextWhiteSpace.contains(QLatin1Char('\n')) && textWhiteSpace.contains(QLatin1Char('\n'))) { + int contextOffset = contextWhiteSpace.indexOf(QLatin1Char('\n')) + 1; + int textOffset = textWhiteSpace.indexOf(QLatin1Char('\n')) + 1; contextPosition += contextOffset; contextWhiteSpace.remove(0, contextOffset); textPosition += textOffset; textWhiteSpace.remove(0, textOffset); } int contextOffset = 0; int textOffset = 0; // Skip redundant ordinary whitespace - while(contextOffset < contextWhiteSpace.size() && textOffset < textWhiteSpace.size() && contextWhiteSpace[contextOffset].isSpace() && contextWhiteSpace[contextOffset] != QLatin1Char('\n') && textWhiteSpace[textOffset].isSpace() && textWhiteSpace[textOffset] != QLatin1Char('\n')) - { - bool contextWasTab = contextWhiteSpace[contextOffset] == QLatin1Char('\t'); - bool textWasTab = textWhiteSpace[contextOffset] == QLatin1Char('\t'); + while (contextOffset < contextWhiteSpace.size() && textOffset < textWhiteSpace.size() && + contextWhiteSpace[contextOffset].isSpace() && contextWhiteSpace[contextOffset] != QLatin1Char('\n') && + textWhiteSpace[textOffset].isSpace() && textWhiteSpace[textOffset] != QLatin1Char('\n')) { + bool contextWasTab = contextWhiteSpace[contextOffset] == QLatin1Char('\t'); + bool textWasTab = textWhiteSpace[contextOffset] == QLatin1Char('\t'); ++contextOffset; ++textOffset; - if( contextWasTab != textWasTab ) - { + if (contextWasTab != textWasTab) { // Problem: We have a mismatch of tabs and/or ordinary whitespaces - if( contextWasTab ) - { - for( int s = 1; s < tabWidth; ++s ) - if (textOffset < textWhiteSpace.size() && textWhiteSpace[textOffset] == QLatin1Char(' ')) - ++textOffset; - }else if( textWasTab ) - { - for( int s = 1; s < tabWidth; ++s ) - if (contextOffset < contextWhiteSpace.size() && contextWhiteSpace[contextOffset] == QLatin1Char(' ')) - ++contextOffset; - } + if (contextWasTab) { + for (int s = 1; s < tabWidth; ++s) + if (textOffset < textWhiteSpace.size() && textWhiteSpace[textOffset] == QLatin1Char(' ')) + ++textOffset; + } else if (textWasTab) { + for (int s = 1; s < tabWidth; ++s) + if (contextOffset < contextWhiteSpace.size() && + contextWhiteSpace[contextOffset] == QLatin1Char(' ')) + ++contextOffset; + } } } - return textPosition+textOffset; + return textPosition + textOffset; } -QString extractFormattedTextFromContext( const QString& _formattedMergedText, const QString& text, const QString& leftContext, const QString& rightContext, int tabWidth, const QString& fuzzyCharacters) +QString extractFormattedTextFromContext(const QString& _formattedMergedText, const QString& text, + const QString& leftContext, const QString& rightContext, int tabWidth, + const QString& fuzzyCharacters) { QString formattedMergedText = _formattedMergedText; //Now remove "leftContext" and "rightContext" from the sides - if(!leftContext.isEmpty()) { - int endOfLeftContext = matchPrefixIgnoringWhitespace( formattedMergedText, leftContext, QString() ); - if(endOfLeftContext == -1) { + if (!leftContext.isEmpty()) { + int endOfLeftContext = matchPrefixIgnoringWhitespace(formattedMergedText, leftContext, QString()); + if (endOfLeftContext == -1) { // Try 2: Ignore the fuzzy characters while matching - endOfLeftContext = matchPrefixIgnoringWhitespace( formattedMergedText, leftContext, fuzzyCharacters ); - if(endOfLeftContext == -1) { + endOfLeftContext = matchPrefixIgnoringWhitespace(formattedMergedText, leftContext, fuzzyCharacters); + if (endOfLeftContext == -1) { qCWarning(UTIL) << "problem matching the left context"; return text; } } int startOfWhiteSpace = endOfLeftContext; // Include all leading whitespace - while(startOfWhiteSpace > 0 && formattedMergedText[startOfWhiteSpace-1].isSpace()) + while (startOfWhiteSpace > 0 && formattedMergedText[startOfWhiteSpace - 1].isSpace()) --startOfWhiteSpace; formattedMergedText = formattedMergedText.mid(startOfWhiteSpace); - int skip = skipRedundantWhiteSpace( leftContext, formattedMergedText, tabWidth ); + int skip = skipRedundantWhiteSpace(leftContext, formattedMergedText, tabWidth); formattedMergedText = formattedMergedText.mid(skip); } - if(!rightContext.isEmpty()) { + if (!rightContext.isEmpty()) { //Add a whitespace behind the text for matching, so that we definitely capture all trailing whitespace int endOfText = matchPrefixIgnoringWhitespace(formattedMergedText, text + QLatin1Char(' '), QString()); - if(endOfText == -1) { + if (endOfText == -1) { // Try 2: Ignore the fuzzy characters while matching endOfText = matchPrefixIgnoringWhitespace(formattedMergedText, text + QLatin1Char(' '), fuzzyCharacters); - if(endOfText == -1) { + if (endOfText == -1) { qCWarning(UTIL) << "problem matching the text while formatting"; return text; } } formattedMergedText.truncate(endOfText); - int skip = skipRedundantWhiteSpace( reverse(rightContext), reverse(formattedMergedText), tabWidth ); + int skip = skipRedundantWhiteSpace(reverse(rightContext), reverse(formattedMergedText), tabWidth); formattedMergedText.chop(skip); } return formattedMergedText; } } - - diff --git a/kdevplatform/util/formattinghelpers.h b/kdevplatform/util/formattinghelpers.h index 0a7fb12069..0c682d7fde 100644 --- a/kdevplatform/util/formattinghelpers.h +++ b/kdevplatform/util/formattinghelpers.h @@ -1,44 +1,47 @@ /* This file is part of KDevelop * Copyright 2011 David Nolden 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_FORMATTINGHELPERS_H #define KDEVPLATFORM_FORMATTINGHELPERS_H #include "utilexport.h" #include namespace KDevelop { /** * Helps extracting a re-formatted version of a text fragment, within a specific left and right context. * The re-formatting must be an operation which only changes whitespace, and keeps whitespace boundaries * between identifiers intact. If this is not the case, the original text is returned. * * @param formattedMergedText The re-formatted merged text: format(leftContext + text + rightContext) * @param text The text fragment of which the re-formatted version will be returned * @param leftContext The left context of the text fragment * @param rightContext The right context of the text fragment * @param tabWidth The width of one tab, required while matching tabs vs. spaces * @param fuzzyCharacters Characters which are ignored in case of mismatches - * + * * @return The re-formatted version of @p text * */ -KDEVPLATFORMUTIL_EXPORT QString extractFormattedTextFromContext(const QString& formattedMergedText, const QString& text, const QString& leftContext, const QString& rightContext, int tabWidth = 4, const QString& fuzzyCharacters = QStringLiteral("{}()/*/")); +KDEVPLATFORMUTIL_EXPORT QString extractFormattedTextFromContext(const QString& formattedMergedText, const QString& text, + const QString& leftContext, const QString& rightContext, + int tabWidth = 4, + const QString& fuzzyCharacters = QStringLiteral( "{}()/*/" )); } #endif // KDEVPLATFORM_FORMATTINGHELPERS_H diff --git a/kdevplatform/util/jobstatus.cpp b/kdevplatform/util/jobstatus.cpp index 56f39af602..7564b0c2e5 100644 --- a/kdevplatform/util/jobstatus.cpp +++ b/kdevplatform/util/jobstatus.cpp @@ -1,81 +1,80 @@ /* * Copyright 2015 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 "jobstatus.h" #include #include using namespace KDevelop; - class KDevelop::JobStatusPrivate { public: explicit JobStatusPrivate(JobStatus* q) : q(q) {} void slotPercent(KJob* job, unsigned long percent); JobStatus* q; KJob* m_job; QString m_statusName; }; void JobStatusPrivate::slotPercent(KJob* job, long unsigned int percent) { Q_UNUSED(job); emit q->showProgress(q, 0, 100, percent); } JobStatus::JobStatus(KJob* job, const QString& statusName, QObject* parent) : QObject(parent) , d(new JobStatusPrivate(this)) { d->m_job = job; d->m_statusName = statusName; connect(job, &KJob::infoMessage, this, [this](KJob*, const QString& plain, const QString&) { emit showMessage(this, plain); }); connect(job, &KJob::finished, this, [this, job]() { if (job->error() == KJob::KilledJobError) { emit showErrorMessage(i18n("Task aborted")); } emit hideProgress(this); deleteLater(); }); // no new-signal-slot syntax possible :( connect(job, SIGNAL(percent(KJob*,ulong)), this, SLOT(slotPercent(KJob*,ulong))); } JobStatus::~JobStatus() { } QString JobStatus::statusName() const { return d->m_statusName; } #include "moc_jobstatus.cpp" diff --git a/kdevplatform/util/jobstatus.h b/kdevplatform/util/jobstatus.h index 9e9ad06865..cab7fa8527 100644 --- a/kdevplatform/util/jobstatus.h +++ b/kdevplatform/util/jobstatus.h @@ -1,68 +1,69 @@ /* * Copyright 2015 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 . * */ #pragma once #include "utilexport.h" #include #include class KJob; namespace KDevelop { /** * @brief Class for making KJobs exposable to the IStatus interface * * Using this class you use any KJob-based class as source for IStatus updates */ -class KDEVPLATFORMUTIL_EXPORT JobStatus : public QObject, public IStatus +class KDEVPLATFORMUTIL_EXPORT JobStatus : public QObject + , public IStatus { Q_OBJECT Q_INTERFACES(KDevelop::IStatus) public: /** * Construct a JobStatus observing the job @p job * * @note As soon as @p job finished, this object will be auto-deleted */ explicit JobStatus(KJob* job, const QString& statusName = QString(), QObject* parent = nullptr); ~JobStatus() override; QString statusName() const override; Q_SIGNALS: void clearMessage(KDevelop::IStatus*) override; void hideProgress(KDevelop::IStatus*) override; void showErrorMessage(const QString& message, int timeout = 0) override; void showMessage(KDevelop::IStatus*, const QString& message, int timeout = 0) override; void showProgress(KDevelop::IStatus*, int minimum, int maximum, int value) override; private: const QScopedPointer d; Q_PRIVATE_SLOT(d, void slotPercent(KJob*, unsigned long)) }; } diff --git a/kdevplatform/util/kdevformatfile.cpp b/kdevplatform/util/kdevformatfile.cpp index 3bf3460e0d..87dd931c1a 100644 --- a/kdevplatform/util/kdevformatfile.cpp +++ b/kdevplatform/util/kdevformatfile.cpp @@ -1,152 +1,152 @@ /* Copyright 2016 Anton Anikin 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 "kdevformatfile.h" #include #include #include #include namespace KDevelop { static const QString formatFileName = QStringLiteral("format_sources"); KDevFormatFile::KDevFormatFile(const QString& origFilePath, const QString& tempFilePath) : m_origFilePath(origFilePath) , m_tempFilePath(tempFilePath) { } bool KDevFormatFile::find() { QDir srcDir(QFileInfo(m_origFilePath).canonicalPath()); while (!srcDir.isRoot()) { if (srcDir.exists(formatFileName)) { QDir::setCurrent(srcDir.canonicalPath()); qStdOut() << "found \"" << QFileInfo(srcDir.canonicalPath() + QDir::separator() + formatFileName).canonicalFilePath() << "\"\n"; return true; } srcDir.cdUp(); } return false; } bool KDevFormatFile::read() { static const QChar delimeter = QLatin1Char(':'); QFile formatFile(formatFileName); if (!formatFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qStdOut() << "unable to open \"" << formatFileName << "\"\n"; return false; } int lineNumber = 0; QString line; QStringList wildcards; QString command; while (!formatFile.atEnd()) { ++lineNumber; line = QString::fromUtf8(formatFile.readLine().trimmed()); if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) continue; if (line.indexOf(delimeter) < 0) { // We found the simple syntax without wildcards, and only with the command wildcards.clear(); m_formatLines.append({wildcards, line}); } else { // We found the correct syntax with "wildcards : command" wildcards = line.section(delimeter, 0, 0).split(QLatin1Char(' '), QString::SkipEmptyParts); command = line.section(delimeter, 1).trimmed(); if (wildcards.isEmpty()) { qStdOut() << formatFileName << ":" << lineNumber << ": error: empty wildcard, skip the line\n"; continue; } m_formatLines.append({wildcards, command}); } } if (m_formatLines.isEmpty()) { qStdOut() << formatFileName << ": error: no commands are found\n"; return false; } return true; } bool KDevFormatFile::apply() { foreach (const KDevFormatLine& formatLine, m_formatLines) { if (formatLine.wildcards.isEmpty()) { qStdOut() << "matched \"" << m_origFilePath << "\" without wildcard"; return executeCommand(formatLine.command); } - foreach(const QString& wildcard, formatLine.wildcards) { + foreach (const QString& wildcard, formatLine.wildcards) { if (QDir::match(QDir::current().canonicalPath() + QDir::separator() + wildcard.trimmed(), m_origFilePath)) { qStdOut() << "matched \"" << m_origFilePath << "\" with wildcard \"" << wildcard << '\"'; return executeCommand(formatLine.command); } } } qStdOut() << formatFileName << ": error: no commands applicable to \"" << m_origFilePath << "\"\n"; return false; } bool KDevFormatFile::executeCommand(QString command) { qStdOut() << ", using command \"" << command << "\"\n"; command.replace(QStringLiteral("$ORIGFILE"), m_origFilePath); command.replace(QStringLiteral("$TMPFILE"), m_tempFilePath); #ifdef Q_OS_WIN int execResult = QProcess::execute(QStringLiteral("cmd"), {QStringLiteral("/c"), command}); #else int execResult = QProcess::execute(QStringLiteral("sh"), {QStringLiteral("-c"), command}); #endif if (execResult == -2) { qStdOut() << "command \"" << command << "\" failed to start\n"; return false; } if (execResult == -1) { qStdOut() << "command \"" << command << "\" crashed\n"; return false; } return true; } } diff --git a/kdevplatform/util/kdevformatfile.h b/kdevplatform/util/kdevformatfile.h index c568b1e482..6d23f5c74c 100644 --- a/kdevplatform/util/kdevformatfile.h +++ b/kdevplatform/util/kdevformatfile.h @@ -1,76 +1,76 @@ /* Copyright 2016 Anton Anikin 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. */ #pragma once #include #include #include namespace KDevelop { struct KDevFormatLine { QStringList wildcards; QString command; }; class KDevFormatFile { public: KDevFormatFile(const QString& origFilePath, const QString& tempFilePath); bool find(); bool read(); bool apply(); private: bool executeCommand(QString command); QString m_origFilePath; QString m_tempFilePath; QVector m_formatLines; }; class AutoFlushingQTextStream : public QTextStream { public: AutoFlushingQTextStream(FILE* f, QIODevice::OpenMode o) : QTextStream(f, o) { } template - AutoFlushingQTextStream& operator << (T&& s) + AutoFlushingQTextStream& operator <<(T&& s) { - *((QTextStream*) this) << std::forward(s); + *(( QTextStream* ) this) << std::forward(s); flush(); return *this; } }; inline AutoFlushingQTextStream& qStdOut() { static AutoFlushingQTextStream s{stdout, QIODevice::WriteOnly | QIODevice::Text}; return s; } } Q_DECLARE_TYPEINFO(KDevelop::KDevFormatLine, Q_MOVABLE_TYPE); diff --git a/kdevplatform/util/kdevstringhandler.cpp b/kdevplatform/util/kdevstringhandler.cpp index e9d0dd4ac7..72ce174879 100644 --- a/kdevplatform/util/kdevstringhandler.cpp +++ b/kdevplatform/util/kdevstringhandler.cpp @@ -1,244 +1,244 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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. - - This file mostly code takes from Qt's QSettings class, the copyright + + This file mostly code takes from Qt's QSettings class, the copyright header from that file follows: - + **************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** **************************************************************************** */ - #include "kdevstringhandler.h" #include #include #include #include #include #include #include #include -namespace KDevelop +namespace KDevelop { +QString joinWithEscaping(const QStringList& input, const QChar& joinchar, const QChar& escapechar) { - QString joinWithEscaping( const QStringList& input, const QChar& joinchar, const QChar& escapechar ) - { - QStringList tmp = input; - return tmp.replaceInStrings( joinchar, QString( joinchar ) + QString( escapechar ) ).join( joinchar ); - } - - QStringList splitWithEscaping( const QString& input, const QChar& splitchar, const QChar& escapechar ) - { - enum State { Normal, SeenEscape } state; - - state = Normal; - - QStringList result; - QString currentstring; - for( int i = 0; i < input.size(); i++ ) { - switch( state ) { - case Normal: - if( input[i] == escapechar ) { - state = SeenEscape; - } else if( input[i] == splitchar ) { - result << currentstring; - currentstring.clear(); - } else { - currentstring += input[i]; - } - break; - case SeenEscape: - currentstring += input[i]; - state = Normal; - break; + QStringList tmp = input; + return tmp.replaceInStrings(joinchar, QString(joinchar) + QString(escapechar)).join(joinchar); +} + +QStringList splitWithEscaping(const QString& input, const QChar& splitchar, const QChar& escapechar) +{ + enum State { Normal, SeenEscape } state; + + state = Normal; + + QStringList result; + QString currentstring; + for (int i = 0; i < input.size(); i++) { + switch (state) { + case Normal: + if (input[i] == escapechar) { + state = SeenEscape; + } else if (input[i] == splitchar) { + result << currentstring; + currentstring.clear(); + } else { + currentstring += input[i]; } + break; + case SeenEscape: + currentstring += input[i]; + state = Normal; + break; } - if( !currentstring.isEmpty() ) { - result << currentstring; - } - return result; } - - QVariant stringToQVariant(const QString& s) - { - // Taken from qsettings.cpp, stringToVariant() - if (s.startsWith(QLatin1Char('@'))) { - if (s.endsWith(QLatin1Char(')'))) { - if (s.startsWith(QLatin1String("@Variant("))) { - QByteArray a(s.toLatin1().mid(9)); - QDataStream stream(&a, QIODevice::ReadOnly); - stream.setVersion(QDataStream::Qt_4_4); - QVariant result; - stream >> result; - return result; - } + + if (!currentstring.isEmpty()) { + result << currentstring; + } + return result; +} + +QVariant stringToQVariant(const QString& s) +{ + // Taken from qsettings.cpp, stringToVariant() + if (s.startsWith(QLatin1Char('@'))) { + if (s.endsWith(QLatin1Char(')'))) { + if (s.startsWith(QLatin1String("@Variant("))) { + QByteArray a(s.toLatin1().mid(9)); + QDataStream stream(&a, QIODevice::ReadOnly); + stream.setVersion(QDataStream::Qt_4_4); + QVariant result; + stream >> result; + return result; } } - return QVariant(); - } - - QString qvariantToString(const QVariant& variant) + return QVariant(); + +} + +QString qvariantToString(const QVariant& variant) +{ + // Taken from qsettings.cpp, variantToString() + QByteArray a; { - // Taken from qsettings.cpp, variantToString() - QByteArray a; - { - QDataStream s(&a, QIODevice::WriteOnly); - s.setVersion(QDataStream::Qt_4_4); - s << variant; - } - - QString result = QStringLiteral("@Variant(") + QString::fromLatin1(a.constData(), a.size()) + QLatin1Char(')'); - return result; - + QDataStream s(&a, QIODevice::WriteOnly); + s.setVersion(QDataStream::Qt_4_4); + s << variant; } - QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode) - { - switch (mode) { - case FastMode: { - QString result(s); - result.remove(QRegExp(QStringLiteral("<[^>]+>"))); - return result; - } - case CompleteMode: { - QTextDocument doc; - doc.setHtml(s); - return doc.toPlainText(); - } - } - return QString(); // never reached + QString result = QStringLiteral("@Variant(") + QString::fromLatin1(a.constData(), a.size()) + QLatin1Char(')'); + return result; + +} + +QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode) +{ + switch (mode) { + case FastMode: { + QString result(s); + result.remove(QRegExp(QStringLiteral("<[^>]+>"))); + return result; + } + case CompleteMode: { + QTextDocument doc; + doc.setHtml(s); + return doc.toPlainText(); + } } + return QString(); // never reached +} } QString KDevelop::stripAnsiSequences(const QString& str) { if (str.isEmpty()) { return QString(); // fast path } enum { PLAIN, ANSI_START, ANSI_CSI, ANSI_SEQUENCE, ANSI_WAITING_FOR_ST, ANSI_ST_STARTED } state = PLAIN; QString result; result.reserve(str.count()); for (const QChar c : str) { const auto val = c.unicode(); switch (state) { case PLAIN: if (val == 27) // 'ESC' state = ANSI_START; else if (val == 155) // equivalent to 'ESC'-'[' state = ANSI_CSI; else result.append(c); break; case ANSI_START: if (val == 91) // [ state = ANSI_CSI; else if (val == 80 || val == 93 || val == 94 || val == 95) // 'P', ']', '^' and '_' state = ANSI_WAITING_FOR_ST; else if (val >= 64 && val <= 95) state = PLAIN; else state = ANSI_SEQUENCE; break; case ANSI_CSI: if (val >= 64 && val <= 126) // Anything between '@' and '~' state = PLAIN; break; case ANSI_SEQUENCE: if (val >= 64 && val <= 95) // Anything between '@' and '_' state = PLAIN; break; case ANSI_WAITING_FOR_ST: if (val == 7) // 'BEL' state = PLAIN; else if (val == 27) // 'ESC' state = ANSI_ST_STARTED; break; case ANSI_ST_STARTED: if (val == 92) // '\' state = PLAIN; else state = ANSI_WAITING_FOR_ST; break; } } + return result; } void KDevelop::normalizeLineEndings(QByteArray& text) { for (int i = 0, s = text.size(); i < s; ++i) { if (text[i] != '\r') { continue; } if (i + 1 < s && text[i + 1] == '\n') { text.remove(i, 1); } else { text[i] = '\n'; } } } diff --git a/kdevplatform/util/kdevstringhandler.h b/kdevplatform/util/kdevstringhandler.h index b75af05e15..3e1a2d7562 100644 --- a/kdevplatform/util/kdevstringhandler.h +++ b/kdevplatform/util/kdevstringhandler.h @@ -1,76 +1,77 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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. */ #ifndef KDEVPLATFORM_KDEVSTRINGHANDLER_H #define KDEVPLATFORM_KDEVSTRINGHANDLER_H #include "utilexport.h" class QString; class QByteArray; class QChar; class QStringList; class QVariant; -namespace KDevelop -{ - KDEVPLATFORMUTIL_EXPORT QStringList splitWithEscaping( const QString& input, const QChar& splitChar, const QChar& escapeChar ); - KDEVPLATFORMUTIL_EXPORT QString joinWithEscaping( const QStringList& input, const QChar& joinChar, const QChar& escapeChar ); - - /** - * convert the @p variant into a string which can then be stored - * easily in a KConfig entry. This supports any QVariant type (including custom types) - * for which there is a QDataStream operator defined - * @returns a QString containing the data from the QVariant. - */ - KDEVPLATFORMUTIL_EXPORT QString qvariantToString( const QVariant& variant ); - - /** - * convert the @p s into a QVariant, usually the string is read from KConfig. - * This supports any QVariant type (including custom types) - * for which there is a QDataStream operator defined - * @returns a QVariant created from the bytearray - */ - KDEVPLATFORMUTIL_EXPORT QVariant stringToQVariant( const QString& s ); +namespace KDevelop { +KDEVPLATFORMUTIL_EXPORT QStringList splitWithEscaping(const QString& input, const QChar& splitChar, + const QChar& escapeChar); +KDEVPLATFORMUTIL_EXPORT QString joinWithEscaping(const QStringList& input, const QChar& joinChar, + const QChar& escapeChar); - enum HtmlToPlainTextMode { - FastMode, /**< Fast (conversion via regular expression) */ - CompleteMode, /**< Slower, but with expected behavior (conversion via QTextDocument::toPlainText). +/** +* convert the @p variant into a string which can then be stored +* easily in a KConfig entry. This supports any QVariant type (including custom types) +* for which there is a QDataStream operator defined +* @returns a QString containing the data from the QVariant. +*/ +KDEVPLATFORMUTIL_EXPORT QString qvariantToString(const QVariant& variant); + +/** +* convert the @p s into a QVariant, usually the string is read from KConfig. +* This supports any QVariant type (including custom types) +* for which there is a QDataStream operator defined +* @returns a QVariant created from the bytearray +*/ +KDEVPLATFORMUTIL_EXPORT QVariant stringToQVariant(const QString& s); + +enum HtmlToPlainTextMode { + FastMode, /**< Fast (conversion via regular expression) */ + CompleteMode, /**< Slower, but with expected behavior (conversion via QTextDocument::toPlainText). This also replaces
with newline chars, for example. */ - }; +}; - /** - * Strip HTML tags from string @p s - * - * @return String no longer containing any HTML tags - */ - KDEVPLATFORMUTIL_EXPORT QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode = FastMode); +/** + * Strip HTML tags from string @p s + * + * @return String no longer containing any HTML tags + */ +KDEVPLATFORMUTIL_EXPORT QString htmlToPlainText(const QString& s, HtmlToPlainTextMode mode = FastMode); - /** - * Strip ANSI sequences from string @p str - */ - KDEVPLATFORMUTIL_EXPORT QString stripAnsiSequences(const QString& str); +/** + * Strip ANSI sequences from string @p str + */ +KDEVPLATFORMUTIL_EXPORT QString stripAnsiSequences(const QString& str); - /** - * Replace all occurrences of "\r" or "\r\n" in @p text with "\n". - */ - KDEVPLATFORMUTIL_EXPORT void normalizeLineEndings(QByteArray& text); +/** + * Replace all occurrences of "\r" or "\r\n" in @p text with "\n". + */ +KDEVPLATFORMUTIL_EXPORT void normalizeLineEndings(QByteArray& text); } #endif // KDEVPLATFORM_KDEVSTRINGHANDLER_H diff --git a/kdevplatform/util/kdevvarlengtharray.h b/kdevplatform/util/kdevvarlengtharray.h index 133ef326d3..c7ff6ce29b 100644 --- a/kdevplatform/util/kdevvarlengtharray.h +++ b/kdevplatform/util/kdevvarlengtharray.h @@ -1,85 +1,86 @@ /* Copyright 2009 David Nolden Copyright 2011 Milian Wolff 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. */ #ifndef KDEVPLATFORM_KDEVVARLENGTHARRAY_H #define KDEVPLATFORM_KDEVVARLENGTHARRAY_H #include #include /** * Extended QVarLengthArray with additional convenience API. */ template -class KDevVarLengthArray : public QVarLengthArray +class KDevVarLengthArray : public QVarLengthArray { using Base = QVarLengthArray; public: using Base::QVarLengthArray; ///Removes exactly one occurrence of the given value from the array. Returns false if none was found. inline bool removeOne(const T& value); /// @return QList of items in this array QList toList() const; /// @return QVector of items in this array QVector toVector() const; }; -template +template Q_INLINE_TEMPLATE bool KDevVarLengthArray::removeOne(const T& value) { const int idx = Base::indexOf(value); if (idx == -1) { return false; } Base::remove(idx); return true; } -template -Q_OUTOFLINE_TEMPLATE QList< T > KDevVarLengthArray::toList() const +template +Q_OUTOFLINE_TEMPLATE QList KDevVarLengthArray::toList() const { QList ret; ret.reserve(Base::size()); const T* const end = Base::constEnd(); - for(const T* it = Base::constBegin(); it != end; ++it) { + for (const T* it = Base::constBegin(); it != end; ++it) { ret << *it; } return ret; } -template -Q_OUTOFLINE_TEMPLATE QVector< T > KDevVarLengthArray::toVector() const +template +Q_OUTOFLINE_TEMPLATE QVector KDevVarLengthArray::toVector() const { QVector ret; ret.reserve(Base::size()); const T* const end = Base::constEnd(); - for(const T* it = Base::constBegin(); it != end; ++it) { + for (const T* it = Base::constBegin(); it != end; ++it) { ret << *it; } return ret; } #endif // KDEVPLATFORM_KDEVVARLENGTHARRAY_H diff --git a/kdevplatform/util/ksharedobject.h b/kdevplatform/util/ksharedobject.h index be48139ac1..c9338ed7e1 100644 --- a/kdevplatform/util/ksharedobject.h +++ b/kdevplatform/util/ksharedobject.h @@ -1,74 +1,83 @@ /* Copyright 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_KSHAREDOBJECT_H #define KDEVPLATFORM_KSHAREDOBJECT_H #include #include namespace KDevelop { -struct FakeAtomic { - inline FakeAtomic(QObject& object, QSharedData& real) : m_object(object), m_real(real) { +struct FakeAtomic +{ + inline FakeAtomic(QObject& object, QSharedData& real) : m_object(object) + , m_real(real) + { } - inline operator int() const { - const int value = m_real.ref.loadAcquire(); - if(value == 0) - return 1; //Always return true, because we handle the deleting by ourself using deleteLater - return value; + inline operator int() const + { + const int value = m_real.ref.loadAcquire(); + if (value == 0) + return 1; //Always return true, because we handle the deleting by ourself using deleteLater + return value; } - - inline bool ref() { - return m_real.ref.ref(); + + inline bool ref() + { + return m_real.ref.ref(); } - - inline bool deref() { - bool ret = m_real.ref.deref(); - if(!ret) - m_object.deleteLater(); - return true; //Always return true, because we handle the deleting by ourself + inline bool deref() + { + bool ret = m_real.ref.deref(); + if (!ret) + m_object.deleteLater(); + + return true; //Always return true, because we handle the deleting by ourself } - inline int load() const { + inline int load() const + { return m_real.ref.load(); } - + QObject& m_object; QSharedData& m_real; }; /** * Wrapper around QSharedData for use with KSharedPtr when the object is based on QObject as well. * Instead of deleting the object once the reference-count reaches zero, QObject::deleteLater() is called. * This prevents a possible crash when the reference-count reaches zero during event-processing while the queue * contains events to be delivered to that object. * * Notice however that the object will not be deleted immediately, which may lead to unintended behavior. */ -struct KSharedObject : public QSharedData { - inline explicit KSharedObject(QObject& object) : ref(object, *this) { - } - - mutable FakeAtomic ref; +struct KSharedObject : public QSharedData +{ + inline explicit KSharedObject(QObject& object) : ref(object, *this) + { + } + + mutable FakeAtomic ref; }; } #endif diff --git a/kdevplatform/util/multilevellistview.cpp b/kdevplatform/util/multilevellistview.cpp index 70d73a5edf..a679711eff 100644 --- a/kdevplatform/util/multilevellistview.cpp +++ b/kdevplatform/util/multilevellistview.cpp @@ -1,466 +1,468 @@ /* 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 "multilevellistview.h" #include #include #include #include /** * Interface to set the label of a model. */ class LabeledProxy { public: virtual ~LabeledProxy() { } void setLabel(const QString& label) { m_label = label; } QVariant header(QAbstractItemModel* model, int section, Qt::Orientation orientation, int role) const { if (model && section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole) { return m_label; } else { return QVariant(); } } + protected: QString m_label; }; /** * The left-most view's model which only contains the root nodes of the source model. */ -class RootProxyModel : public QSortFilterProxyModel, public LabeledProxy +class RootProxyModel : public QSortFilterProxyModel + , public LabeledProxy { Q_OBJECT public: - explicit RootProxyModel( QObject* parent = nullptr ) - : QSortFilterProxyModel( parent ) + explicit RootProxyModel(QObject* parent = nullptr) + : QSortFilterProxyModel(parent) { } - bool filterAcceptsRow( int /*source_row*/, const QModelIndex& source_parent ) const override + bool filterAcceptsRow(int /*source_row*/, const QModelIndex& source_parent) const override { return !source_parent.isValid(); } - QVariant headerData( int section, Qt::Orientation orientation, int role ) const override + QVariant headerData(int section, Qt::Orientation orientation, int role) const override { return header(sourceModel(), section, orientation, role); } }; /** * A class that automatically updates its contents based on the selection in another view. */ -class SubTreeProxyModel : public KSelectionProxyModel, public LabeledProxy +class SubTreeProxyModel : public KSelectionProxyModel + , public LabeledProxy { Q_OBJECT public: - explicit SubTreeProxyModel( QItemSelectionModel* selectionModel, QObject* parent = nullptr ) - : KSelectionProxyModel( selectionModel, parent ) + explicit SubTreeProxyModel(QItemSelectionModel* selectionModel, QObject* parent = nullptr) + : KSelectionProxyModel(selectionModel, parent) {} - QVariant headerData( int section, Qt::Orientation orientation, int role ) const override + QVariant headerData(int section, Qt::Orientation orientation, int role) const override { return header(sourceModel(), section, orientation, role); } Qt::ItemFlags flags(const QModelIndex& index) const override { Qt::ItemFlags ret = KSelectionProxyModel::flags(index); if (filterBehavior() == KSelectionProxyModel::SubTreesWithoutRoots && hasChildren(index)) { // we want to select child items ret &= ~Qt::ItemIsSelectable; } return ret; } }; using namespace KDevelop; class KDevelop::MultiLevelListViewPrivate { public: explicit MultiLevelListViewPrivate(MultiLevelListView* view); ~MultiLevelListViewPrivate(); void viewSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void lastViewsContentsChanged(); void ensureViewSelected(QTreeView* view); /** * @param index index in any of our proxy models * @return an index in the source model */ QModelIndex mapToSource(QModelIndex index); /** * @param index an index in the source model * @return an index in the view's model at level @p level */ QModelIndex mapFromSource(QModelIndex index, int level); MultiLevelListView* view; int levels; QList views; QList proxies; QList layouts; QAbstractItemModel* model; }; MultiLevelListViewPrivate::MultiLevelListViewPrivate(MultiLevelListView* view_) -: view(view_) -, levels(0) -, model(nullptr) + : view(view_) + , levels(0) + , model(nullptr) { } MultiLevelListViewPrivate::~MultiLevelListViewPrivate() { } void MultiLevelListViewPrivate::viewSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { if (!current.isValid()) { // ignore, as we should always have some kind of selection return; } // figure out which proxy this signal belongs to auto* proxy = qobject_cast( const_cast(current.model())); Q_ASSERT(proxy); // what level is this proxy in int level = -1; - for(int i = 0; i < levels; ++i) { + for (int i = 0; i < levels; ++i) { if (views.at(i)->model() == proxy) { level = i; break; } } + Q_ASSERT(level >= 0 && level < levels); if (level + 1 == levels) { // right-most view if (proxy->hasIndex(0, 0, current)) { // select the first leaf node for this view QModelIndex idx = current; QModelIndex child = proxy->index(0, 0, idx); - while(child.isValid()) { + while (child.isValid()) { idx = child; child = proxy->index(0, 0, idx); } views.last()->setCurrentIndex(idx); return; } // signal that our actual selection has changed emit view->currentIndexChanged(mapToSource(current), mapToSource(previous)); } else { // some leftish view // ensure the next view's first item is selected QTreeView* treeView = views.at(level + 1); // we need to delay the call, because at this point the child view // will still have its old data which is going to be invalidated // right after this method exits // be we must not set the index to 0,0 here directly, since e.g. // MultiLevelListView::setCurrentIndex might have been used, which // sets a proper index already. QMetaObject::invokeMethod(view, "ensureViewSelected", Qt::QueuedConnection, Q_ARG(QTreeView*, treeView)); } } void MultiLevelListViewPrivate::lastViewsContentsChanged() { views.last()->expandAll(); } void MultiLevelListViewPrivate::ensureViewSelected(QTreeView* view) { if (!view->currentIndex().isValid()) { view->setCurrentIndex(view->model()->index(0, 0)); } } QModelIndex MultiLevelListViewPrivate::mapToSource(QModelIndex index) { if (!index.isValid()) { return index; } - while(index.model() != model) { + while (index.model() != model) { auto* proxy = qobject_cast( const_cast(index.model())); Q_ASSERT(proxy); index = proxy->mapToSource(index); Q_ASSERT(index.isValid()); } return index; } QModelIndex MultiLevelListViewPrivate::mapFromSource(QModelIndex index, int level) { if (!index.isValid()) { return index; } Q_ASSERT(index.model() == model); auto* proxy = qobject_cast(views.at(level)->model()); Q_ASSERT(proxy); // find all proxies between the source and our view QVector proxies; proxies << proxy; forever { auto* child = qobject_cast(proxy->sourceModel()); if (child) { proxy = child; proxies << proxy; } else { Q_ASSERT(proxy->sourceModel() == model); break; } } // iterate in reverse order to find the view's index - for(int i = proxies.size() - 1; i >= 0; --i) { + for (int i = proxies.size() - 1; i >= 0; --i) { proxy = proxies.at(i); index = proxy->mapFromSource(index); Q_ASSERT(index.isValid()); } + return index; } MultiLevelListView::MultiLevelListView(QWidget* parent, Qt::WindowFlags f) -: QWidget(parent, f) -, d(new MultiLevelListViewPrivate(this)) + : QWidget(parent, f) + , d(new MultiLevelListViewPrivate(this)) { setLayout(new QHBoxLayout()); layout()->setContentsMargins(0, 0, 0, 0); qRegisterMetaType("QTreeView*"); } MultiLevelListView::~MultiLevelListView() = default; int MultiLevelListView::levels() const { return d->levels; } void MultiLevelListView::setLevels(int levels) { qDeleteAll(d->views); qDeleteAll(d->proxies); qDeleteAll(d->layouts); d->views.clear(); d->proxies.clear(); d->layouts.clear(); d->levels = levels; d->views.reserve(levels); d->proxies.reserve(levels); d->layouts.reserve(levels); QTreeView* previousView = nullptr; - for (int i = 0; i < d->levels; ++i) - { + for (int i = 0; i < d->levels; ++i) { auto* levelLayout = new QVBoxLayout(); auto* view = new QTreeView(this); view->setContentsMargins(0, 0, 0, 0); // only the right-most view is decorated view->setRootIsDecorated(i + 1 == d->levels); view->setHeaderHidden(false); view->setSelectionMode(QAbstractItemView::SingleSelection); if (!previousView) { // the root, i.e. left-most view auto* root = new RootProxyModel(this); root->setDynamicSortFilter(true); d->proxies << root; root->setSourceModel(d->model); view->setModel(root); } else { auto* subTreeProxy = new SubTreeProxyModel(previousView->selectionModel(), this); if (i + 1 < d->levels) { // middel views only shows children of selection subTreeProxy->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection); } else { // right-most view shows the rest subTreeProxy->setFilterBehavior(KSelectionProxyModel::SubTreesWithoutRoots); } d->proxies << subTreeProxy; subTreeProxy->setSourceModel(d->model); // sorting requires another proxy in-between auto* sortProxy = new QSortFilterProxyModel(subTreeProxy); sortProxy->setSourceModel(subTreeProxy); sortProxy->setDynamicSortFilter(true); view->setModel(sortProxy); } // view->setModel creates the selection model connect(view->selectionModel(), &QItemSelectionModel::currentChanged, - this, [&] (const QModelIndex& current, const QModelIndex& previous) { d->viewSelectionChanged(current, previous); }); + this, [&](const QModelIndex& current, const QModelIndex& previous) { + d->viewSelectionChanged(current, previous); + }); if (i + 1 == d->levels) { connect(view->model(), &QAbstractItemModel::rowsInserted, - this, [&] { d->lastViewsContentsChanged(); }); + this, [&] { + d->lastViewsContentsChanged(); + }); } view->setSortingEnabled(true); view->sortByColumn(0, Qt::AscendingOrder); levelLayout->addWidget(view); layout()->addItem(levelLayout); d->layouts << levelLayout; d->views << view; previousView = view; } setModel(d->model); } QAbstractItemModel* MultiLevelListView::model() const { return d->model; } void MultiLevelListView::setModel(QAbstractItemModel* model) { d->model = model; - foreach (LabeledProxy* proxy, d->proxies) - { + foreach (LabeledProxy* proxy, d->proxies) { dynamic_cast(proxy)->setSourceModel(model); } - if (model && !d->views.isEmpty()) - { + if (model && !d->views.isEmpty()) { d->views.first()->setCurrentIndex(d->views.first()->model()->index(0, 0)); } } -QTreeView* MultiLevelListView::viewForLevel( int level ) const +QTreeView* MultiLevelListView::viewForLevel(int level) const { return d->views[level]; } void MultiLevelListView::addWidget(int level, QWidget* widget) { Q_ASSERT(level < d->levels); d->layouts[level]->addWidget(widget); } QModelIndex MultiLevelListView::currentIndex() const { return d->mapToSource(d->views.last()->currentIndex()); } void MultiLevelListView::setCurrentIndex(const QModelIndex& index) { // incoming index is for the original model Q_ASSERT(!index.isValid() || index.model() == d->model); const QModelIndex previous = currentIndex(); QModelIndex idx(index); QVector indexes; - while (idx.isValid()) - { + while (idx.isValid()) { indexes.prepend(idx); idx = idx.parent(); } - for (int i = 0; i < d->levels; ++i) - { + for (int i = 0; i < d->levels; ++i) { QTreeView* view = d->views.at(i); if (indexes.size() <= i) { // select first item by default view->setCurrentIndex(view->model()->index(0, 0)); continue; } QModelIndex index; if (i + 1 == d->levels) { // select the very last index in the list (i.e. might be deep down in the actual tree) index = indexes.last(); } else { // select the first index for that level index = indexes.at(i); } view->setCurrentIndex(d->mapFromSource(index, i)); } emit currentIndexChanged(index, previous); } void MultiLevelListView::setRootIndex(const QModelIndex& index) { Q_ASSERT(!index.isValid() || index.model() == d->model); d->views.first()->setRootIndex(index); } void MultiLevelListView::setHeaderLabels(const QStringList& labels) { int n = qMin(d->levels, labels.size()); - for (int i = 0; i < n; ++i) - { + for (int i = 0; i < n; ++i) { d->proxies.at(i)->setLabel(labels[i]); } } static KSelectionProxyModel::FilterBehavior toSelectionProxyModelFilterBehavior(MultiLevelListView::LastLevelViewMode mode) { switch (mode) { - case MultiLevelListView::SubTrees: - return KSelectionProxyModel::SubTreesWithoutRoots; - case MultiLevelListView::DirectChildren: - return KSelectionProxyModel::ChildrenOfExactSelection; + case MultiLevelListView::SubTrees: + return KSelectionProxyModel::SubTreesWithoutRoots; + case MultiLevelListView::DirectChildren: + return KSelectionProxyModel::ChildrenOfExactSelection; } Q_UNREACHABLE(); } void MultiLevelListView::setLastLevelViewMode(LastLevelViewMode mode) { if (d->proxies.isEmpty()) { return; } const auto filterBehavior = toSelectionProxyModelFilterBehavior(mode); dynamic_cast(d->proxies.last())->setFilterBehavior(filterBehavior); } - #include "multilevellistview.moc" #include "moc_multilevellistview.cpp" diff --git a/kdevplatform/util/multilevellistview.h b/kdevplatform/util/multilevellistview.h index 83bfc6b781..54eddf543e 100644 --- a/kdevplatform/util/multilevellistview.h +++ b/kdevplatform/util/multilevellistview.h @@ -1,155 +1,155 @@ /* 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. */ #ifndef KDEVPLATFORM_MULTILEVELLISTVIEW_H #define KDEVPLATFORM_MULTILEVELLISTVIEW_H #include #include "utilexport.h" class QTreeView; class QModelIndex; class QAbstractItemModel; -namespace KDevelop -{ +namespace KDevelop { /** * A view for displaying a tree structure in a series of list views. * * A MultiLevelListView can have any number of levels, with one list view for each level. * Selecting an item at one level causes that item to become the root of the next level. * * For compatibility and convenience, this class has methods and signals similar to those of * QAbstractItemView, such as setModel(), setRootIndex() and currentIndexChanged(). */ class KDEVPLATFORMUTIL_EXPORT MultiLevelListView : public QWidget { Q_OBJECT + public: enum LastLevelViewMode { SubTrees, ///< Shows complete subtree for each child. Only leafs are selectable. DirectChildren ///< Shows only the direct childs. }; Q_ENUM(LastLevelViewMode) /** * Creates a new MultiLevelListView with parent @p parent. * * Call setLevels() afterwards to set the number of list views. * * @param parent parent widget * @param f window flags, passed to QWidget */ explicit MultiLevelListView(QWidget* parent = nullptr, Qt::WindowFlags f = {}); /** * Default destructor */ ~MultiLevelListView() override; /** * @return the number of list view */ int levels() const; /** * Sets the number of levels, i.e. the number of list views visible, to @p levels * @param levels the new number of levels */ void setLevels(int levels); /** * @return the model displayed by this view, or 0 if none was set * @sa QAbstractItemView::model() */ QAbstractItemModel* model() const; /** * Sets the model to be displayed by this view. * * @param model the model to be displayed * @sa QAbstractItemView::setModel() */ void setModel(QAbstractItemModel* model); /** * Provides access to the QTreeView objects used internally. * Returns the view for level @p level of the tree structure. * * @param level the level of the tree structure shown by the returned view */ QTreeView* viewForLevel(int level) const; /** * The current index of the view. * * The current index is determined as the current index of the last list view. * * @sa QAbstractItemView::currentIndex() */ QModelIndex currentIndex() const; /** * Adds the widget @p widget under the list view for level @p level. * This function can be used to insert custom widgets into the view hierarchy. * * @param level specifies where to place the widget * @param widget the widget to add */ void addWidget(int level, QWidget* widget); void setHeaderLabels(const QStringList& labels); /** * Set the view mode of the view for the last level. * Default is @c SubTrees. */ void setLastLevelViewMode(LastLevelViewMode mode); Q_SIGNALS: /** * Notified that the current index has changed from @p previous to @p current * * @param current the new current index * @param previous the previous index * * @sa currentIndex(), QItemSelectionModel::currentChanged() */ void currentIndexChanged(const QModelIndex& current, const QModelIndex& previous); public Q_SLOTS: /** * Sets the root index of the entire view to @p index. * * @sa QAbstractItemView::setRootIndex() */ void setRootIndex(const QModelIndex& index); /** * Sets the current index to @p index. * * @sa currentIndex(), QAbstractItemView::setCurrentIndex() */ void setCurrentIndex(const QModelIndex& index); private: const QScopedPointer d; friend class MultiLevelListViewPrivate; - Q_PRIVATE_SLOT(d, void ensureViewSelected(QTreeView* view)) + Q_PRIVATE_SLOT(d, void ensureViewSelected(QTreeView * view)) }; } #endif // KDEVPLATFORM_MULTILEVELLISTVIEW_H diff --git a/kdevplatform/util/objectlist.cpp b/kdevplatform/util/objectlist.cpp index 634303a848..48327b3f20 100644 --- a/kdevplatform/util/objectlist.cpp +++ b/kdevplatform/util/objectlist.cpp @@ -1,85 +1,85 @@ /* * 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 "objectlist.h" using namespace KDevelop; class KDevelop::ObjectListTrackerPrivate { public: /// List of tracked objects (contains unique items only) QList m_list; ObjectListTracker::CleanupBehavior m_cleanupBehavior; }; ObjectListTracker::ObjectListTracker(CleanupBehavior behavior, QObject* parent) : QObject(parent) - , d(new ObjectListTrackerPrivate{{}, behavior}) + , d(new ObjectListTrackerPrivate {{}, behavior}) { } ObjectListTracker::~ObjectListTracker() { if (d->m_cleanupBehavior == CleanupWhenDone) { deleteAll(); } } const QList& ObjectListTracker::data() const { return d->m_list; } void ObjectListTracker::objectDestroyed(QObject* object) { bool success = d->m_list.removeOne(object); Q_ASSERT(success); Q_UNUSED(success); } void ObjectListTracker::append(QObject* object) { if (!object || d->m_list.contains(object)) { return; } d->m_list.append(object); connect(object, &QObject::destroyed, this, &ObjectListTracker::objectDestroyed); } bool ObjectListTracker::remove(QObject* object) { if (!object) { return false; } disconnect(object, &QObject::destroyed, this, &ObjectListTracker::objectDestroyed); return d->m_list.removeOne(object); } void ObjectListTracker::deleteAll() { qDeleteAll(d->m_list); d->m_list.clear(); } #include "moc_objectlist.cpp" diff --git a/kdevplatform/util/path.cpp b/kdevplatform/util/path.cpp index 621a41faf2..d92d635140 100644 --- a/kdevplatform/util/path.cpp +++ b/kdevplatform/util/path.cpp @@ -1,511 +1,517 @@ /* * This file is part of KDevelop * Copyright 2012 Milian Wolff * Copyright 2015 Kevin Funk * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "path.h" #include #include #include using namespace KDevelop; namespace { inline bool isWindowsDriveLetter(const QString& segment) { #ifdef Q_OS_WIN return segment.size() == 2 && segment.at(0).isLetter() && segment.at(1) == QLatin1Char(':'); #else Q_UNUSED(segment); return false; #endif } inline bool isAbsolutePath(const QString& path) { if (path.startsWith(QLatin1Char('/'))) { return true; // Even on Windows: Potentially a path of a remote URL } #ifdef Q_OS_WIN return path.size() >= 2 && path.at(0).isLetter() && path.at(1) == QLatin1Char(':'); #else return false; #endif } } QString KDevelop::toUrlOrLocalFile(const QUrl& url, QUrl::FormattingOptions options) { const auto str = url.toString(options | QUrl::PreferLocalFile); #ifdef Q_OS_WIN // potentially strip leading slash if (url.isLocalFile() && !str.isEmpty() && str[0] == QLatin1Char('/')) { return str.mid(1); // expensive copying, but we'd like toString(...) to properly format everything first } #endif return str; } Path::Path() { } Path::Path(const QString& pathOrUrl) : Path(QUrl::fromUserInput(pathOrUrl, QString(), QUrl::DefaultResolution)) { } Path::Path(const QUrl& url) { if (!url.isValid()) { // empty or invalid Path return; } // we do not support urls with: // - fragments // - sub urls // - query // nor do we support relative urls if (url.hasFragment() || url.hasQuery() || url.isRelative() || url.path().isEmpty()) { // invalid qWarning("Path::init: invalid/unsupported Path encountered: \"%s\"", qPrintable(url.toDisplayString(QUrl::PreferLocalFile))); return; } if (!url.isLocalFile()) { // handle remote urls QString urlPrefix = url.scheme() + QLatin1String("://"); const QString user = url.userName(); if (!user.isEmpty()) { urlPrefix += user + QLatin1Char('@'); } urlPrefix += url.host(); if (url.port() != -1) { urlPrefix += QLatin1Char(':') + QString::number(url.port()); } m_data << urlPrefix; } addPath(url.isLocalFile() ? url.toLocalFile() : url.path()); // support for root paths, they are valid but don't really contain any data if (m_data.isEmpty() || (isRemote() && m_data.size() == 1)) { m_data << QString(); } } Path::Path(const Path& other, const QString& child) -: m_data(other.m_data) + : m_data(other.m_data) { if (isAbsolutePath(child)) { // absolute path: only share the remote part of @p other m_data.resize(isRemote() ? 1 : 0); } else if (!other.isValid() && !child.isEmpty()) { qWarning("Path::Path: tried to append relative path \"%s\" to invalid base", qPrintable(child)); return; } addPath(child); } static QString generatePathOrUrl(bool onlyPath, bool isLocalFile, const QVector& data) { // more or less a copy of QtPrivate::QStringList_join const int size = data.size(); if (size == 0) { return QString(); } int totalLength = 0; // separators: '/' totalLength += size; // skip Path segment if we only want the path int start = (onlyPath && !isLocalFile) ? 1 : 0; // path and url prefix for (int i = start; i < size; ++i) { totalLength += data.at(i).size(); } // build string representation QString res; res.reserve(totalLength); #ifdef Q_OS_WIN if (start == 0 && isLocalFile) { Q_ASSERT(data.at(0).endsWith(QLatin1Char(':'))); // assume something along "C:" res += data.at(0); start++; } #endif for (int i = start; i < size; ++i) { if (i || isLocalFile) { res += QLatin1Char('/'); } res += data.at(i); } return res; } QString Path::pathOrUrl() const { return generatePathOrUrl(false, isLocalFile(), m_data); } QString Path::path() const { return generatePathOrUrl(true, isLocalFile(), m_data); } QString Path::toLocalFile() const { return isLocalFile() ? path() : QString(); } QString Path::relativePath(const Path& path) const { if (!path.isValid()) { return QString(); } if (!isValid() || remotePrefix() != path.remotePrefix()) { // different remote destinations or we are invalid, return input as-is return path.pathOrUrl(); } // while I'd love to use QUrl::relativePath here, it seems to behave pretty // strangely, and adds unexpected "./" at the start for example // so instead, do it on our own based on _relativePath in kurl.cpp // this should also be more performant I think // Find where they meet int level = isRemote() ? 1 : 0; const int maxLevel = qMin(m_data.count(), path.m_data.count()); - while(level < maxLevel && m_data.at(level) == path.m_data.at(level)) { + while (level < maxLevel && m_data.at(level) == path.m_data.at(level)) { ++level; } // Need to go down out of our path to the common branch. // but keep in mind that e.g. '/' paths have an empty name int backwardSegments = m_data.count() - level; if (backwardSegments && level < maxLevel && m_data.at(level).isEmpty()) { --backwardSegments; } // Now up up from the common branch to the second path. int forwardSegmentsLength = 0; for (int i = level; i < path.m_data.count(); ++i) { forwardSegmentsLength += path.m_data.at(i).length(); // slashes if (i + 1 != path.m_data.count()) { forwardSegmentsLength += 1; } } QString relativePath; relativePath.reserve((backwardSegments * 3) + forwardSegmentsLength); - for(int i = 0; i < backwardSegments; ++i) { + for (int i = 0; i < backwardSegments; ++i) { relativePath.append(QLatin1String("../")); } + for (int i = level; i < path.m_data.count(); ++i) { relativePath.append(path.m_data.at(i)); if (i + 1 != path.m_data.count()) { relativePath.append(QLatin1Char('/')); } } + Q_ASSERT(relativePath.length() == ((backwardSegments * 3) + forwardSegmentsLength)); return relativePath; } static bool isParentPath(const QVector& parent, const QVector& child, bool direct) { if (direct && child.size() != parent.size() + 1) { return false; } else if (!direct && child.size() <= parent.size()) { return false; } for (int i = 0; i < parent.size(); ++i) { if (child.at(i) != parent.at(i)) { // support for trailing '/' if (i + 1 == parent.size() && parent.at(i).isEmpty()) { return true; } // otherwise we take a different branch here return false; } } + return true; } bool Path::isParentOf(const Path& path) const { if (!isValid() || !path.isValid() || remotePrefix() != path.remotePrefix()) { return false; } return isParentPath(m_data, path.m_data, false); } bool Path::isDirectParentOf(const Path& path) const { if (!isValid() || !path.isValid() || remotePrefix() != path.remotePrefix()) { return false; } return isParentPath(m_data, path.m_data, true); } QString Path::remotePrefix() const { return isRemote() ? m_data.first() : QString(); } bool Path::operator<(const Path& other) const { const int size = m_data.size(); const int otherSize = other.m_data.size(); const int toCompare = qMin(size, otherSize); // compare each Path segment in turn and try to return early for (int i = 0; i < toCompare; ++i) { int comparison = m_data.at(i).compare(other.m_data.at(i)); if (comparison == 0) { // equal, try next segment continue; } else { // return whether our segment is less then the other one return comparison < 0; } } + // when we reach this point, all elements that we compared where equal // thus return whether we have less items than the other Path return size < otherSize; } QUrl Path::toUrl() const { return QUrl::fromUserInput(pathOrUrl()); } bool Path::isLocalFile() const { // if the first data element contains a '/' it is a Path prefix return !m_data.isEmpty() && !m_data.first().contains(QLatin1Char('/')); } bool Path::isRemote() const { return !m_data.isEmpty() && m_data.first().contains(QLatin1Char('/')); } QString Path::lastPathSegment() const { // remote Paths are offset by one, thus never return the first item of them as file name if (m_data.isEmpty() || (!isLocalFile() && m_data.size() == 1)) { return QString(); } return m_data.last(); } void Path::setLastPathSegment(const QString& name) { // remote Paths are offset by one, thus never return the first item of them as file name if (m_data.isEmpty() || (!isLocalFile() && m_data.size() == 1)) { // append the name to empty Paths or remote Paths only containing the Path prefix m_data.append(name); } else { // overwrite the last data member m_data.last() = name; } } static void cleanPath(QVector* data, const bool isRemote) { if (data->isEmpty()) { return; } const int startOffset = isRemote ? 1 : 0; const auto start = data->begin() + startOffset; auto it = start; - while(it != data->end()) { + while (it != data->end()) { if (*it == QLatin1String("..")) { if (it == start) { it = data->erase(it); } else { if (isWindowsDriveLetter(*(it - 1))) { it = data->erase(it); // keep the drive letter } else { it = data->erase(it - 1, it + 1); } } } else if (*it == QLatin1String(".")) { it = data->erase(it); } else { ++it; } } if (data->count() == startOffset) { data->append(QString()); } } // Optimized QString::split code for the specific Path use-case -static QVarLengthArray splitPath(const QString &source) +static QVarLengthArray splitPath(const QString& source) { QVarLengthArray list; int start = 0; int end = 0; while ((end = source.indexOf(QLatin1Char('/'), start)) != -1) { if (start != end) { list.append(source.mid(start, end - start)); } start = end + 1; } if (start != source.size()) { list.append(source.mid(start, -1)); } return list; } void Path::addPath(const QString& path) { if (path.isEmpty()) { return; } const auto& newData = splitPath(path); if (newData.isEmpty()) { if (m_data.size() == (isRemote() ? 1 : 0)) { // this represents the root path, we just turned an invalid path into it m_data << QString(); } return; } auto it = newData.begin(); if (!m_data.isEmpty() && m_data.last().isEmpty()) { // the root item is empty, set its contents and continue appending m_data.last() = *it; ++it; } std::copy(it, newData.end(), std::back_inserter(m_data)); cleanPath(&m_data, isRemote()); } Path Path::parent() const { if (m_data.isEmpty()) { return Path(); } Path ret(*this); if (m_data.size() == (1 + (isRemote() ? 1 : 0))) { // keep the root item, but clear it, otherwise we'd make the path invalid // or a URL a local path auto& root = ret.m_data.last(); if (!isWindowsDriveLetter(root)) { root.clear(); } } else { ret.m_data.pop_back(); } return ret; } bool Path::hasParent() const { const int rootIdx = isRemote() ? 1 : 0; return m_data.size() > rootIdx && !m_data[rootIdx].isEmpty(); } void Path::clear() { m_data.clear(); } Path Path::cd(const QString& dir) const { if (!isValid()) { return Path(); } return Path(*this, dir); } namespace KDevelop { uint qHash(const Path& path) { KDevHash hash; foreach (const QString& segment, path.segments()) { hash << qHash(segment); } + return hash; } template static Path::List toPathList_impl(const Container& list) { Path::List ret; ret.reserve(list.size()); for (const auto& entry : list) { Path path(entry); if (path.isValid()) { ret << path; } } + ret.squeeze(); return ret; } Path::List toPathList(const QList& list) { return toPathList_impl(list); } -Path::List toPathList(const QList< QString >& list) +Path::List toPathList(const QList& list) { return toPathList_impl(list); } } QDebug operator<<(QDebug s, const Path& string) { s.nospace() << string.pathOrUrl(); return s.space(); } namespace QTest { template<> -char *toString(const Path &path) +char* toString(const Path& path) { return qstrdup(qPrintable(path.pathOrUrl())); } } diff --git a/kdevplatform/util/path.h b/kdevplatform/util/path.h index c41b7767a7..f7c6b69530 100644 --- a/kdevplatform/util/path.h +++ b/kdevplatform/util/path.h @@ -1,382 +1,383 @@ /* * This file is part of KDevelop * Copyright 2012 Milian Wolff * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KDEVELOP_PATH_H #define KDEVELOP_PATH_H #include "utilexport.h" #include #include #include #include namespace KDevelop { /** * @return Return a string representation of @p url, if possible as local file * * Convenience method for working around https://bugreports.qt.io/browse/QTBUG-41729 */ -QString KDEVPLATFORMUTIL_EXPORT toUrlOrLocalFile(const QUrl& url, QUrl::FormattingOptions options = QUrl::FormattingOptions(QUrl::PrettyDecoded)); +QString KDEVPLATFORMUTIL_EXPORT toUrlOrLocalFile(const QUrl& url, + QUrl::FormattingOptions options = QUrl::FormattingOptions( QUrl::PrettyDecoded )); /** * @brief Path data type optimized for memory consumption. * * This class holds data that represents a local or remote path. * In the project model e.g. we usually store whole trees such as * * /foo/ * /foo/bar/ * /foo/bar/asdf.txt * * Normal QString/QUrl/QUrl types would not share any memory for these paths * at all. This class though can share the segments of the paths and thus * consume far less total memory. * * Just like the URL types, the Path can point to a remote location. * * Example for how to leverage memory sharing for the above input data: * * @code * Path foo("/foo"); * Path bar(foo, "bar"); * Path asdf(foo, "asdf.txt"); * @endcode * * @note Just as with QString e.g. you won't share any data implicitly when * you do something like this: * * @code * Path foo1("/foo"); * Path foo2("/foo"); * @endcode */ class KDEVPLATFORMUTIL_EXPORT Path { public: typedef QVector List; /** * Construct an empty, invalid Path. */ Path(); /** * Create a Path out of a string representation of a path or URL. * * @note Not every kind of remote URL is supported, rather only path-like * URLs without fragments, queries, sub-Paths and the like are supported. * * Empty paths or URLs containing one of the following are considered invalid: * - URL fragments (i.e. "...#fragment") * - URL queries (i.e. "...?query=") * - sub-URLs (i.e. "file:///tmp/kde.tgz#gzip:/#tar:/kdevelop") * * @sa isValid() */ explicit Path(const QString& pathOrUrl); /** * Convert a QUrl to a Path. * * @note Not every kind of remote URL is supported, rather only path-like * URLs without fragments, queries, sub-Paths and the like are supported. * * Empty paths or URLs containing one of the following are considered invalid: * - URL fragments (i.e. "...#fragment") * - URL queries (i.e. "...?query=") * - sub-URLs (i.e. "file:///tmp/kde.tgz#gzip:/#tar:/kdevelop") * * @sa isValid() */ explicit Path(const QUrl& url); /** * Create a copy of @p base and optionally append a path segment @p subPath. * * This implicitly shares the data of @p base and thus is very efficient * memory wise compared to creating two Paths from separate strings. * * @p subPath A relative or absolute path. If this is an absolute path then * the path in @p base will be ignored and only the remote data copied. If * this is a relative path it will be combined with @p base. * * @sa addPath() */ Path(const Path& base, const QString& subPath = QString()); /** * Equality comparison between @p other and this Path. * * @return true if @p other is equal to this Path. */ inline bool operator==(const Path& other) const { return m_data == other.m_data; } /** * Inequality comparison between @p other and this Path. * * @return true if @p other is different from this Path. */ inline bool operator!=(const Path& other) const { return !operator==(other); } /** * Less-than path comparison between @p other and this Path. * * @return true if this Path is less than @p other. */ bool operator<(const Path& other) const; /** * Greater-than path comparison between @p other and this Path. * * @return true if this Path is greater than @p other. */ inline bool operator>(const Path& other) const { return other < *this; } /** * Less-than-equal path comparison between @p other and this Path. * * @return true if this Path is less than @p other or equal. */ inline bool operator<=(const Path& other) const { return *this < other || other == *this; } /** * Greater-than-equal path comparison between @p other and this Path. * * @return true if this Path is greater than @p other or equal. */ inline bool operator>=(const Path& other) const { return other < *this || other == *this; } /** * Check whether this Path is valid. * * @return true if the Path is valid, i.e. contains data, false otherwise. */ inline bool isValid() const { return !m_data.isEmpty(); } /** * Check whether this Path is empty. * * @return true if the Path is empty, false otherwise, i.e. if it contains data. */ inline bool isEmpty() const { return m_data.isEmpty(); } /** * Convert the Path to a string, yielding either the plain path for local * paths or the stringified URL for remote Paths. * * @return a string representation of this Path. * @sa path() */ QString pathOrUrl() const; /** * Return the path segment of this Path. This is the same for local paths * as calling pathOrUrl(). The difference is only for remote paths which * would return the protocol, host etc. data in pathOrUrl but not here. * * @return the path segment of this Path. * * @sa pathOrUrl() */ QString path() const; /** * Return the path for local path and an empty string for remote paths. */ QString toLocalFile() const; /** * @return the relative path from this path to @p path. * * Examples: * @code * Path p1("/foo/bar"); * Path p2("/foo/bar/asdf/test.txt"); * p1.relativePath(p2); // returns: asdf/test.txt * Path p3("/foo/asdf/lala"); * p3.relativePath(p1); // returns: ../../bar * @endcode * * @sa QUrl::relativePath */ QString relativePath(const Path& path) const; /** * @return True if this path is the parent of @p path. * * For instance, ftp://host/dir/ is a parent of ftp://host/dir/subdir/blub, * or /foo is a parent of /foo/bar. * * NOTE: Contrary to QUrl::isParentOf this returns false if the path equals this one. */ bool isParentOf(const Path& path) const; /** * @return True if this path is the direct parent of @p path. * * For instance, ftp://host/dir/ is the direct parent of ftp://host/dir/subdir, * but ftp.//host/ is a parent but not the direct parent. * * This is more efficient than @code path.parent() == *this @endcode since * it does not require any temporary allocations as for the parent() call. */ bool isDirectParentOf(const Path& path) const; /** * @return the prefix of a remote URL containing protocol, host, port etc. pp. * If this path is not remote, this returns an empty string. */ QString remotePrefix() const; /** * @return an implicitly shared copy of the internal data. */ inline QVector segments() const { return m_data; } /** * @return the Path converted to a QUrl. */ QUrl toUrl() const; /** * @return true when this Path points to a local file, false otherwise. */ bool isLocalFile() const; /** * @return true when this Path points to a remote file, false otherwise. */ bool isRemote() const; /** * @return the last element of the path. * * This will never return the remote URL prefix. */ QString lastPathSegment() const; /** * Set the file name of this Path, i.e. the last element of the path. * * This will never overwrite the remote URL prefix. */ void setLastPathSegment(const QString& name); /** * Append @p path to this Path. * * NOTE: If @p path starts with a slash, this function ignores it. * I.e. you cannot set the path this way. @sa QUrl::addPath */ void addPath(const QString& path); /** * @return the path pointing to the parent folder of this path. * * @sa KIO::upUrl() */ Path parent() const; /** * @return true when this path has a parent and false if this is a root or invalid path. */ bool hasParent() const; /** * Clear the path, i.e. make it invalid and empty. */ void clear(); /** * Change directory by relative path @p dir. * * NOTE: This is expensive. * * @sa QUrl::cd */ Path cd(const QString& dir) const; private: // for remote urls the first element contains the a Path prefix // containing the protocol, user, port etc. pp. QVector m_data; }; KDEVPLATFORMUTIL_EXPORT uint qHash(const Path& path); /** * Convert the @p list of QUrls to a list of Paths. */ KDEVPLATFORMUTIL_EXPORT Path::List toPathList(const QList& list); /** * Convert the @p list of QStrings to a list of Paths. */ KDEVPLATFORMUTIL_EXPORT Path::List toPathList(const QList& list); } /** * qDebug() stream operator. Writes the string to the debug output. */ KDEVPLATFORMUTIL_EXPORT QDebug operator<<(QDebug s, const KDevelop::Path& string); namespace QTest { template char* toString(const T&); /** * QTestLib integration to have nice output in e.g. QCOMPARE failures. */ template<> KDEVPLATFORMUTIL_EXPORT char* toString(const KDevelop::Path& path); } Q_DECLARE_TYPEINFO(KDevelop::Path, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(KDevelop::Path) Q_DECLARE_TYPEINFO(KDevelop::Path::List, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(KDevelop::Path::List) #endif // KDEVELOP_PATH_H diff --git a/kdevplatform/util/placeholderitemproxymodel.cpp b/kdevplatform/util/placeholderitemproxymodel.cpp index e7c27bf718..d04d04e39c 100644 --- a/kdevplatform/util/placeholderitemproxymodel.cpp +++ b/kdevplatform/util/placeholderitemproxymodel.cpp @@ -1,212 +1,211 @@ /* * Copyright 2013 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 "placeholderitemproxymodel.h" #include using namespace KDevelop; class KDevelop::PlaceholderItemProxyModelPrivate { public: explicit PlaceholderItemProxyModelPrivate(PlaceholderItemProxyModel* qq) : q(qq) {} inline int sourceRowCount() { return q->sourceModel() ? q->sourceModel()->rowCount() : 0; } inline bool isPlaceholderRow(const QModelIndex& index) const { if (!q->sourceModel()) { return false; } return index.row() == q->sourceModel()->rowCount(); } PlaceholderItemProxyModel* const q; /// column -> hint mapping QMap m_columnHints; }; PlaceholderItemProxyModel::PlaceholderItemProxyModel(QObject* parent) : QIdentityProxyModel(parent) , d(new PlaceholderItemProxyModelPrivate(this)) {} PlaceholderItemProxyModel::~PlaceholderItemProxyModel() { } QVariant PlaceholderItemProxyModel::columnHint(int column) const { return d->m_columnHints.value(column); } void PlaceholderItemProxyModel::setColumnHint(int column, const QVariant& hint) { if (column < 0) { return; } d->m_columnHints[column] = hint; const int row = d->sourceRowCount(); emit dataChanged(index(row, 0), index(row, columnCount())); } Qt::ItemFlags PlaceholderItemProxyModel::flags(const QModelIndex& index) const { if (d->isPlaceholderRow(index)) { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; const int column = index.column(); // if the column doesn't provide a hint we assume that we can't edit this field if (d->m_columnHints.contains(column)) { flags |= Qt::ItemIsEditable; } return flags; } return QIdentityProxyModel::flags(index); } void PlaceholderItemProxyModel::setSourceModel(QAbstractItemModel* sourceModel) { QIdentityProxyModel::setSourceModel(sourceModel); // TODO: Listen to layoutDataChanged signals? } int PlaceholderItemProxyModel::rowCount(const QModelIndex& parent) const { if (!sourceModel()) return 0; // only flat models supported for now, assert early in case that's not true Q_ASSERT(!parent.isValid()); Q_UNUSED(parent); return sourceModel()->rowCount() + 1; } bool KDevelop::PlaceholderItemProxyModel::hasChildren(const QModelIndex& parent) const { - if ( !parent.isValid() ) { + if (!parent.isValid()) { return true; } return QIdentityProxyModel::hasChildren(parent); } QVariant PlaceholderItemProxyModel::data(const QModelIndex& proxyIndex, int role) const { const int column = proxyIndex.column(); if (d->isPlaceholderRow(proxyIndex)) { switch (role) { case Qt::DisplayRole: return columnHint(column); case Qt::ForegroundRole: { const KColorScheme scheme(QPalette::Normal); return scheme.foreground(KColorScheme::InactiveText); } default: return QVariant(); } } return QIdentityProxyModel::data(proxyIndex, role); } QModelIndex PlaceholderItemProxyModel::parent(const QModelIndex& child) const { if (d->isPlaceholderRow(child)) { return QModelIndex(); } return QIdentityProxyModel::parent(child); } QModelIndex PlaceholderItemProxyModel::buddy(const QModelIndex& index) const { if (d->isPlaceholderRow(index)) { return index; } return QIdentityProxyModel::buddy(index); } QModelIndex PlaceholderItemProxyModel::sibling(int row, int column, const QModelIndex& idx) const { const bool isPlaceHolderRow = (sourceModel() ? row == sourceModel()->rowCount() : false); if (isPlaceHolderRow) { return index(row, column, QModelIndex()); } return QIdentityProxyModel::sibling(row, column, idx); } - QModelIndex PlaceholderItemProxyModel::mapToSource(const QModelIndex& proxyIndex) const { if (d->isPlaceholderRow(proxyIndex)) { return QModelIndex(); } return QIdentityProxyModel::mapToSource(proxyIndex); } bool PlaceholderItemProxyModel::setData(const QModelIndex& index, const QVariant& value, int role) { const int column = index.column(); if (d->isPlaceholderRow(index) && role == Qt::EditRole && d->m_columnHints.contains(column)) { const bool accept = validateRow(index, value); // if validation fails, clear the complete line if (!accept) { emit dataChanged(index, index); return false; } // update view emit dataChanged(index, index); // notify observers emit dataInserted(column, value); return true; } return QIdentityProxyModel::setData(index, value, role); } QModelIndex PlaceholderItemProxyModel::index(int row, int column, const QModelIndex& parent) const { Q_ASSERT(!parent.isValid()); Q_UNUSED(parent); const bool isPlaceHolderRow = (sourceModel() ? row == sourceModel()->rowCount() : false); if (isPlaceHolderRow) { return createIndex(row, column); } return QIdentityProxyModel::index(row, column, parent); } bool PlaceholderItemProxyModel::validateRow(const QModelIndex& index, const QVariant& value) const { Q_UNUSED(index); return !value.toString().isEmpty(); } #include "moc_placeholderitemproxymodel.cpp" diff --git a/kdevplatform/util/processlinemaker.cpp b/kdevplatform/util/processlinemaker.cpp index 7b7e3f96d8..5496370db2 100644 --- a/kdevplatform/util/processlinemaker.cpp +++ b/kdevplatform/util/processlinemaker.cpp @@ -1,129 +1,132 @@ /* This file is part of the KDE project Copyright 2002 John Firebaugh Copyright 2007 Andreas Pakulat Copyright 2007 Oswald Buddenhagen 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 "processlinemaker.h" #include #include -namespace KDevelop -{ +namespace KDevelop { class ProcessLineMakerPrivate { public: QByteArray stdoutbuf; QByteArray stderrbuf; ProcessLineMaker* p; QProcess* m_proc; - explicit ProcessLineMakerPrivate( ProcessLineMaker* maker ) + explicit ProcessLineMakerPrivate(ProcessLineMaker* maker) : p(maker) { } void slotReadyReadStdout() { stdoutbuf += m_proc->readAllStandardOutput(); processStdOut(); } - static QStringList streamToStrings(QByteArray &data) + static QStringList streamToStrings(QByteArray& data) { QStringList lineList; int pos; - while ( (pos = data.indexOf('\n')) != -1) { + while ((pos = data.indexOf('\n')) != -1) { if (pos > 0 && data.at(pos - 1) == '\r') lineList << QString::fromLocal8Bit(data.constData(), pos - 1); else lineList << QString::fromLocal8Bit(data.constData(), pos); - data.remove(0, pos+1); + data.remove(0, pos + 1); } return lineList; } void processStdOut() { emit p->receivedStdoutLines(streamToStrings(stdoutbuf)); } void slotReadyReadStderr() { stderrbuf += m_proc->readAllStandardError(); processStdErr(); } void processStdErr() { emit p->receivedStderrLines(streamToStrings(stderrbuf)); } }; ProcessLineMaker::ProcessLineMaker(QObject* parent) : QObject(parent) - , d( new ProcessLineMakerPrivate( this ) ) + , d(new ProcessLineMakerPrivate(this)) { } -ProcessLineMaker::ProcessLineMaker( QProcess* proc, QObject* parent ) +ProcessLineMaker::ProcessLineMaker(QProcess* proc, QObject* parent) : QObject(parent) - , d( new ProcessLineMakerPrivate( this ) ) + , d(new ProcessLineMakerPrivate(this)) { d->m_proc = proc; connect(proc, &QProcess::readyReadStandardOutput, - this, [&] { d->slotReadyReadStdout(); } ); + this, [&] { + d->slotReadyReadStdout(); + }); connect(proc, &QProcess::readyReadStandardError, - this, [&] { d->slotReadyReadStderr(); } ); + this, [&] { + d->slotReadyReadStderr(); + }); } ProcessLineMaker::~ProcessLineMaker() = default; -void ProcessLineMaker::slotReceivedStdout( const QByteArray& buffer ) +void ProcessLineMaker::slotReceivedStdout(const QByteArray& buffer) { d->stdoutbuf += buffer; d->processStdOut(); } -void ProcessLineMaker::slotReceivedStderr( const QByteArray& buffer ) +void ProcessLineMaker::slotReceivedStderr(const QByteArray& buffer) { d->stderrbuf += buffer; d->processStdErr(); } -void ProcessLineMaker::discardBuffers( ) +void ProcessLineMaker::discardBuffers() { d->stderrbuf.truncate(0); d->stdoutbuf.truncate(0); } void ProcessLineMaker::flushBuffers() { if (!d->stdoutbuf.isEmpty()) emit receivedStdoutLines(QStringList(QString::fromLocal8Bit(d->stdoutbuf))); if (!d->stderrbuf.isEmpty()) emit receivedStderrLines(QStringList(QString::fromLocal8Bit(d->stderrbuf))); discardBuffers(); } } #include "moc_processlinemaker.cpp" diff --git a/kdevplatform/util/processlinemaker.h b/kdevplatform/util/processlinemaker.h index 7fd02f49e8..99bda1a71a 100644 --- a/kdevplatform/util/processlinemaker.h +++ b/kdevplatform/util/processlinemaker.h @@ -1,118 +1,117 @@ /* This file is part of the KDE project Copyright 2002 John Firebaugh Copyright 2007 Andreas Pakulat Copyright 2007 Oswald Buddenhagen 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. */ #ifndef _PROCESSLINEMAKER_H_ #define _PROCESSLINEMAKER_H_ #include #include "utilexport.h" /** @file processlinemaker.h Utility objects for process output views. */ class QProcess; class QStringList; /** Convenience class to catch output of QProcess. */ -namespace KDevelop -{ +namespace KDevelop { class KDEVPLATFORMUTIL_EXPORT ProcessLineMaker : public QObject { Q_OBJECT public: - explicit ProcessLineMaker( QObject* parent = nullptr ); - explicit ProcessLineMaker( QProcess* process, QObject* parent = nullptr ); - + explicit ProcessLineMaker(QObject* parent = nullptr); + explicit ProcessLineMaker(QProcess* process, QObject* parent = nullptr); + ~ProcessLineMaker() override; /** * clears out the internal buffers, this drops any data without * emitting the related signal */ void discardBuffers(); /** - * Flush the data from the buffers and then clear them. - * This should be called once when the process has + * Flush the data from the buffers and then clear them. + * This should be called once when the process has * exited to make sure all data that was received from the * process is properly converted and emitted. * * Note: Connecting this class to the process finished signal * is not going to work, as the user of this class will do - * that itself too and possibly delete the process, making + * that itself too and possibly delete the process, making * it impossible to fetch the last output. */ void flushBuffers(); public Q_SLOTS: /** * This should be used (instead of hand-crafted code) when * you need to do custom things with the process output * before feeding it to the linemaker and have it convert * it to QString lines. * @param buffer the output from the process */ - void slotReceivedStdout( const QByteArray& buffer ); + void slotReceivedStdout(const QByteArray& buffer); /** * This should be used (instead of hand-crafted code) when * you need to do custom things with the process error output * before feeding it to the linemaker and have it convert * it to QString lines. * @param buffer the output from the process */ - void slotReceivedStderr( const QByteArray& buffer ); + void slotReceivedStderr(const QByteArray& buffer); Q_SIGNALS: /** * Emitted whenever the process prints something * to its standard output. The output is converted * to a QString using fromLocal8Bit() and will * be split on '\n'. * @param lines the lines that the process printed */ - void receivedStdoutLines( const QStringList& lines ); + void receivedStdoutLines(const QStringList& lines); /** * Emitted whenever the process prints something * to its error output. The output is converted * to a QString using fromLocal8Bit() and will * be split on '\n'. * @param lines the lines that the process printed */ - void receivedStderrLines( const QStringList& lines ); + void receivedStderrLines(const QStringList& lines); private: const QScopedPointer d; friend class ProcessLineMakerPrivate; }; } #endif diff --git a/kdevplatform/util/projecttestjob.cpp b/kdevplatform/util/projecttestjob.cpp index 023a762e8d..afc0dc9160 100644 --- a/kdevplatform/util/projecttestjob.cpp +++ b/kdevplatform/util/projecttestjob.cpp @@ -1,127 +1,128 @@ /* * This file is part of KDevelop * * Copyright 2012 Miha Čančula * * 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 "projecttestjob.h" #include #include #include #include #include using namespace KDevelop; class KDevelop::ProjectTestJobPrivate { public: explicit ProjectTestJobPrivate(ProjectTestJob* q) : q(q) , m_currentJob(nullptr) , m_currentSuite(nullptr) {} void runNext(); void gotResult(ITestSuite* suite, const TestResult& result); ProjectTestJob* q; QList m_suites; KJob* m_currentJob; ITestSuite* m_currentSuite; ProjectTestResult m_result; }; void ProjectTestJobPrivate::runNext() { m_currentSuite = m_suites.takeFirst(); m_currentJob = m_currentSuite->launchAllCases(ITestSuite::Silent); m_currentJob->start(); } void ProjectTestJobPrivate::gotResult(ITestSuite* suite, const TestResult& result) { if (suite == m_currentSuite) { m_result.total++; q->emitPercent(m_result.total, m_result.total + m_suites.size()); - switch (result.suiteResult) - { - case TestResult::Passed: - m_result.passed++; - break; - - case TestResult::Failed: - m_result.failed++; - break; - - case TestResult::Error: - m_result.error++; - break; - - default: - break; + switch (result.suiteResult) { + case TestResult::Passed: + m_result.passed++; + break; + + case TestResult::Failed: + m_result.failed++; + break; + + case TestResult::Error: + m_result.error++; + break; + + default: + break; } if (m_suites.isEmpty()) { q->emitResult(); } else { runNext(); } } } ProjectTestJob::ProjectTestJob(IProject* project, QObject* parent) : KJob(parent) , d(new ProjectTestJobPrivate(this)) { setCapabilities(Killable); setObjectName(i18n("Run all tests in %1", project->name())); d->m_suites = ICore::self()->testController()->testSuitesForProject(project); connect(ICore::self()->testController(), &ITestController::testRunFinished, - this, [&] (ITestSuite* suite, const TestResult& result) { d->gotResult(suite, result); }); + this, [&](ITestSuite* suite, const TestResult& result) { + d->gotResult(suite, result); + }); } ProjectTestJob::~ProjectTestJob() { } void ProjectTestJob::start() { d->runNext(); } bool ProjectTestJob::doKill() { if (d->m_currentJob) { d->m_currentJob->kill(); } else { d->m_suites.clear(); } return true; } ProjectTestResult ProjectTestJob::testResult() { return d->m_result; } #include "moc_projecttestjob.cpp" diff --git a/kdevplatform/util/projecttestjob.h b/kdevplatform/util/projecttestjob.h index 6d2ac49110..1e12026943 100644 --- a/kdevplatform/util/projecttestjob.h +++ b/kdevplatform/util/projecttestjob.h @@ -1,114 +1,115 @@ /* * This file is part of KDevelop * * Copyright 2012 Miha Čančula * * 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_PROJECTTESTJOB_H #define KDEVPLATFORM_PROJECTTESTJOB_H #include #include "utilexport.h" namespace KDevelop { class IProject; /** * A combined result of a project test job * * @sa ProjectTestJob * **/ struct KDEVPLATFORMUTIL_EXPORT ProjectTestResult { ProjectTestResult() {} /** * The total number of test suites launched in this job **/ int total = 0; /** * The number of passed test suites in this job. **/ int passed = 0; /** * The number of failed test suites in this job. **/ int failed = 0; /** * The number of errors in this job. **/ int error = 0; }; /** * @brief A job that tests an entire project and reports the total result * * Launches all test suites in the specified project without raising the output window. * Instead of providing individual test results, it combines and simplifies them. * **/ class KDEVPLATFORMUTIL_EXPORT ProjectTestJob : public KJob { Q_OBJECT + public: /** * Create a project test job * * @param project The project to be tested * @param parent This job's parent object, or 0 for no parent. * **/ explicit ProjectTestJob(IProject* project, QObject* parent = nullptr); /** * Destructor * **/ ~ProjectTestJob() override; /** * Start this job. **/ void start() override; /** * @brief The result of this job * * This function only returns a correnct result after all the tests are completed. * It is therefore best to call this after the KJob::result() signal is emitted. * * @sa ProjectTestResult **/ ProjectTestResult testResult(); protected: bool doKill() override; private: friend class ProjectTestJobPrivate; const QScopedPointer d; }; } Q_DECLARE_TYPEINFO(KDevelop::ProjectTestResult, Q_MOVABLE_TYPE); #endif // KDEVPLATFORM_PROJECTTESTJOB_H diff --git a/kdevplatform/util/scopeddialog.h b/kdevplatform/util/scopeddialog.h index 32dae1743f..e215214a8d 100644 --- a/kdevplatform/util/scopeddialog.h +++ b/kdevplatform/util/scopeddialog.h @@ -1,90 +1,96 @@ /* This file is part of KDevelop * * Copyright 2017 Christoph Roick * * 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. */ #ifndef KDEVPLATFORM_SCOPEDDIALOG_H #define KDEVPLATFORM_SCOPEDDIALOG_H #include namespace KDevelop { /** * Wrapper class for QDialogs which should not be instantiated on stack. * * Parents of QDialogs may be unintentionally deleted during the execution of the * dialog and automatically delete their children. When returning to the calling * function they get intentionally deleted again, which will lead to a crash. This * can be circumvented by using a QPointer which keeps track of the QDialogs validity. * See this * blog entry * for explanation. The ScopedDialog utility allows using the dialog like a * common pointer. * * Instead of * \code QFileDialog dlg(this); if (dlg.exec()) return; \endcode simply use * \code ScopedDialog dlg(this); if (dlg->exec()) return; \endcode without need to manually clean up afterwards. */ template -class ScopedDialog { - public: - /// Construct the dialog with any set of allowed arguments - /// for the construction of DialogType - template - explicit ScopedDialog(Arguments ... args) : ptr(new DialogType(args...)) { - } - /// Automatically deletes the dialog if it is still present - ~ScopedDialog() { - delete ptr; - } +class ScopedDialog +{ +public: + /// Construct the dialog with any set of allowed arguments + /// for the construction of DialogType + template + explicit ScopedDialog(Arguments ... args) : ptr(new DialogType(args ...)) + { + } + /// Automatically deletes the dialog if it is still present + ~ScopedDialog() + { + delete ptr; + } - /// Access the raw pointer to the dialog - DialogType* data() const { - return ptr; - } - /// Access members of the dialog - DialogType* operator->() const { - return ptr; - } - /// Access the dialog - DialogType & operator*() const { - return *ptr; - } - /// Return the corresponding pointer - operator DialogType*() const { - return ptr; - } + /// Access the raw pointer to the dialog + DialogType* data() const + { + return ptr; + } + /// Access members of the dialog + DialogType* operator->() const + { + return ptr; + } + /// Access the dialog + DialogType& operator*() const + { + return *ptr; + } + /// Return the corresponding pointer + operator DialogType*() const { + return ptr; + } - private: - QPointer ptr; +private: + QPointer ptr; }; } #endif // KDEVPLATFORM_SCOPEDDIALOG_H diff --git a/kdevplatform/util/shellutils.cpp b/kdevplatform/util/shellutils.cpp index d281451318..71da2a4b72 100644 --- a/kdevplatform/util/shellutils.cpp +++ b/kdevplatform/util/shellutils.cpp @@ -1,118 +1,122 @@ /* * This file is part of KDevelop * * Copyright 2012 Ivan Shapovalov * * 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 "shellutils.h" #include #include #include #include #include #include #include #include #include #include namespace KDevelop { -bool askUser( const QString& mainText, - const QString& ttyPrompt, - const QString& mboxTitle, - const QString& mboxAdditionalText, - const QString& confirmText, - const QString& rejectText, - bool ttyDefaultToYes ) +bool askUser(const QString& mainText, + const QString& ttyPrompt, + const QString& mboxTitle, + const QString& mboxAdditionalText, + const QString& confirmText, + const QString& rejectText, + bool ttyDefaultToYes) { - if( !qobject_cast(qApp) ) { + if (!qobject_cast(qApp)) { // no ui-mode e.g. for duchainify and other tools - QTextStream out( stdout ); + QTextStream out(stdout); out << mainText << endl; - QTextStream in( stdin ); + QTextStream in(stdin); QString input; forever { - if( ttyDefaultToYes ) { - out << QStringLiteral( "%1: [Y/n] " ).arg( ttyPrompt ) << flush; + if (ttyDefaultToYes) { + out << QStringLiteral("%1: [Y/n] ").arg(ttyPrompt) << flush; } else { - out << QStringLiteral( "%1: [y/N] ").arg( ttyPrompt ) << flush; + out << QStringLiteral("%1: [y/N] ").arg(ttyPrompt) << flush; } input = in.readLine().trimmed(); - if( input.isEmpty() ) { + if (input.isEmpty()) { return ttyDefaultToYes; - } else if( input.toLower() == QLatin1String("y") ) { + } else if (input.toLower() == QLatin1String("y")) { return true; - } else if( input.toLower() == QLatin1String("n") ) { + } else if (input.toLower() == QLatin1String("n")) { return false; } } } else { auto okButton = KStandardGuiItem::ok(); okButton.setText(confirmText); auto rejectButton = KStandardGuiItem::cancel(); rejectButton.setText(rejectText); - int userAnswer = KMessageBox::questionYesNo( ICore::self()->uiController()->activeMainWindow(), - mainText + QLatin1String("\n\n") + mboxAdditionalText, - mboxTitle, - okButton, - rejectButton ); + int userAnswer = KMessageBox::questionYesNo(ICore::self()->uiController()->activeMainWindow(), + mainText + QLatin1String("\n\n") + mboxAdditionalText, + mboxTitle, + okButton, + rejectButton); return userAnswer == KMessageBox::Yes; } } -bool ensureWritable( const QList &urls ) +bool ensureWritable(const QList& urls) { QStringList notWritable; for (const QUrl& url : urls) { - if (url.isLocalFile()) - { + if (url.isLocalFile()) { QFile file(url.toLocalFile()); - if (file.exists() && !(file.permissions() & QFileDevice::WriteOwner) && !(file.permissions() & QFileDevice::WriteGroup)) - { + if (file.exists() && !(file.permissions() & QFileDevice::WriteOwner) && + !(file.permissions() & QFileDevice::WriteGroup)) { notWritable << url.toLocalFile(); } } } - if (!notWritable.isEmpty()) - { + + if (!notWritable.isEmpty()) { int answer = KMessageBox::questionYesNoCancel(ICore::self()->uiController()->activeMainWindow(), - i18n("You don't have write permissions for the following files; add write permissions for owner before saving?") + QLatin1String("\n\n") + notWritable.join(QLatin1Char('\n')), - i18n("Some files are write-protected"), - KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel()); + i18n( + "You don't have write permissions for the following files; add write permissions for owner before saving?") + + QLatin1String("\n\n") + notWritable.join(QLatin1Char('\n')), + i18n("Some files are write-protected"), + KStandardGuiItem::yes(), + KStandardGuiItem::no(), KStandardGuiItem::cancel()); if (answer == KMessageBox::Yes) { bool success = true; foreach (const QString& filename, notWritable) { QFile file(filename); QFileDevice::Permissions permissions = file.permissions(); permissions |= QFileDevice::WriteOwner; success &= file.setPermissions(permissions); } - if (!success) - { - KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), i18n("Failed adding write permissions for some files."), i18n("Failed setting permissions")); + + if (!success) { + KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), + i18n("Failed adding write permissions for some files."), + i18n("Failed setting permissions")); return false; } } return answer != KMessageBox::Cancel; } return true; } } diff --git a/kdevplatform/util/shellutils.h b/kdevplatform/util/shellutils.h index f067462efa..b79cd57e3b 100644 --- a/kdevplatform/util/shellutils.h +++ b/kdevplatform/util/shellutils.h @@ -1,55 +1,55 @@ /* * This file is part of KDevelop * * Copyright 2012 Ivan Shapovalov * * 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 SHELLUTILS_H #define SHELLUTILS_H #include "utilexport.h" #include class QString; class QUrl; namespace KDevelop { /** * Asks user of an arbitrary question by using either a \ref KMessageBox or stdin/stderr. * * @return @c true if user chose "Yes" and @c false otherwise. */ -bool KDEVPLATFORMUTIL_EXPORT askUser( const QString& mainText, - const QString& ttyPrompt, - const QString& mboxTitle, - const QString& mboxAdditionalText, - const QString& confirmText, - const QString& rejectText, - bool ttyDefaultToYes = true ); +bool KDEVPLATFORMUTIL_EXPORT askUser(const QString& mainText, + const QString& ttyPrompt, + const QString& mboxTitle, + const QString& mboxAdditionalText, + const QString& confirmText, + const QString& rejectText, + bool ttyDefaultToYes = true); /** * Ensures that the given list of files is writable. If some files are not writable, * asks the user whether they should be made writable. If the user disagrees, * or if the operation failed, returns false. * */ -bool KDEVPLATFORMUTIL_EXPORT ensureWritable( const QList &urls ); +bool KDEVPLATFORMUTIL_EXPORT ensureWritable(const QList& urls); } #endif // SHELLUTILS_H diff --git a/kdevplatform/util/stack.h b/kdevplatform/util/stack.h index edc70a0ec6..d05f927a9d 100644 --- a/kdevplatform/util/stack.h +++ b/kdevplatform/util/stack.h @@ -1,80 +1,82 @@ /* * Copyright 2015 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 KDEVELOP_STACK_H #define KDEVELOP_STACK_H #include namespace KDevelop { /** * @brief Implementation of a stack based on QVarLengthArray * * Since stacks are usually short-lived containers, it make sense to optimize their memory usage * * Internally using QVarLengthArray. The first @p Prealloc items are placed on the stack. * If the size of the stack exceeds @p Prealloc, the contents are moved to the heap. * * @note Make sure to pass a sensible amount for @p Prealloc; avoiding stack overflows * * The default value for Prealloc, 32, * seems to be a good candidate for between conserving stack space and keeping heap allocations low * (verified by a few heaptrack runs of duchainify) * * @sa QVarLengthArray */ template -class Stack : public QVarLengthArray +class Stack : public QVarLengthArray { using Base = QVarLengthArray; public: using Base::QVarLengthArray; - inline void swap(Stack &other) + inline void swap(Stack& other) { // prevent Stack<->QVarLengthArray swaps Base::swap(other); } - inline void push(const T &t) + inline void push(const T& t) { Base::append(t); } inline T pop() { T r = Base::last(); Base::removeLast(); return r; } - inline T& top() { + inline T& top() + { return Base::last(); } inline const T& top() const { return Base::last(); } }; } #endif // KDEVELOP_STACK_H diff --git a/kdevplatform/util/tests/test_embeddedfreetree.cpp b/kdevplatform/util/tests/test_embeddedfreetree.cpp index a2438ce65c..92068739c5 100644 --- a/kdevplatform/util/tests/test_embeddedfreetree.cpp +++ b/kdevplatform/util/tests/test_embeddedfreetree.cpp @@ -1,609 +1,677 @@ /* This file is part of KDevelop Copyright 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. */ unsigned int extractor_div_with = 0; #include #include #include #include #include #include #include #include #include #include #include #include -struct TestItem { - explicit TestItem(uint _value = 0) : value(_value) { +struct TestItem +{ + explicit TestItem(uint _value = 0) : value(_value) + { } uint value; int leftChild = -1; int rightChild = -1; - bool operator==(const TestItem& rhs) const { + bool operator==(const TestItem& rhs) const + { return value == rhs.value; } - bool operator<(const TestItem& item) const { + bool operator<(const TestItem& item) const + { return value < item.value; } }; -struct TestItemConversion { - static uint toIndex(const TestItem& item) { +struct TestItemConversion +{ + static uint toIndex(const TestItem& item) + { return item.value; } - static TestItem toItem(uint index) { + static TestItem toItem(uint index) + { return TestItem(index); } }; -struct Extractor{ - static TestItem extract(const TestItem& item) { - return TestItem(item.value/extractor_div_with); +struct Extractor +{ + static TestItem extract(const TestItem& item) + { + return TestItem(item.value / extractor_div_with); } }; +clock_t std_insertion = 0, std_removal = 0, std_contains = 0, std_iteration = 0, emb_insertion = 0, emb_removal = 0, + emb_contains = 0, emb_iteration = 0; -clock_t std_insertion = 0, std_removal = 0, std_contains = 0, std_iteration = 0, emb_insertion = 0, emb_removal = 0, emb_contains = 0, emb_iteration = 0; - -QString toString(const std::set& set) { +QString toString(const std::set& set) +{ QString ret; - for(auto it = set.begin(); it != set.end(); ++it) + for (auto it = set.begin(); it != set.end(); ++it) ret += QStringLiteral("%1 ").arg(*it); + return ret; } -bool operator==(const std::set& a, const std::set& b) { - if(a.size() != b.size()) { +bool operator==(const std::set& a, const std::set& b) +{ + if (a.size() != b.size()) { qDebug() << "size mismatch" << toString(a) << ": " << toString(b); return false; } auto aIt = a.begin(); auto bIt = b.begin(); - for(; aIt != a.end(); ++aIt, ++bIt) - if(*aIt != *bIt) { + for (; aIt != a.end(); ++aIt, ++bIt) + if (*aIt != *bIt) { qDebug() << "mismatch" << toString(a) << ": " << toString(b); return false; } + return true; } - struct TestItemHandler { - public: - static int rightChild(const TestItem& m_data) { - return m_data.rightChild; - } - static int leftChild(const TestItem& m_data) { - return m_data.leftChild; - } - static void setLeftChild(TestItem& m_data, int child) { - m_data.leftChild = child; - } - static void setRightChild(TestItem& m_data, int child) { - m_data.rightChild = child; - } - //Copies this item into the given one - static void copyTo(const TestItem& m_data, TestItem& data) { - data = m_data; - } - static void createFreeItem(TestItem& data) { - data = TestItem(); - } - static inline bool isFree(const TestItem& m_data) { - return !m_data.value; - } +struct TestItemHandler +{ +public: + static int rightChild(const TestItem& m_data) + { + return m_data.rightChild; + } + static int leftChild(const TestItem& m_data) + { + return m_data.leftChild; + } + static void setLeftChild(TestItem& m_data, int child) + { + m_data.leftChild = child; + } + static void setRightChild(TestItem& m_data, int child) + { + m_data.rightChild = child; + } + //Copies this item into the given one + static void copyTo(const TestItem& m_data, TestItem& data) + { + data = m_data; + } + static void createFreeItem(TestItem& data) + { + data = TestItem(); + } + static inline bool isFree(const TestItem& m_data) + { + return !m_data.value; + } - static const TestItem& data(const TestItem& m_data) { - return m_data; - } + static const TestItem& data(const TestItem& m_data) + { + return m_data; + } - inline static bool equals(const TestItem& m_data, const TestItem& rhs) { - return m_data.value == rhs.value; - } - }; + inline static bool equals(const TestItem& m_data, const TestItem& rhs) + { + return m_data.value == rhs.value; + } +}; - class TestItemBasedSet { - public: - TestItemBasedSet() { - } +class TestItemBasedSet +{ +public: + TestItemBasedSet() + { + } - void insert(uint i) { - TestItem item(i); - KDevelop::EmbeddedTreeAddItem add(data.data(), data.size(), m_centralFree, item); + void insert(uint i) + { + TestItem item(i); + KDevelop::EmbeddedTreeAddItem add(data.data(), data.size(), m_centralFree, item); - if((int)add.newItemCount() != (int)data.size()) { - QVector newData; - newData.resize(add.newItemCount()); - add.transferData(newData.data(), newData.size()); - data = newData; - } - } + if (( int )add.newItemCount() != ( int )data.size()) { + QVector newData; + newData.resize(add.newItemCount()); + add.transferData(newData.data(), newData.size()); + data = newData; + } + } - bool contains(uint item) { - KDevelop::EmbeddedTreeAlgorithms alg(data.data(), data.size(), m_centralFree); - return alg.indexOf(TestItem(item)) != -1; - } + bool contains(uint item) + { + KDevelop::EmbeddedTreeAlgorithms alg(data.data(), data.size(), m_centralFree); + return alg.indexOf(TestItem(item)) != -1; + } - void remove(uint i) { - TestItem item(i); - KDevelop::EmbeddedTreeRemoveItem remove(data.data(), data.size(), m_centralFree, item); + void remove(uint i) + { + TestItem item(i); + KDevelop::EmbeddedTreeRemoveItem remove(data.data(), data.size(), m_centralFree, + item); + + if (( int )remove.newItemCount() != ( int )data.size()) { + QVector newData; + newData.resize(remove.newItemCount()); + remove.transferData(newData.data(), newData.size()); + data = newData; + } + } - if((int)remove.newItemCount() != (int)data.size()) { - QVector newData; - newData.resize(remove.newItemCount()); - remove.transferData(newData.data(), newData.size()); - data = newData; - } - } + std::set toSet() const + { + std::set ret; - std::set toSet() const { - std::set ret; + for (int a = 0; a < data.size(); ++a) + if (data[a].value) + ret.insert(data[a].value); - for(int a = 0; a < data.size(); ++a) - if(data[a].value) - ret.insert(data[a].value); + return ret; + } - return ret; + void verify() + { + //1. verify order + uint last = 0; + uint freeCount = 0; + for (int a = 0; a < data.size(); ++a) { + if (data[a].value) { + QVERIFY(last < data[a].value); + last = data[a].value; + } else { + ++freeCount; } + } - void verify() { - //1. verify order - uint last = 0; - uint freeCount = 0; - for(int a = 0; a < data.size(); ++a) { - if(data[a].value) { - QVERIFY(last < data[a].value); - last = data[a].value; - }else{ - ++freeCount; - } - } - KDevelop::EmbeddedTreeAlgorithms algorithms(data.data(), data.size(), m_centralFree); - uint countFree = algorithms.countFreeItems(); - QCOMPARE(freeCount, countFree); - algorithms.verifyTreeConsistent(); - } + KDevelop::EmbeddedTreeAlgorithms algorithms(data.data(), data.size(), m_centralFree); + uint countFree = algorithms.countFreeItems(); + QCOMPARE(freeCount, countFree); + algorithms.verifyTreeConsistent(); + } - uint getItem(uint number) const { - Q_ASSERT(number < (uint)data.size()); - uint ret = 0; - uint size = (uint)data.size(); - uint current = 0; - for(uint a = 0; a < size; ++a) { - if(data[a].value) { - //Only count the non-free items - if(current == number) - ret = data[a].value; - ++current; - }else{ - //This is a free item - } - } - return ret; + uint getItem(uint number) const + { + Q_ASSERT(number < ( uint )data.size()); + uint ret = 0; + uint size = ( uint )data.size(); + uint current = 0; + for (uint a = 0; a < size; ++a) { + if (data[a].value) { + //Only count the non-free items + if (current == number) + ret = data[a].value; + ++current; + } else { + //This is a free item } + } - private: - int m_centralFree = -1; - QVector data; - }; + return ret; + } +private: + int m_centralFree = -1; + QVector data; +}; -class TestSet { - public: - void add(uint i) { - if(realSet.find(i) != realSet.end()) { +class TestSet +{ +public: + void add(uint i) + { + if (realSet.find(i) != realSet.end()) { QVERIFY(set.contains(i)); return; - }else{ + } else { QVERIFY(!set.contains(i)); } clock_t start = clock(); realSet.insert(i); std_insertion += clock() - start; start = clock(); set.insert(i); emb_insertion += clock() - start; start = clock(); bool contained = realSet.find(i) != realSet.end(); std_contains += clock() - start; start = clock(); set.contains(i); emb_contains += clock() - start; QVERIFY(set.contains(i)); QVERIFY(contained); set.verify(); } - void remove(uint i) { - if(realSet.find(i) != realSet.end()) { + void remove(uint i) + { + if (realSet.find(i) != realSet.end()) { QVERIFY(set.contains(i)); - }else{ + } else { QVERIFY(!set.contains(i)); return; } clock_t start = clock(); set.remove(i); emb_removal += clock() - start; start = clock(); realSet.erase(i); std_removal += clock() - start; - QVERIFY(!set.contains(i)); } - uint size() const { + uint size() const + { return realSet.size(); } - uint getItem(uint number) const { + uint getItem(uint number) const + { Q_ASSERT(number < size()); uint current = 0; uint ret = 0; clock_t start = clock(); - for(auto it = realSet.begin(); it != realSet.end(); ++it) { - if(current == number) { + for (auto it = realSet.begin(); it != realSet.end(); ++it) { + if (current == number) { ret = *it; } ++current; } + std_iteration += clock() - start; start = clock(); set.getItem(number); emb_iteration += clock() - start; Q_ASSERT(ret); return ret; } - void verify() { + void verify() + { QVERIFY(realSet == set.toSet()); set.verify(); } - private: + +private: std::set realSet; TestItemBasedSet set; }; -float toSeconds(clock_t time) { - return ((float)time) / CLOCKS_PER_SEC; +float toSeconds(clock_t time) +{ + return (( float )time) / CLOCKS_PER_SEC; } -struct StaticRepository { - static Utils::BasicSetRepository* repository() { +struct StaticRepository +{ + static Utils::BasicSetRepository* repository() + { static Utils::BasicSetRepository repository(QStringLiteral("test repository")); return &repository; } }; -struct UintSetVisitor { +struct UintSetVisitor +{ std::set& s; - explicit UintSetVisitor(std::set& _s) : s(_s) { + explicit UintSetVisitor(std::set& _s) : s(_s) + { } - inline bool operator() (const TestItem& item) { + inline bool operator()(const TestItem& item) + { s.insert(item.value); return true; } }; -struct NothingDoVisitor { - inline bool operator() (const TestItem& item) { +struct NothingDoVisitor +{ + inline bool operator()(const TestItem& item) + { Q_UNUSED(item); return true; } }; -class TestEmbeddedFreeTree : public QObject { - Q_OBJECT - private Q_SLOTS: - void initTestCase() { +class TestEmbeddedFreeTree : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase() + { KDevelop::AutoTestShell::init(); KDevelop::TestCore::initialize(KDevelop::Core::NoUi); } - void cleanupTestCase() { + void cleanupTestCase() + { KDevelop::TestCore::shutdown(); } - void randomizedTest() { + void randomizedTest() + { const int cycles = 10000; const int valueRange = 1000; const int removeProbability = 40; //Percent TestSet set; srand(time(nullptr)); - for(int a = 0; a < cycles; ++a) { - if(a % (cycles / 10) == 0) { + for (int a = 0; a < cycles; ++a) { + if (a % (cycles / 10) == 0) { qDebug() << "cycle" << a; } bool remove = (rand() % 100) < removeProbability; - if(remove && set.size()) { + if (remove && set.size()) { set.remove(set.getItem(rand() % set.size())); - }else{ + } else { int value = (rand() % valueRange) + 1; set.add(value); } set.verify(); } - qDebug() << "Performance embedded list: insertion:" << toSeconds(emb_insertion) << "removal:" << toSeconds(emb_removal) << "contains:" << toSeconds(emb_contains) << "iteration:" << toSeconds(emb_iteration); - qDebug() << "Performance std::set: insertion:" << toSeconds(std_insertion) << "removal:" << toSeconds(std_removal) << "contains:" << toSeconds(std_contains) << "iteration:" << toSeconds(std_iteration); + + qDebug() << "Performance embedded list: insertion:" << toSeconds(emb_insertion) << "removal:" << toSeconds( + emb_removal) << "contains:" << toSeconds(emb_contains) << "iteration:" << toSeconds(emb_iteration); + qDebug() << "Performance std::set: insertion:" << toSeconds(std_insertion) << "removal:" << toSeconds( + std_removal) << "contains:" << toSeconds(std_contains) << "iteration:" << toSeconds(std_iteration); } - void sequentialTest() { + void sequentialTest() + { TestSet set; set.add(5); set.verify(); set.remove(5); set.verify(); set.add(3); set.verify(); set.add(4); set.verify(); set.add(7); set.verify(); set.remove(3); set.verify(); set.remove(7); set.verify(); set.add(6); set.verify(); set.add(1); set.verify(); set.add(9); set.verify(); set.remove(4); set.verify(); set.remove(9); set.verify(); set.add(1); set.verify(); set.add(2); set.verify(); set.add(3); set.verify(); set.add(4); set.verify(); set.add(5); set.verify(); set.remove(1); set.verify(); set.remove(3); set.verify(); set.add(15); set.verify(); set.add(16); set.verify(); set.add(17); set.verify(); set.add(18); set.verify(); set.remove(18); set.verify(); set.remove(17); set.verify(); set.add(9); set.verify(); } - void testFiltering() { + void testFiltering() + { clock_t stdTime = 0; clock_t algoTime = 0; clock_t treeAlgoTime = 0; clock_t treeAlgoVisitorTime = 0; clock_t insertionStdTime = 0; clock_t insertionAlgoTime = 0; clock_t insertionTreeAlgoTime = 0; typedef Utils::StorableSet RepositorySet; const uint cycles = 3000; const uint setSize = 1500; uint totalItems = 0, totalFilteredItems = 0; srand(time(nullptr)); - for(uint a = 0; a < cycles; ++a) { + for (uint a = 0; a < cycles; ++a) { KDevelop::ConvenientFreeListSet set1; std::set testSet1; KDevelop::ConvenientFreeListSet set2; std::set testSet2; RepositorySet repSet2; - if(a % (cycles / 10) == 0) { + if (a % (cycles / 10) == 0) { qDebug() << "cycle" << a; } //Build the sets extractor_div_with = (rand() % 10) + 1; - for(uint a = 0; a < setSize; ++a) { + for (uint a = 0; a < setSize; ++a) { uint value = rand() % 3000; - uint divValue = value/extractor_div_with; - if(!divValue) + uint divValue = value / extractor_div_with; + if (!divValue) continue; // qDebug() << "inserting" << value; auto it = testSet1.lower_bound(value); int pos = set1.iterator().lowerBound(TestItem(value)); //This tests the upperBound functionality if (pos != -1) { QVERIFY(it != testSet1.end()); QVERIFY(set1.data()[pos].value == *it); } else { QVERIFY(it == testSet1.end()); } - if((rand() % 10) == 0) { + if ((rand() % 10) == 0) { set1.insert(TestItem(value)); testSet1.insert(value); } //This is tuned so in the end, about 99% of all declarations are filtered out, like in the symbol table. - if((rand() % (extractor_div_with*100)) == 0) { + if ((rand() % (extractor_div_with * 100)) == 0) { clock_t start = clock(); set2.insert(TestItem(divValue)); insertionStdTime += clock() - start; start = clock(); testSet2.insert(divValue); insertionAlgoTime += clock() - start; start = clock(); repSet2.insert(TestItem(divValue)); insertionTreeAlgoTime += clock() - start; start = clock(); } } std::set verifySet1; - for(KDevelop::ConvenientFreeListSet::Iterator it = set1.iterator(); it; ++it) + for (KDevelop::ConvenientFreeListSet::Iterator it = set1.iterator(); it; ++it) verifySet1.insert(it->value); std::set verifySet2; - for(KDevelop::ConvenientFreeListSet::Iterator it = set2.iterator(); it; ++it) + for (KDevelop::ConvenientFreeListSet::Iterator it = set2.iterator(); it; ++it) verifySet2.insert(it->value); std::set verifyRepSet2; - for(RepositorySet::Iterator it = repSet2.iterator(); it; ++it) + for (RepositorySet::Iterator it = repSet2.iterator(); it; ++it) verifyRepSet2.insert((*it).value); QCOMPARE(verifySet1, testSet1); QCOMPARE(verifySet2, testSet2); QCOMPARE(verifyRepSet2, testSet2); std::set algoFiltered; std::set treeAlgoFiltered; std::set treeAlgoVisitorFiltered; { //Do the filtering once without actions on the filtered items, just for calculating the time clock_t start = clock(); { - KDevelop::ConvenientEmbeddedSetFilterIterator filterIterator(set1.iterator(), set2.iterator()); - while(filterIterator) + KDevelop::ConvenientEmbeddedSetFilterIterator filterIterator(set1.iterator(), set2.iterator()); + while (filterIterator) ++filterIterator; algoTime += clock() - start; } start = clock(); { - KDevelop::ConvenientEmbeddedSetTreeFilterIterator filterIterator(set1.iterator(), repSet2); - while(filterIterator) + KDevelop::ConvenientEmbeddedSetTreeFilterIterator filterIterator(set1.iterator(), repSet2); + while (filterIterator) ++filterIterator; treeAlgoTime += clock() - start; } { start = clock(); NothingDoVisitor v; - KDevelop::ConvenientEmbeddedSetTreeFilterVisitor visit(v, set1.iterator(), repSet2); + KDevelop::ConvenientEmbeddedSetTreeFilterVisitor visit(v, set1.iterator(), repSet2); treeAlgoVisitorTime += clock() - start; } - start = clock(); - for(auto it = testSet1.begin(); it != testSet1.end(); ++it) { - if(testSet2.count((*it) / extractor_div_with) == 1) { + for (auto it = testSet1.begin(); it != testSet1.end(); ++it) { + if (testSet2.count((*it) / extractor_div_with) == 1) { } } stdTime += clock() - start; } { - KDevelop::ConvenientEmbeddedSetFilterIterator filterIterator(set1.iterator(), set2.iterator()); - while(filterIterator) { + KDevelop::ConvenientEmbeddedSetFilterIterator filterIterator(set1.iterator(), set2.iterator()); + while (filterIterator) { algoFiltered.insert(filterIterator->value); ++filterIterator; } } { - KDevelop::ConvenientEmbeddedSetTreeFilterIterator filterIterator(set1.iterator(), repSet2); - while(filterIterator) { + KDevelop::ConvenientEmbeddedSetTreeFilterIterator filterIterator(set1.iterator(), repSet2); + while (filterIterator) { treeAlgoFiltered.insert((*filterIterator).value); ++filterIterator; } } { UintSetVisitor v(treeAlgoVisitorFiltered); - KDevelop::ConvenientEmbeddedSetTreeFilterVisitor visit(v, set1.iterator(), repSet2); + KDevelop::ConvenientEmbeddedSetTreeFilterVisitor visit(v, set1.iterator(), repSet2); } - totalItems += testSet1.size(); totalFilteredItems += algoFiltered.size(); - std::set stdFiltered; - for(auto it = testSet1.begin(); it != testSet1.end(); ++it) { - if(testSet2.count((*it) / extractor_div_with) == 1) { + for (auto it = testSet1.begin(); it != testSet1.end(); ++it) { + if (testSet2.count((*it) / extractor_div_with) == 1) { stdFiltered.insert(*it); } } QCOMPARE(algoFiltered, stdFiltered); QCOMPARE(treeAlgoFiltered, stdFiltered); QCOMPARE(treeAlgoVisitorFiltered, stdFiltered); } - qDebug() << "Filtering performance: embedded-list filtering:" << toSeconds(algoTime) << "set-repository filtering:" << toSeconds(treeAlgoTime) << "set-repository visitor filtering:" << toSeconds(treeAlgoVisitorTime) << "std::set filtering:" << toSeconds(stdTime) << "Normal -> Embedded speedup ratio:" << (toSeconds(stdTime) / toSeconds(algoTime)) << "Normal -> Repository speedup ratio:" << (toSeconds(stdTime) / toSeconds(treeAlgoVisitorTime)) << "total processed items:" << totalItems << "total items after filtering:" << totalFilteredItems; - qDebug() << "Insertion: embedded-list:" << toSeconds(insertionAlgoTime) << "set-repository:" << toSeconds(insertionTreeAlgoTime) << "std::set:" << toSeconds(insertionStdTime); + + qDebug() << "Filtering performance: embedded-list filtering:" << toSeconds(algoTime) << + "set-repository filtering:" << toSeconds(treeAlgoTime) << "set-repository visitor filtering:" << toSeconds( + treeAlgoVisitorTime) << "std::set filtering:" << toSeconds(stdTime) << + "Normal -> Embedded speedup ratio:" << (toSeconds(stdTime) / toSeconds(algoTime)) << + "Normal -> Repository speedup ratio:" << (toSeconds(stdTime) / toSeconds(treeAlgoVisitorTime)) << + "total processed items:" << totalItems << "total items after filtering:" << totalFilteredItems; + qDebug() << "Insertion: embedded-list:" << toSeconds(insertionAlgoTime) << "set-repository:" << toSeconds( + insertionTreeAlgoTime) << "std::set:" << toSeconds(insertionStdTime); } }; #include "test_embeddedfreetree.moc" QTEST_MAIN(TestEmbeddedFreeTree) diff --git a/kdevplatform/util/tests/test_environment.cpp b/kdevplatform/util/tests/test_environment.cpp index 52cea2530f..ed226554eb 100644 --- a/kdevplatform/util/tests/test_environment.cpp +++ b/kdevplatform/util/tests/test_environment.cpp @@ -1,82 +1,82 @@ /* * This file is part of KDevelop * * Copyright 2015 Artur Puzio * * 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 "test_environment.h" #include "util/environmentprofilelist.h" #include #include QTEST_MAIN(TestEnvironment) -using ProcEnv = QMap; +using ProcEnv = QMap; void TestEnvironment::testExpandVariables_data() { QTest::addColumn("env"); QTest::addColumn("expectedEnv"); QTest::newRow("no variables") << ProcEnv({}) << ProcEnv({}); QTest::newRow("simple variables") << ProcEnv{ - {"VAR1","data"}, - {"Var2","some other data"} - } << ProcEnv({ - {"VAR1","data"}, - {"Var2","some other data"} + {"VAR1", "data"}, + {"Var2", "some other data"} + } << ProcEnv({ + {"VAR1", "data"}, + {"Var2", "some other data"} }); - QTest::newRow("PATH append and prepend") << ProcEnv({ - {"PATH","/home/usr/bin:$PATH:/home/user/folder"} + QTest::newRow("PATH append and prepend") << ProcEnv({ + {"PATH", "/home/usr/bin:$PATH:/home/user/folder"} }) << ProcEnv({ {"PATH", "/home/usr/bin:/bin:/usr/bin:/home/user/folder"} }); QTest::newRow("\\$VAR") << ProcEnv({ - {"MY_VAR","\\$PATH something \\$HOME"} + {"MY_VAR", "\\$PATH something \\$HOME"} }) << ProcEnv({ - {"MY_VAR","$PATH something $HOME"} + {"MY_VAR", "$PATH something $HOME"} }); QTest::newRow("spaces, \\$VAR after $VAR") << ProcEnv({ - {"MY_VAR","$PATH:$HOME something \\$HOME"} + {"MY_VAR", "$PATH:$HOME something \\$HOME"} }) << ProcEnv({ - {"MY_VAR","/bin:/usr/bin:/home/tom something $HOME"} + {"MY_VAR", "/bin:/usr/bin:/home/tom something $HOME"} }); QTest::newRow("VAR2=$VAR1") << ProcEnv({ - {"VAR1","/some/path"},{"VAR2","$VAR1"} + {"VAR1", "/some/path"}, {"VAR2", "$VAR1"} }) << ProcEnv({ - {"VAR1","/some/path"},{"VAR2",""} + {"VAR1", "/some/path"}, {"VAR2", ""} }); } void TestEnvironment::testExpandVariables() { QFETCH(ProcEnv, env); QFETCH(ProcEnv, expectedEnv); QProcessEnvironment fakeSysEnv; - fakeSysEnv.insert(QStringLiteral("PATH"),QStringLiteral("/bin:/usr/bin")); - fakeSysEnv.insert(QStringLiteral("HOME"),QStringLiteral("/home/tom")); + fakeSysEnv.insert(QStringLiteral("PATH"), QStringLiteral("/bin:/usr/bin")); + fakeSysEnv.insert(QStringLiteral("HOME"), QStringLiteral("/home/tom")); KDevelop::expandVariables(env, fakeSysEnv); - for (auto it = expectedEnv.cbegin(); it!= expectedEnv.cend(); ++it) { + for (auto it = expectedEnv.cbegin(); it != expectedEnv.cend(); ++it) { QCOMPARE(env.value(it.key()), it.value()); } } diff --git a/kdevplatform/util/tests/test_executecompositejob.h b/kdevplatform/util/tests/test_executecompositejob.h index aaa936ef2d..930b612142 100644 --- a/kdevplatform/util/tests/test_executecompositejob.h +++ b/kdevplatform/util/tests/test_executecompositejob.h @@ -1,55 +1,57 @@ /* * This file is part of KDevelop * Copyright 2014 Milian Wolff * * 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 KDEVPLATFORM_TEST_EXECUTECOMPOSITEJOB_H #define KDEVPLATFORM_TEST_EXECUTECOMPOSITEJOB_H #include #include class TestJob : public KJob { Q_OBJECT + public: void start() override { emit started(this); } void callEmitResult() { emitResult(); } Q_SIGNALS: void started(KJob* job); }; class TestExecuteCompositeJob : public QObject { Q_OBJECT + private Q_SLOTS: void runOneJob(); void runTwoJobs(); }; #endif // KDEVPLATFORM_TEST_EXECUTECOMPOSITEJOB_H diff --git a/kdevplatform/util/tests/test_foregroundlock.cpp b/kdevplatform/util/tests/test_foregroundlock.cpp index 0cbb7d2ada..1b820fce6b 100644 --- a/kdevplatform/util/tests/test_foregroundlock.cpp +++ b/kdevplatform/util/tests/test_foregroundlock.cpp @@ -1,89 +1,93 @@ /* Copyright 2010 Milian Wolff 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 "test_foregroundlock.h" #include #include #include "../foregroundlock.h" QTEST_MAIN(KDevelop::TestForegroundLock) using namespace KDevelop; //BEGIN Helper Threads -class TryLockThread : public QThread { +class TryLockThread : public QThread +{ Q_OBJECT + public: - void run() override { + void run() override + { ForegroundLock lock(false); - for(int i = 0; i < 1000; ++i) { + for (int i = 0; i < 1000; ++i) { if (lock.tryLock()) { lock.unlock(); } QThread::usleep(qrand() % 20); } } }; void TestForegroundLock::testTryLock_data() { QTest::addColumn("numThreads"); for (int i = 1; i <= 10; ++i) { QTest::newRow(qPrintable(QString::number(i))) << i; } } void TestForegroundLock::testTryLock() { QFETCH(int, numThreads); QList threads; for (int i = 0; i < numThreads; ++i) { threads << new TryLockThread; } ForegroundLock lock(true); - foreach(TryLockThread* thread, threads) { + foreach (TryLockThread* thread, threads) { thread->start(); } lock.unlock(); - while(true) { + while (true) { bool running = false; - foreach(TryLockThread* thread, threads) { + foreach (TryLockThread* thread, threads) { if (thread->isRunning()) { running = true; break; } } + if (!running) { break; } lock.relock(); QThread::usleep(10); lock.unlock(); } } #include "moc_test_foregroundlock.cpp" #include "test_foregroundlock.moc" diff --git a/kdevplatform/util/tests/test_foregroundlock.h b/kdevplatform/util/tests/test_foregroundlock.h index bedf45cab3..ca230fed79 100644 --- a/kdevplatform/util/tests/test_foregroundlock.h +++ b/kdevplatform/util/tests/test_foregroundlock.h @@ -1,35 +1,35 @@ /* Copyright 2010 Milian Wolff 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. */ #ifndef KDEVPLATFORM_TEST_FOREGROUNDLOCK_H #define KDEVPLATFORM_TEST_FOREGROUNDLOCK_H #include namespace KDevelop { -class TestForegroundLock : public QObject { +class TestForegroundLock : public QObject +{ Q_OBJECT private Q_SLOTS: void testTryLock_data(); void testTryLock(); }; } - #endif // KDEVPLATFORM_TEST_FOREGROUNDLOCK_H diff --git a/kdevplatform/util/tests/test_kdevformatsource.cpp b/kdevplatform/util/tests/test_kdevformatsource.cpp index 7ad67bad1d..5405d3fd6a 100644 --- a/kdevplatform/util/tests/test_kdevformatsource.cpp +++ b/kdevplatform/util/tests/test_kdevformatsource.cpp @@ -1,260 +1,261 @@ /* Copyright 2016 Anton Anikin 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 "test_kdevformatsource.h" #include "../kdevformatfile.h" #include "qtcompat_p.h" #include #include #include #include #include QTEST_MAIN(KDevelop::TestKdevFormatSource) using namespace KDevelop; TestKdevFormatSource::TestKdevFormatSource() { } TestKdevFormatSource::~TestKdevFormatSource() { } void TestKdevFormatSource::testNotFound_data() { static const QStringList formatFileData = {}; QCOMPARE(initTest(formatFileData), true); for (const Source& source : qAsConst(m_sources)) { QTest::newRow(source.path.toUtf8()) << source.path << false << false << false << source.lines; } } void TestKdevFormatSource::testNotFound() { runTest(); } void TestKdevFormatSource::testNoCommands_data() { static const QStringList formatFileData = {QStringLiteral("# some comment")}; QCOMPARE(initTest(formatFileData), true); for (const Source& source : qAsConst(m_sources)) { QTest::newRow(source.path.toUtf8()) << source.path << true << false << false << source.lines; } } void TestKdevFormatSource::testNoCommands() { runTest(); } void TestKdevFormatSource::testNotMatch_data() { static const QStringList formatFileData = {QStringLiteral("notmatched.cpp : unused_command")}; QCOMPARE(initTest(formatFileData), true); for (const Source& source : qAsConst(m_sources)) { QTest::newRow(source.path.toUtf8()) << source.path << true << true << false << source.lines; } } void TestKdevFormatSource::testNotMatch() { runTest(); } void TestKdevFormatSource::testMatch1_data() { static const QStringList formatFileData({ QStringLiteral("src1/source_1.cpp : cat $ORIGFILE | sed 's/foo/FOO/' > tmp && mv tmp $ORIGFILE"), QStringLiteral("src2/source_2.cpp : cat $ORIGFILE | sed 's/sqrt/std::sqrt/' > tmp && mv tmp $ORIGFILE"), QStringLiteral("*.cpp : cat $ORIGFILE | sed 's/z/Z/' > tmp && mv tmp $ORIGFILE"), QStringLiteral("notmatched.cpp : unused_command"), }); QCOMPARE(initTest(formatFileData), true); m_sources[0].lines.replaceInStrings(QStringLiteral("foo"), QStringLiteral("FOO")); m_sources[1].lines.replaceInStrings(QStringLiteral("sqrt"), QStringLiteral("std::sqrt")); m_sources[2].lines.replaceInStrings(QStringLiteral("z"), QStringLiteral("Z")); for (const Source& source : qAsConst(m_sources)) { QTest::newRow(source.path.toUtf8()) << source.path << true << true << true << source.lines; } } void TestKdevFormatSource::testMatch1() { runTest(); } void TestKdevFormatSource::testMatch2_data() { static const QStringList formatFileData({QStringLiteral("cat $ORIGFILE | sed 's/;/;;/' > tmp && mv tmp $ORIGFILE")}); QCOMPARE(initTest(formatFileData), true); for (Source& source : m_sources) { source.lines.replaceInStrings(QStringLiteral(";"), QStringLiteral(";;")); QTest::newRow(source.path.toUtf8()) << source.path << true << true << true << source.lines; } } void TestKdevFormatSource::testMatch2() { runTest(); } bool TestKdevFormatSource::initTest(const QStringList& formatFileData) { QTest::addColumn("path"); QTest::addColumn("isFound"); QTest::addColumn("isRead"); QTest::addColumn("isApplied"); QTest::addColumn("lines"); m_temporaryDir.reset(new QTemporaryDir); const QString workPath = m_temporaryDir->path(); qDebug() << "Using temporary dir:" << workPath; if (!mkPath(workPath + "/src1")) return false; if (!mkPath(workPath + "/src2")) return false; if (!QDir::setCurrent(workPath)) { qDebug() << "unable to set current directory to" << workPath; return false; } m_sources.resize(3); m_sources[0].path = workPath + "/src1/source_1.cpp"; m_sources[0].lines = QStringList({ QStringLiteral("void foo(int x) {"), QStringLiteral(" printf(\"squared x = %d\\n\", x * x);"), QStringLiteral("}") }); m_sources[1].path = workPath + "/src2/source_2.cpp"; m_sources[1].lines = QStringList({ QStringLiteral("void bar(double x) {"), QStringLiteral(" x = sqrt(x);"), QStringLiteral(" printf(\"sqrt(x) = %e\\n\", x);"), QStringLiteral("}") }); m_sources[2].path = workPath + "/source_3.cpp"; m_sources[2].lines = QStringList({ QStringLiteral("void baz(double x, double y) {"), QStringLiteral(" double z = pow(x, y);"), QStringLiteral(" printf(\"x^y = %e\\n\", z);"), QStringLiteral("}") }); for (const Source& source : qAsConst(m_sources)) { if (!writeLines(source.path, source.lines)) return false; } if (!formatFileData.isEmpty() && !writeLines(QStringLiteral("format_sources"), formatFileData)) return false; return true; } void TestKdevFormatSource::runTest() const { QFETCH(QString, path); QFETCH(bool, isFound); QFETCH(bool, isRead); QFETCH(bool, isApplied); QFETCH(QStringList, lines); KDevFormatFile formatFile(path, path); QCOMPARE(formatFile.find(), isFound); if (isFound) QCOMPARE(formatFile.read(), isRead); if (isRead) QCOMPARE(formatFile.apply(), isApplied); QStringList processedLines; QCOMPARE(readLines(path, processedLines), true); QCOMPARE(processedLines, lines); } bool TestKdevFormatSource::mkPath(const QString& path) const { if (!QDir().exists(path) && !QDir().mkpath(path)) { qDebug() << "unable to create directory" << path; return false; } return true; } bool TestKdevFormatSource::writeLines(const QString& path, const QStringList& lines) const { QFile outFile(path); if (!outFile.open(QIODevice::WriteOnly)) { qDebug() << "unable to open file" << path << "for writing"; return false; } QTextStream outStream(&outFile); for (const QString& line : lines) { outStream << line << "\n"; } + outStream.flush(); outFile.close(); return true; } bool TestKdevFormatSource::readLines(const QString& path, QStringList& lines) const { QFile inFile(path); if (!inFile.open(QIODevice::ReadOnly)) { qDebug() << "unable to open file" << path << "for reading"; return false; } lines.clear(); QTextStream inStream(&inFile); while (!inStream.atEnd()) { lines += inStream.readLine(); } inFile.close(); return true; } diff --git a/kdevplatform/util/tests/test_kdevformatsource.h b/kdevplatform/util/tests/test_kdevformatsource.h index 8abb3b6328..d19de7f4eb 100644 --- a/kdevplatform/util/tests/test_kdevformatsource.h +++ b/kdevplatform/util/tests/test_kdevformatsource.h @@ -1,72 +1,71 @@ /* Copyright 2016 Anton Anikin 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. */ #pragma once #include #include #include class QTemporaryDir; -namespace KDevelop -{ +namespace KDevelop { struct Source { QString path; QStringList lines; }; class TestKdevFormatSource : public QObject { Q_OBJECT public: TestKdevFormatSource(); ~TestKdevFormatSource() override; private Q_SLOTS: void testNotFound(); void testNotFound_data(); void testNoCommands(); void testNoCommands_data(); void testNotMatch(); void testNotMatch_data(); void testMatch1(); void testMatch1_data(); void testMatch2(); void testMatch2_data(); private: bool initTest(const QStringList& formatFileData); void runTest() const; bool mkPath(const QString& path) const; bool writeLines(const QString& path, const QStringList& lines) const; bool readLines(const QString& path, QStringList& lines) const; QScopedPointer m_temporaryDir; QVector m_sources; }; } diff --git a/kdevplatform/util/tests/test_kdevvarlengtharray.cpp b/kdevplatform/util/tests/test_kdevvarlengtharray.cpp index 15e19c164e..4ea78b843e 100644 --- a/kdevplatform/util/tests/test_kdevvarlengtharray.cpp +++ b/kdevplatform/util/tests/test_kdevvarlengtharray.cpp @@ -1,99 +1,102 @@ /* This file is part of KDevelop Copyright 2010 Milian Wolff 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 #include #include #include "../kdevvarlengtharray.h" -struct TestValue { +struct TestValue +{ TestValue() {} TestValue(const TestValue& other) { if (other.m_index) { int mustDo = 1; ++mustDo; } m_index = other.m_index; } uint m_index = 0; }; -class TestKDevVarLengthArray : public QObject { +class TestKDevVarLengthArray : public QObject +{ Q_OBJECT private Q_SLOTS: /** * Make sure that valgrind does not report any warnings here * about uninitialized member variables. */ - void appendReallocIntegrity() { + void appendReallocIntegrity() + { KDevVarLengthArray array; QCOMPARE(array.size(), 0); QCOMPARE(array.capacity(), 2); qDebug() << "append item 1"; array << TestValue(); qDebug() << "appended index is:" << array[0].m_index; QCOMPARE(array.size(), 1); QCOMPARE(array.capacity(), 2); qDebug() << "append item 2"; array << TestValue(); // should trigger the realloc qDebug() << "appended index is:" << array[1].m_index; QCOMPARE(array.size(), 2); QCOMPARE(array.capacity(), 2); qDebug() << "append item 3"; array << TestValue(); qDebug() << "appended index is:" << array[2].m_index; QCOMPARE(array.size(), 3); QCOMPARE(array.capacity(), 4); array.clear(); } void mixed() { KDevVarLengthArray array; array.append(1); array << 2; array.insert(0, 0); QCOMPARE(array.back(), 2); array.pop_back(); QCOMPARE(array.back(), 1); array.append(1); QVERIFY(array.removeOne(1)); QCOMPARE(array.toList(), QList() << 0 << 1); QCOMPARE(array.toVector(), QVector() << 0 << 1); array.insert(0, 42); QCOMPARE(array.toVector(), QVector() << 42 << 0 << 1); array.remove(0); QCOMPARE(array.toVector(), QVector() << 0 << 1); QVERIFY(array.contains(1)); QVERIFY(!array.contains(42)); QCOMPARE(array.back(), 1); QCOMPARE(array.indexOf(1), 1); QCOMPARE(array.indexOf(42), -1); } }; QTEST_MAIN(TestKDevVarLengthArray) #include "test_kdevvarlengtharray.moc" diff --git a/kdevplatform/util/tests/test_path.cpp b/kdevplatform/util/tests/test_path.cpp index ee11ee56d8..812052d6ae 100644 --- a/kdevplatform/util/tests/test_path.cpp +++ b/kdevplatform/util/tests/test_path.cpp @@ -1,622 +1,629 @@ /* * This file is part of KDevelop * Copyright 2012 Milian Wolff * Copyright 2015 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 "test_path.h" #include #include #include QTEST_MAIN(TestPath) using namespace KDevelop; static const int FILES_PER_FOLDER = 10; static const int FOLDERS_PER_FOLDER = 5; static const int TREE_DEPTH = 5; template T stringToUrl(const QString& path) { return T(path); } template<> QStringList stringToUrl(const QString& path) { return path.split('/'); } template T childUrl(const T& parent, const QString& child) { return T(parent, child); } template<> QStringList childUrl(const QStringList& parent, const QString& child) { QStringList ret = parent; ret << child; return ret; } template<> QUrl childUrl(const QUrl& parent, const QString& child) { QUrl ret = parent; ret.setPath(ret.path() + '/' + child); return ret; } template QVector generateData(const T& parent, int level) { QVector ret; // files per folder for (int i = 0; i < FILES_PER_FOLDER; ++i) { const QString fileName = QStringLiteral("file%1.txt").arg(i); const T file = childUrl(parent, fileName); Q_ASSERT(!ret.contains(file)); ret << file; } + // nesting depth if (level < TREE_DEPTH) { // folders per folder for (int i = 0; i < FOLDERS_PER_FOLDER; ++i) { const QString folderName = QStringLiteral("folder%1").arg(i); const T folder = childUrl(parent, folderName); Q_ASSERT(!ret.contains(folder)); ret << folder; ret += generateData(folder, level + 1); } } return ret; } template void runBenchmark() { QBENCHMARK { const T base = stringToUrl("/tmp/foo/bar"); generateData(base, 0); } } void TestPath::bench_qurl() { runBenchmark(); } void TestPath::bench_qstringlist() { runBenchmark(); } void TestPath::bench_path() { runBenchmark(); } void TestPath::bench_fromLocalPath() { QFETCH(int, variant); const QString input(QStringLiteral("/foo/bar/asdf/bla/blub.h")); const int repeat = 1000; if (variant == 1) { QBENCHMARK { - for(int i = 0; i < repeat; ++i) { + for (int i = 0; i < repeat; ++i) { Path path = Path(QUrl::fromLocalFile(input)); Q_UNUSED(path); } } } else if (variant == 2) { QBENCHMARK { - for(int i = 0; i < repeat; ++i) { + for (int i = 0; i < repeat; ++i) { Path path = Path(input); Q_UNUSED(path); } } } else { QFAIL("unexpected variant"); } } void TestPath::bench_fromLocalPath_data() { QTest::addColumn("variant"); QTest::newRow("QUrl::fromLocalFile") << 1; QTest::newRow("QString") << 2; } void TestPath::bench_hash() { const Path path(QStringLiteral("/my/very/long/path/to/a/file.cpp")); QBENCHMARK { auto hash = qHash(path); Q_UNUSED(hash); } } /// Invoke @p op on URL @p base, but preserve drive letter if @p op removes it template QUrl preserveWindowsDriveLetter(const QUrl& base, Func op) { #ifndef Q_OS_WIN return op(base); #else // only apply to local files if (!base.isLocalFile()) { return op(base); } // save drive letter const QString windowsDriveLetter = base.toLocalFile().mid(0, 2); QUrl url = op(base); // restore drive letter if (url.toLocalFile().startsWith('/')) { url = QUrl::fromLocalFile(windowsDriveLetter + url.toLocalFile()); } return url; #endif } QUrl resolvedUrl(const QUrl& base, const QUrl& relative) { - return preserveWindowsDriveLetter(base, [&](const QUrl& url) { return url.resolved(relative); }); + return preserveWindowsDriveLetter(base, [&](const QUrl& url) { + return url.resolved(relative); + }); } QUrl comparableUpUrl(const QUrl& url) { - QUrl ret = preserveWindowsDriveLetter(url, [&](const QUrl& url) { return KIO::upUrl(url).adjusted(QUrl::RemovePassword); }); + QUrl ret = preserveWindowsDriveLetter(url, [&](const QUrl& url) { + return KIO::upUrl(url).adjusted(QUrl::RemovePassword); + }); return ret.adjusted(QUrl::StripTrailingSlash); } void TestPath::testPath() { QFETCH(QString, input); QUrl url = QUrl::fromUserInput(input); url = url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments); Path optUrl(input); if (!url.password().isEmpty()) { QUrl urlNoPass = url.adjusted(QUrl::RemovePassword); QCOMPARE(optUrl.toUrl(), urlNoPass); } else { QCOMPARE(optUrl.toUrl(), url); } QCOMPARE(optUrl.isLocalFile(), url.isLocalFile()); QCOMPARE(optUrl.pathOrUrl(), toUrlOrLocalFile(url, QUrl::RemovePassword)); QCOMPARE(optUrl.isValid(), url.isValid()); QCOMPARE(optUrl.isEmpty(), url.isEmpty()); QCOMPARE(optUrl.lastPathSegment(), url.fileName()); QCOMPARE(optUrl.path(), url.isLocalFile() ? url.toLocalFile() : url.path()); QCOMPARE(optUrl.parent().toUrl(), comparableUpUrl(url)); QCOMPARE(optUrl.toLocalFile(), url.toLocalFile()); QCOMPARE(optUrl, Path(input)); QCOMPARE(optUrl, Path(optUrl)); QVERIFY(optUrl != Path(input + "/asdf")); if (url.isLocalFile() && !input.startsWith(QLatin1String("file://"))) { QCOMPARE(optUrl, Path(QUrl::fromLocalFile(input))); } QCOMPARE(optUrl, Path(url)); if (url.isValid()) { QVERIFY(optUrl.relativePath(optUrl).isEmpty()); Path relativePath(optUrl, QStringLiteral("foo/bar")); QCOMPARE(optUrl.relativePath(relativePath), QLatin1String("foo/bar")); QCOMPARE(relativePath.relativePath(optUrl), QLatin1String("../../")); QVERIFY(optUrl.isParentOf(relativePath)); QVERIFY(!relativePath.isParentOf(optUrl)); #ifndef Q_OS_WIN Path absolutePath(optUrl, QStringLiteral("/laa/loo")); QCOMPARE(absolutePath.path(), QLatin1String("/laa/loo")); QCOMPARE(url.resolved(QUrl(QStringLiteral("/laa/loo"))).path(), QLatin1String("/laa/loo")); Path absolutePath2(optUrl, QStringLiteral("/")); QCOMPARE(absolutePath2.path(), QLatin1String("/")); QCOMPARE(url.resolved(QUrl(QStringLiteral("/"))).path(), QLatin1String("/")); #endif Path unrelatedPath(QStringLiteral("https://test@blubasdf.com:12345/")); QCOMPARE(optUrl.relativePath(unrelatedPath), unrelatedPath.pathOrUrl()); QCOMPARE(unrelatedPath.relativePath(optUrl), optUrl.pathOrUrl()); QVERIFY(!unrelatedPath.isParentOf(optUrl)); QVERIFY(!optUrl.isParentOf(unrelatedPath)); } QCOMPARE(Path().relativePath(optUrl), optUrl.pathOrUrl()); QVERIFY(optUrl.relativePath(Path()).isEmpty()); QVERIFY(Path().relativePath(Path()).isEmpty()); QVERIFY(!optUrl.isParentOf(Path())); QVERIFY(!Path().isParentOf(optUrl)); QVERIFY(!Path().isParentOf(Path())); QVERIFY(!optUrl.isParentOf(optUrl)); QCOMPARE(optUrl.isRemote(), optUrl.isValid() && !optUrl.isLocalFile()); QCOMPARE(optUrl.isRemote(), optUrl.isValid() && !optUrl.remotePrefix().isEmpty()); if (url.path() == QLatin1String("/")) { url.setPath("/test/foo/bar"); } else { url.setPath(url.path() + "/test/foo/bar"); } if (url.scheme().isEmpty()) { url.setScheme(QStringLiteral("file")); } optUrl.addPath(QStringLiteral("test/foo/bar")); QCOMPARE(optUrl.lastPathSegment(), url.fileName()); QCOMPARE(optUrl.path(), url.isLocalFile() ? url.toLocalFile() : url.path()); url = url.adjusted(QUrl::RemoveFilename); url.setPath(url.path() + "lalalala_adsf.txt"); optUrl.setLastPathSegment(QStringLiteral("lalalala_adsf.txt")); QCOMPARE(optUrl.lastPathSegment(), url.fileName()); QCOMPARE(optUrl.path(), url.isLocalFile() ? url.toLocalFile() : url.path()); QCOMPARE(optUrl.parent().toUrl(), comparableUpUrl(url)); QVERIFY(optUrl.parent().isDirectParentOf(optUrl)); QVERIFY(!optUrl.parent().parent().isDirectParentOf(optUrl)); #ifndef Q_OS_WIN Path a(QStringLiteral("/foo/bar/asdf/")); Path b(QStringLiteral("/foo/bar/")); QVERIFY(b.isDirectParentOf(a)); Path c(QStringLiteral("/foo/bar")); QVERIFY(c.isDirectParentOf(a)); #endif optUrl.clear(); url.clear(); QCOMPARE(optUrl.toUrl(), url); } void TestPath::testPath_data() { QTest::addColumn("input"); #ifndef Q_OS_WIN QTest::newRow("invalid") << ""; QTest::newRow("path") << "/tmp/foo/asdf.txt"; QTest::newRow("path-folder") << "/tmp/foo/asdf/"; QTest::newRow("root") << "/"; QTest::newRow("clean-path") << "/tmp/..///asdf/"; QTest::newRow("file") << "file:///tmp/foo/asdf.txt"; QTest::newRow("file-folder") << "file:///tmp/foo/bar/"; #else QTest::newRow("path") << "C:/tmp/foo/asdf.txt"; QTest::newRow("path-folder") << "C:/tmp/foo/asdf/"; QTest::newRow("root") << "C:/"; QTest::newRow("clean-path") << "C:/tmp/..///asdf/"; QTest::newRow("file") << "file:///C:/tmp/foo/asdf.txt"; QTest::newRow("file-folder") << "file:///C:/tmp/foo/bar/"; #endif QTest::newRow("remote-root") << "http://www.test.com/"; QTest::newRow("http") << "http://www.test.com/tmp/asdf.txt"; QTest::newRow("ftps") << "ftps://user@host.com/tmp/foo/asdf.txt"; QTest::newRow("password") << "ftps://user:password@host.com/tmp/asdf.txt"; QTest::newRow("port") << "http://localhost:8080/foo/bar/test.txt"; } void TestPath::testPathInvalid() { QFETCH(QString, input); Path url(input); QVERIFY(!url.isValid()); QVERIFY(url.isEmpty()); } void TestPath::testPathInvalid_data() { QTest::addColumn("input"); QTest::newRow("empty") << ""; QTest::newRow("fragment") << "http://test.com/#hello"; QTest::newRow("query") << "http://test.com/?hello"; QTest::newRow("suburl") << "file:///home/weis/kde.tgz#gzip:/#tar:/kdebase"; QTest::newRow("relative") << "../foo/bar"; QTest::newRow("name") << "asdfasdf"; QTest::newRow("remote-nopath") << "http://www.test.com"; } void TestPath::testPathOperators() { QFETCH(Path, left); QFETCH(Path, right); QFETCH(bool, equal); QFETCH(bool, less); bool greater = !equal && !less; QVERIFY(left == left); QVERIFY(right == right); QCOMPARE(left == right, equal); QCOMPARE(right == left, equal); QCOMPARE(left < right, less); QCOMPARE(left <= right, less || equal); QCOMPARE(left > right, greater); QCOMPARE(left >= right, greater || equal); QCOMPARE(right < left, greater); QCOMPARE(right <= left, greater || equal); QCOMPARE(right > left, less); QCOMPARE(right >= left, less || equal); } void TestPath::testPathOperators_data() { QTest::addColumn("left"); QTest::addColumn("right"); QTest::addColumn("equal"); QTest::addColumn("less"); Path a(QStringLiteral("/tmp/a")); Path b(QStringLiteral("/tmp/b")); Path c(QStringLiteral("/tmp/ac")); Path d(QStringLiteral("/d")); Path e(QStringLiteral("/tmp")); Path f(QStringLiteral("/tmp/")); Path invalid; QTest::newRow("a-b") << a << b << false << true; QTest::newRow("a-copy") << a << Path(a) << true << false; QTest::newRow("c-a") << c << a << false << false; QTest::newRow("c-invalid") << c << invalid << false << false; QTest::newRow("c-d") << c << d << false << false; QTest::newRow("e-f") << e << f << true << false; } void TestPath::testPathAddData() { QFETCH(QString, pathToAdd); const QStringList bases = { #ifndef Q_OS_WIN QStringLiteral("/"), QStringLiteral("/foo/bar/asdf/"), QStringLiteral("file:///foo/bar/asdf/"), #else "C:/", "C:/foo/bar/asdf/", "file:///C:/foo/bar/asdf/", #endif QStringLiteral("http://www.asdf.com/foo/bar/asdf/"), }; for (const QString& base : bases) { QUrl baseUrl = QUrl::fromUserInput(base); if (QDir::isRelativePath(pathToAdd)) { baseUrl = resolvedUrl(baseUrl, QUrl(pathToAdd)); } else if (QDir::isRelativePath(pathToAdd) || baseUrl.path() != QLatin1String("/")) { // if pathToAdd == /absolute && baseUrl == "/", below call would lead to an invalid QUrl // with qtbase.git/f62768d046528636789f901ac79e2cfa1843a7b7 baseUrl.setPath(baseUrl.path() + pathToAdd); } else { baseUrl.setPath(pathToAdd); } baseUrl = baseUrl.adjusted(QUrl::NormalizePathSegments); // QUrl::StripTrailingSlash converts file:/// to file: which is not what we want if (baseUrl.path() != QLatin1String("/")) { baseUrl = baseUrl.adjusted(QUrl::StripTrailingSlash); } Path basePath(base); basePath.addPath(pathToAdd); QCOMPARE(basePath.pathOrUrl(), toUrlOrLocalFile(baseUrl)); QCOMPARE(basePath.toUrl(), baseUrl); } } void TestPath::testPathAddData_data() { QTest::addColumn("pathToAdd"); const QStringList paths = QStringList() - << QStringLiteral("file.txt") - << QStringLiteral("path/file.txt") - << QStringLiteral("path//file.txt") - << QStringLiteral("/absolute") - << QStringLiteral("../") - << QStringLiteral("..") - << QStringLiteral("../../../") - << QStringLiteral("./foo") - << QStringLiteral("../relative") - << QStringLiteral("../../relative") - << QStringLiteral("../foo/../bar") - << QStringLiteral("../foo/./bar") - << QStringLiteral("../../../../../../../invalid"); + << QStringLiteral("file.txt") + << QStringLiteral("path/file.txt") + << QStringLiteral("path//file.txt") + << QStringLiteral("/absolute") + << QStringLiteral("../") + << QStringLiteral("..") + << QStringLiteral("../../../") + << QStringLiteral("./foo") + << QStringLiteral("../relative") + << QStringLiteral("../../relative") + << QStringLiteral("../foo/../bar") + << QStringLiteral("../foo/./bar") + << QStringLiteral("../../../../../../../invalid"); for (const QString& path : paths) { QTest::newRow(qstrdup(path.toUtf8().constData())) << path; } } void TestPath::testPathBaseCtor() { QFETCH(QString, base); QFETCH(QString, subPath); QFETCH(QString, expected); const Path basePath(base); const Path path(basePath, subPath); QCOMPARE(path.pathOrUrl(), expected); } void TestPath::testPathBaseCtor_data() { QTest::addColumn("base"); QTest::addColumn("subPath"); QTest::addColumn("expected"); QTest::newRow("empty") << "" << "" << ""; QTest::newRow("empty-relative") << "" << "foo" << ""; #ifndef Q_OS_WIN QTest::newRow("root-empty") << "/" << "" << "/"; QTest::newRow("root-root") << "/" << "/" << "/"; QTest::newRow("root-relative") << "/" << "bar" << "/bar"; QTest::newRow("root-relative-dirty") << "/" << "bar//foo/a/.." << "/bar/foo"; QTest::newRow("path-relative") << "/foo/bar" << "bar/foo" << "/foo/bar/bar/foo"; QTest::newRow("path-absolute") << "/foo/bar" << "/bar/foo" << "/bar/foo"; #else QTest::newRow("root-empty") << "C:/" << "" << "C:"; QTest::newRow("root1-empty") << "C:" << "" << "C:"; QTest::newRow("root-root") << "C:/" << "C:/" << "C:"; QTest::newRow("root-relative") << "C:/" << "bar" << "C:/bar"; QTest::newRow("root1-relative") << "C:" << "bar" << "C:/bar"; QTest::newRow("root-relative-dirty") << "C:/" << "bar//foo/a/.." << "C:/bar/foo"; QTest::newRow("path-relative") << "C:/foo/bar" << "bar/foo" << "C:/foo/bar/bar/foo"; QTest::newRow("path-absolute") << "C:/foo/bar" << "C:/bar/foo" << "C:/bar/foo"; #endif QTest::newRow("remote-path-absolute") << "http://foo.com/foo/bar" << "/bar/foo" << "http://foo.com/bar/foo"; QTest::newRow("remote-path-relative") << "http://foo.com/foo/bar" << "bar/foo" << "http://foo.com/foo/bar/bar/foo"; } // there is no cd() in QUrl, emulate what KUrl did static bool cdQUrl(QUrl& url, const QString& path) { if (path.isEmpty() || !url.isValid()) { return false; } // have to append slash otherwise last segment is treated as a file name and not a directory if (!url.path().endsWith('/')) { url.setPath(url.path() + '/'); } url = url.resolved(QUrl(path)).adjusted(QUrl::RemoveFragment | QUrl::RemoveQuery); return true; } void TestPath::testPathCd() { QFETCH(QString, base); QFETCH(QString, change); Path path = base.isEmpty() ? Path() : Path(base); QUrl url = QUrl::fromUserInput(base); Path changed = path.cd(change); if (cdQUrl(url, change)) { QVERIFY(changed.isValid()); } url = url.adjusted(QUrl::NormalizePathSegments); QCOMPARE(changed.pathOrUrl(), toUrlOrLocalFile(url, QUrl::StripTrailingSlash)); } void TestPath::testPathCd_data() { QTest::addColumn("base"); QTest::addColumn("change"); const QVector bases{ QString(), #ifndef Q_OS_WIN QStringLiteral("/foo"), QStringLiteral("/foo/bar/asdf"), #else "C:/foo", "C:/foo/bar/asdf", #endif - QStringLiteral("http://foo.com/"), QStringLiteral("http://foo.com/foo"), QStringLiteral("http://foo.com/foo/bar/asdf") + QStringLiteral("http://foo.com/"), QStringLiteral("http://foo.com/foo"), QStringLiteral( + "http://foo.com/foo/bar/asdf") }; for (const QString& base : bases) { QTest::newRow(qstrdup(qPrintable(base + "-"))) << base << ""; QTest::newRow(qstrdup(qPrintable(base + "-.."))) << base << ".."; QTest::newRow(qstrdup(qPrintable(base + "-../"))) << base << "../"; QTest::newRow(qstrdup(qPrintable(base + "v../foo"))) << base << "../foo"; QTest::newRow(qstrdup(qPrintable(base + "-."))) << base << "."; QTest::newRow(qstrdup(qPrintable(base + "-./"))) << base << "./"; QTest::newRow(qstrdup(qPrintable(base + "-./foo"))) << base << "./foo"; QTest::newRow(qstrdup(qPrintable(base + "-./foo/bar"))) << base << "./foo/bar"; QTest::newRow(qstrdup(qPrintable(base + "-foo/.."))) << base << "foo/.."; QTest::newRow(qstrdup(qPrintable(base + "-foo/"))) << base << "foo/"; QTest::newRow(qstrdup(qPrintable(base + "-foo/../bar"))) << base << "foo/../bar"; #ifdef Q_OS_WIN - if (!base.startsWith("C:/") ) { + if (!base.startsWith("C:/")) { // only add next rows for remote URLs on Windows #endif QTest::newRow(qstrdup(qPrintable(base + "-/foo"))) << base << "/foo"; QTest::newRow(qstrdup(qPrintable(base + "-/foo/../bar"))) << base << "/foo/../bar"; #ifdef Q_OS_WIN - } + } + #endif } } void TestPath::testHasParent_data() { QTest::addColumn("input"); QTest::addColumn("hasParent"); QTest::newRow("empty") << QString() << false; #ifdef Q_OS_WIN QTest::newRow("\\") << QStringLiteral("\\") << true; // true b/c parent could be e.g. 'C:' #else QTest::newRow("/") << QStringLiteral("/") << false; QTest::newRow("/foo") << QStringLiteral("/foo") << true; QTest::newRow("/foo/bar") << QStringLiteral("/foo/bar") << true; QTest::newRow("//foo/bar") << QStringLiteral("//foo/bar") << true; #endif QTest::newRow("http://foo.bar") << QStringLiteral("http://foo.bar") << false; QTest::newRow("http://foo.bar/") << QStringLiteral("http://foo.bar/") << false; QTest::newRow("http://foo.bar/asdf") << QStringLiteral("http://foo.bar/asdf") << true; QTest::newRow("http://foo.bar/asdf/asdf") << QStringLiteral("http://foo.bar/asdf/asdf") << true; } void TestPath::testHasParent() { QFETCH(QString, input); Path path(input); QTEST(path.hasParent(), "hasParent"); } void TestPath::QUrl_acceptance() { const QUrl baseLocal = QUrl(QStringLiteral("file:///foo.h")); QCOMPARE(baseLocal.isValid(), true); QCOMPARE(baseLocal.isRelative(), false); QCOMPARE(baseLocal, QUrl::fromLocalFile(QStringLiteral("/foo.h"))); QCOMPARE(baseLocal, QUrl::fromUserInput(QStringLiteral("/foo.h"))); QUrl relative(QStringLiteral("bar.h")); QCOMPARE(relative.isRelative(), true); QCOMPARE(baseLocal.resolved(relative), QUrl(QStringLiteral("file:///bar.h"))); QUrl relative2(QStringLiteral("/foo/bar.h")); QCOMPARE(relative2.isRelative(), true); QCOMPARE(baseLocal.resolved(relative2), QUrl(QStringLiteral("file:///foo/bar.h"))); const QUrl baseRemote = QUrl(QStringLiteral("http://foo.org/asdf/foo/asdf")); QCOMPARE(baseRemote.resolved(relative), QUrl(QStringLiteral("http://foo.org/asdf/foo/bar.h"))); } diff --git a/kdevplatform/util/tests/test_path.h b/kdevplatform/util/tests/test_path.h index 4aceb6f34b..37229c9e48 100644 --- a/kdevplatform/util/tests/test_path.h +++ b/kdevplatform/util/tests/test_path.h @@ -1,56 +1,57 @@ /* * This file is part of KDevelop * Copyright 2012 Milian Wolff * * 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 TESTPATH_H #define TESTPATH_H #include class TestPath : public QObject { Q_OBJECT + private Q_SLOTS: void bench_qurl(); void bench_qstringlist(); void bench_path(); void bench_fromLocalPath(); void bench_fromLocalPath_data(); void bench_hash(); void testPath(); void testPath_data(); void testPathInvalid(); void testPathInvalid_data(); void testPathOperators(); void testPathOperators_data(); void testPathAddData(); void testPathAddData_data(); void testPathBaseCtor(); void testPathBaseCtor_data(); void testPathCd(); void testPathCd_data(); void testHasParent_data(); void testHasParent(); void QUrl_acceptance(); }; #endif // TESTPATH_H diff --git a/kdevplatform/util/texteditorhelpers.cpp b/kdevplatform/util/texteditorhelpers.cpp index 984cea8020..9aa7e79f7c 100644 --- a/kdevplatform/util/texteditorhelpers.cpp +++ b/kdevplatform/util/texteditorhelpers.cpp @@ -1,83 +1,85 @@ /* This file is part of the KDE project Copyright 2015 Maciej Cencora 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 "texteditorhelpers.h" #include #include #include namespace KDevelop { namespace { // TODO: this is a hack, but Kate does not provide interface for this int lineHeight(const KTextEditor::View* view, int curLine) { - KTextEditor::Cursor c(curLine, 0); - int currentHeight = view->cursorToCoordinate(c).y(); - c.setLine(curLine + 1); - if (view->cursorToCoordinate(c).y() < 0) { - c.setLine(curLine - 1); - } - return std::abs(view->cursorToCoordinate(c).y() - currentHeight); + KTextEditor::Cursor c(curLine, 0); + int currentHeight = view->cursorToCoordinate(c).y(); + c.setLine(curLine + 1); + if (view->cursorToCoordinate(c).y() < 0) { + c.setLine(curLine - 1); + } + return std::abs(view->cursorToCoordinate(c).y() - currentHeight); } } QRect KTextEditorHelpers::itemBoundingRect(const KTextEditor::View* view, const KTextEditor::Range& itemRange) { - QPoint startPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.start())); - QPoint endPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.end())); - endPoint.ry() += lineHeight(view, itemRange.start().line()); - return QRect(startPoint, endPoint); + QPoint startPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.start())); + QPoint endPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.end())); + endPoint.ry() += lineHeight(view, itemRange.start().line()); + return QRect(startPoint, endPoint); } KTextEditor::Cursor KTextEditorHelpers::extractCursor(const QString& input, int* pathLength) { // ":ll:cc", ":ll" static const QRegularExpression pattern(QStringLiteral(":(\\d+)(?::(\\d+))?$")); // "#Lll", "#nll", "#ll" as e.g. seen with repo web links static const QRegularExpression pattern2(QStringLiteral("#(?:n|L|)(\\d+)$")); auto match = pattern.match(input); if (!match.hasMatch()) { match = pattern2.match(input); } if (!match.hasMatch()) { if (pathLength) *pathLength = input.length(); return KTextEditor::Cursor::invalid(); } int line = match.capturedRef(1).toInt() - 1; // captured(2) for pattern2 will yield null QString, toInt() thus 0, so no need for if-else // don't use an invalid column when the line is valid int column = qMax(0, match.captured(2).toInt() - 1); if (pathLength) *pathLength = match.capturedStart(0); - return {line, column}; + return { + line, column + }; } } diff --git a/kdevplatform/util/widgetcolorizer.cpp b/kdevplatform/util/widgetcolorizer.cpp index 42874334f1..5d247a97d6 100644 --- a/kdevplatform/util/widgetcolorizer.cpp +++ b/kdevplatform/util/widgetcolorizer.cpp @@ -1,81 +1,81 @@ /* * Copyright 2015 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 "widgetcolorizer.h" #include #include #include #include #include #include #include using namespace KDevelop; QColor WidgetColorizer::blendForeground(QColor color, float ratio, const QColor& foreground, const QColor& background) { - if (KColorUtils::luma(foreground) > KColorUtils::luma(background)) { - // for dark color schemes, produce a fitting color first - color = KColorUtils::tint(foreground, color, 0.5); - } - // adapt contrast - return KColorUtils::mix(foreground, color, ratio); + if (KColorUtils::luma(foreground) > KColorUtils::luma(background)) { + // for dark color schemes, produce a fitting color first + color = KColorUtils::tint(foreground, color, 0.5); + } + // adapt contrast + return KColorUtils::mix(foreground, color, ratio); } QColor WidgetColorizer::blendBackground(const QColor& color, float ratio, const QColor& /*foreground*/, const QColor& background) { - // adapt contrast - return KColorUtils::mix(background, color, ratio); + // adapt contrast + return KColorUtils::mix(background, color, ratio); } void WidgetColorizer::drawBranches(const QTreeView* treeView, QPainter* painter, const QRect& rect, const QModelIndex& /*index*/, const QColor& baseColor) { QRect newRect(rect); newRect.setWidth(treeView->indentation()); painter->fillRect(newRect, baseColor); } QColor WidgetColorizer::colorForId(uint id, const QPalette& activePalette, bool forBackground) { const int high = 255; const int low = 100; - auto color = QColor(qAbs(id % (high-low)), - qAbs((id / 50) % (high-low)), - qAbs((id / (50 * 50)) % (high-low))); + auto color = QColor(qAbs(id % (high - low)), + qAbs((id / 50) % (high - low)), + qAbs((id / (50 * 50)) % (high - low))); const auto& foreground = activePalette.foreground().color(); const auto& background = activePalette.background().color(); if (forBackground) { return blendBackground(color, .5, foreground, background); } else { return blendForeground(color, .5, foreground, background); } } bool WidgetColorizer::colorizeByProject() { return KSharedConfig::openConfig()->group("UiSettings").readEntry("ColorizeByProject", true); } diff --git a/kdevplatform/util/widgetcolorizer.h b/kdevplatform/util/widgetcolorizer.h index 8a006c948b..6369225ba5 100644 --- a/kdevplatform/util/widgetcolorizer.h +++ b/kdevplatform/util/widgetcolorizer.h @@ -1,89 +1,88 @@ /* * Copyright 2015 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 . * */ #pragma once #include #include class QColor; class QModelIndex; class QPainter; class QRect; class QPalette; class QTreeView; namespace KDevelop { -namespace WidgetColorizer -{ - /** - * Generate a new color by blending the input @p color with the foreground. - * - * This function also works nicely on dark color schemes, in contrast to - * simply setting an alpha channel value on the color. - * - * @p color Input color to blend. - * @p ratio Ratio to decide how strong to do the blending. - * When set to 0 you get the foreground color as-is, when set to 1 - * you get the input color as-is. - */ - KDEVPLATFORMUTIL_EXPORT QColor blendForeground(QColor color, float ratio, - const QColor& foreground, const QColor& background); +namespace WidgetColorizer { +/** + * Generate a new color by blending the input @p color with the foreground. + * + * This function also works nicely on dark color schemes, in contrast to + * simply setting an alpha channel value on the color. + * + * @p color Input color to blend. + * @p ratio Ratio to decide how strong to do the blending. + * When set to 0 you get the foreground color as-is, when set to 1 + * you get the input color as-is. + */ +KDEVPLATFORMUTIL_EXPORT QColor blendForeground(QColor color, float ratio, + const QColor& foreground, const QColor& background); - /** - * Generate a new color by blending the input @p color with the background. - * - * This function also works nicely on dark color schemes, in contrast to - * simply setting an alpha channel value on the color. - * - * @p color Input color to blend. - * @p ratio Ratio to decide how strong to do the blending. - * When set to 0 you get the background color as-is, when set to 1 - * you get the input color as-is. - */ - KDEVPLATFORMUTIL_EXPORT QColor blendBackground(const QColor& color, float ratio, - const QColor& foreground, const QColor& background); +/** + * Generate a new color by blending the input @p color with the background. + * + * This function also works nicely on dark color schemes, in contrast to + * simply setting an alpha channel value on the color. + * + * @p color Input color to blend. + * @p ratio Ratio to decide how strong to do the blending. + * When set to 0 you get the background color as-is, when set to 1 + * you get the input color as-is. + */ +KDEVPLATFORMUTIL_EXPORT QColor blendBackground(const QColor& color, float ratio, + const QColor& foreground, const QColor& background); - KDEVPLATFORMUTIL_EXPORT void drawBranches(const QTreeView* treeView, QPainter* painter, - const QRect& rect, const QModelIndex& index, const QColor& color); +KDEVPLATFORMUTIL_EXPORT void drawBranches(const QTreeView* treeView, QPainter* painter, + const QRect& rect, const QModelIndex& index, const QColor& color); - /** - * Return a random color fit for the active palette. - * - * @p id An id which can be generated e.g. by qHash. Same ids will return - * the same color. - * @p activePalette The palette to use for generating the color. - * @p background If set to true, a background color will be returned, - * otherwise a foreground color will be returned by default. - */ - KDEVPLATFORMUTIL_EXPORT QColor colorForId(uint id, const QPalette& activePalette, - bool background = false); +/** + * Return a random color fit for the active palette. + * + * @p id An id which can be generated e.g. by qHash. Same ids will return + * the same color. + * @p activePalette The palette to use for generating the color. + * @p background If set to true, a background color will be returned, + * otherwise a foreground color will be returned by default. + */ +KDEVPLATFORMUTIL_EXPORT QColor colorForId(uint id, const QPalette& activePalette, + bool background = false); - /** - * Returns true when the setting is enabled to colorize widgets representing - * files belonging to projects. - */ - KDEVPLATFORMUTIL_EXPORT bool colorizeByProject(); +/** + * Returns true when the setting is enabled to colorize widgets representing + * files belonging to projects. + */ +KDEVPLATFORMUTIL_EXPORT bool colorizeByProject(); } } diff --git a/kdevplatform/util/zoomcontroller.cpp b/kdevplatform/util/zoomcontroller.cpp index 9539fa4f4d..37b31e38c7 100644 --- a/kdevplatform/util/zoomcontroller.cpp +++ b/kdevplatform/util/zoomcontroller.cpp @@ -1,159 +1,157 @@ /* * Copyright 2016 Igor Kushnir * * 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 "zoomcontroller.h" #include #include #include #include #include - namespace { constexpr const char* factorConfigEntryKey() { return "Zoom Factor"; } constexpr double defaultFactor{1}; constexpr double defaultMultiplier{1.1}; double multiplier(double scale) { // Allow finer-grained control over zoom factor compared to defaultMultiplier constexpr double smallMultiplier{1.05}; return std::pow(smallMultiplier, scale); } } // namespace - using KDevelop::ZoomControllerPrivate; using KDevelop::ZoomController; class KDevelop::ZoomControllerPrivate { public: explicit ZoomControllerPrivate(const KConfigGroup& configGroup); void writeConfig(); KConfigGroup m_configGroup; double m_factor{defaultFactor}; }; ZoomControllerPrivate::ZoomControllerPrivate(const KConfigGroup& configGroup) : m_configGroup{configGroup} { m_factor = m_configGroup.readEntry(factorConfigEntryKey(), defaultFactor); } void ZoomControllerPrivate::writeConfig() { m_configGroup.writeEntry(factorConfigEntryKey(), m_factor); m_configGroup.sync(); } ZoomController::ZoomController(const KConfigGroup& configGroup, QObject* parent) : QObject(parent) , d{new ZoomControllerPrivate(configGroup)} { } ZoomController::~ZoomController() = default; double ZoomController::factor() const { return d->m_factor; } void ZoomController::setFactor(double factor) { factor = qBound(0.1, factor, 10.0); if (d->m_factor == factor) { return; } d->m_factor = factor; d->writeConfig(); emit factorChanged(d->m_factor); } void ZoomController::zoomBy(double scale) { setFactor(d->m_factor * multiplier(scale)); } bool ZoomController::handleKeyPressEvent(QKeyEvent* event) { Q_ASSERT(event); const auto requiredModifiers = Qt::ControlModifier; if (!(event->modifiers() & requiredModifiers)) { return false; } // Qt::ShiftModifier is required for the 0 key in some keyboard layouts // (such as Programmer Dvorak). Allow Qt::KeypadModifier to support numpad keys. // Don't allow other modifiers, such as Alt and Meta, to minimize shortcut conflicts. const auto allowedModifiers = Qt::ControlModifier | Qt::ShiftModifier | Qt::KeypadModifier; if (event->modifiers() & ~allowedModifiers) { return false; } if (event->key() == Qt::Key_0) { resetZoom(); event->accept(); return true; } return false; } bool ZoomController::handleWheelEvent(QWheelEvent* event) { Q_ASSERT(event); if (!(event->modifiers() & Qt::ControlModifier)) { return false; } constexpr double minStandardDelta{120}; const QPoint delta = event->angleDelta(); const double scale{(delta.x() + delta.y()) / minStandardDelta}; zoomBy(scale); event->accept(); return true; } void ZoomController::zoomIn() { setFactor(d->m_factor * defaultMultiplier); } void ZoomController::zoomOut() { setFactor(d->m_factor / defaultMultiplier); } void ZoomController::resetZoom() { setFactor(defaultFactor); } diff --git a/kdevplatform/util/zoomcontroller.h b/kdevplatform/util/zoomcontroller.h index a64056bb4d..ea3cbb3f35 100644 --- a/kdevplatform/util/zoomcontroller.h +++ b/kdevplatform/util/zoomcontroller.h @@ -1,118 +1,119 @@ /* * Copyright 2016 Igor Kushnir * * 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 KDEVPLATFORM_ZOOMCONTROLLER_H #define KDEVPLATFORM_ZOOMCONTROLLER_H #include "utilexport.h" #include #include class KConfigGroup; class QKeyEvent; class QWheelEvent; namespace KDevelop { /** * @brief Stores zoom factor; provides common zoom operations and notifications */ class KDEVPLATFORMUTIL_EXPORT ZoomController : public QObject { Q_OBJECT + public: /** * @param configGroup A place to store zoom factor in */ explicit ZoomController(const KConfigGroup& configGroup, QObject* parent = nullptr); ~ZoomController() override; /** * @return Current zoom factor */ double factor() const; /** * @brief Sets current zoom factor to @p factor */ void setFactor(double factor); /** * @brief Changes current zoom factor according to @p scale * * @param scale If equal to 0, factor remains the same; * otherwise, larger scale leads to larger factor. * Can be both positive (zoom in) and negative (zoom out). */ void zoomBy(double scale); /** * @brief Changes current zoom factor in response to designated shortcuts * * @param event Event to be analyzed and possibly accepted * * @return true If the event was accepted as a zoom shortcut */ bool handleKeyPressEvent(QKeyEvent* event); /** * @brief Changes current zoom factor in response to Ctrl+mouse_scroll * * @param event Event to be analyzed and possibly accepted * * @return true If the event was accepted as a zoom scroll */ bool handleWheelEvent(QWheelEvent* event); public Q_SLOTS: /** * @brief Increases current zoom factor by the default multiplier */ void zoomIn(); /** * @brief Decreases current zoom factor by the default multiplier */ void zoomOut(); /** * @brief Sets current zoom factor to the default value (1.0) */ void resetZoom(); Q_SIGNALS: /** * @brief Notifies when current zoom factor is changed * * @param factor Current zoom factor */ void factorChanged(double factor); private: const QScopedPointer d; }; } #endif // KDEVPLATFORM_ZOOMCONTROLLER_H