diff options
author | hjk <[email protected]> | 2021-03-05 12:48:26 +0100 |
---|---|---|
committer | hjk <[email protected]> | 2021-03-09 12:40:12 +0000 |
commit | 641604429a6e400324e60294f338e4eee5cb4631 (patch) | |
tree | a4c1706608d86a9f6372b18f2b2963539dafcac8 /src/plugins | |
parent | cb55af8e55b47fd5fae1a3bc9fe3ed9a1d2686ba (diff) |
Debugger: Use an aspect for the source path mapping
More in line with the other settings.
Change-Id: I86494f1955120cddda7d2f2eec8ba0fdbfd99585
Reviewed-by: David Schulz <[email protected]>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/debugger/cdb/cdbengine.cpp | 2 | ||||
-rw-r--r-- | src/plugins/debugger/commonoptionspage.cpp | 37 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeractions.cpp | 46 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeractions.h | 38 | ||||
-rw-r--r-- | src/plugins/debugger/debuggercore.h | 4 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerengine.cpp | 6 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerplugin.cpp | 8 | ||||
-rw-r--r-- | src/plugins/debugger/debuggersourcepathmappingwidget.cpp | 167 | ||||
-rw-r--r-- | src/plugins/debugger/debuggersourcepathmappingwidget.h | 60 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 3 | ||||
-rw-r--r-- | src/plugins/debugger/lldb/lldbengine.cpp | 3 |
11 files changed, 197 insertions, 177 deletions
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index faf54a6355c..c530b063c04 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -254,7 +254,7 @@ void CdbEngine::init() } } - const SourcePathMap &sourcePathMap = Internal::globalDebuggerOptions()->sourcePathMap; + const SourcePathMap &sourcePathMap = debuggerSettings()->sourcePathMap.value(); if (!sourcePathMap.isEmpty()) { for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) { m_sourcePathMappings.push_back({QDir::toNativeSeparators(it.key()), diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index 3301f74ec84..80c50b6c729 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -27,31 +27,15 @@ #include "debuggeractions.h" #include "debuggerinternalconstants.h" -#include "debuggercore.h" -#include "debuggersourcepathmappingwidget.h" #include <coreplugin/icore.h> -#include <app/app_version.h> - -#include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> -#include <utils/qtcassert.h> -#include <utils/variablechooser.h> - -#include <QCoreApplication> -#include <QLabel> -#include <QTextStream> using namespace Core; using namespace Debugger::Constants; -using namespace ProjectExplorer; using namespace Utils; -namespace Utils { -} - namespace Debugger { namespace Internal { @@ -68,12 +52,6 @@ class CommonOptionsPageWidget : public Core::IOptionsPageWidget public: explicit CommonOptionsPageWidget() { - m_sourceMappingWidget = new DebuggerSourcePathMappingWidget(this); - - GlobalDebuggerOptions *options = Internal::globalDebuggerOptions(); - SourcePathMap allPathMap = options->sourcePathMap; - m_sourceMappingWidget->setSourcePathMap(allPathMap); - DebuggerSettings &s = *debuggerSettings(); using namespace Layouting; @@ -100,29 +78,18 @@ public: Column { Group { Title("Behavior"), Row { col1, col2, Stretch() } }, - m_sourceMappingWidget, + s.sourcePathMap, Stretch() }.attachTo(this); } - void apply() final; + void apply() final { m_group.apply(); m_group.writeSettings(ICore::settings()); } void finish() final { m_group.finish(); } private: AspectContainer &m_group = debuggerSettings()->page1; - DebuggerSourcePathMappingWidget *m_sourceMappingWidget = nullptr; }; -void CommonOptionsPageWidget::apply() -{ - m_group.apply(); - m_group.writeSettings(ICore::settings()); - - GlobalDebuggerOptions *options = Internal::globalDebuggerOptions(); - options->sourcePathMap = m_sourceMappingWidget->sourcePathMap(); - options->toSettings(); -} - CommonOptionsPage::CommonOptionsPage() { setId(DEBUGGER_COMMON_SETTINGS_ID); diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index 326ee706145..00374f27f46 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -41,55 +41,15 @@ #include <utils/qtcassert.h> #include <QDebug> -#include <QSettings> using namespace Utils; -static const char debugModeSettingsGroupC[] = "DebugMode"; -static const char cdbSettingsGroupC[] = "CDB2"; -static const char sourcePathMappingArrayNameC[] = "SourcePathMappings"; -static const char sourcePathMappingSourceKeyC[] = "Source"; -static const char sourcePathMappingTargetKeyC[] = "Target"; +const char debugModeSettingsGroupC[] = "DebugMode"; +const char cdbSettingsGroupC[] = "CDB2"; namespace Debugger { namespace Internal { -void GlobalDebuggerOptions::toSettings() const -{ - QSettings *s = Core::ICore::settings(); - s->beginWriteArray(sourcePathMappingArrayNameC); - if (!sourcePathMap.isEmpty()) { - const QString sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); - const QString sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); - int i = 0; - for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); - it != cend; - ++it, ++i) { - s->setArrayIndex(i); - s->setValue(sourcePathMappingSourceKey, it.key()); - s->setValue(sourcePathMappingTargetKey, it.value()); - } - } - s->endArray(); -} - -void GlobalDebuggerOptions::fromSettings() -{ - QSettings *s = Core::ICore::settings(); - sourcePathMap.clear(); - if (const int count = s->beginReadArray(sourcePathMappingArrayNameC)) { - const QString sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); - const QString sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); - for (int i = 0; i < count; ++i) { - s->setArrayIndex(i); - const QString key = s->value(sourcePathMappingSourceKey).toString(); - const QString value = s->value(sourcePathMappingTargetKey).toString(); - sourcePathMap.insert(key, value); - } - } - s->endArray(); -} - ////////////////////////////////////////////////////////////////////////// // // DebuggerSettings @@ -604,6 +564,8 @@ DebuggerSettings::DebuggerSettings() page1.registerAspect(&showQmlObjectTree); page1.registerAspect(&stationaryEditorWhileStepping); + page1.registerAspect(&sourcePathMap); + // Page 2 page2.registerAspect(&gdbWatchdogTimeout); page2.registerAspect(&skipKnownFrames); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index e078d353de1..7078f5a9111 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -35,22 +35,36 @@ namespace Debugger { namespace Internal { +class SourcePathMapAspectPrivate; + +// Entries starting with '(' are considered regular expressions in the ElfReader. +// This is useful when there are multiple build machines with different +// path, and the user would like to match anything up to some known +// directory to his local project. +// Syntax: (/home/.*)/KnownSubdir -> /home/my/project using SourcePathMap = QMap<QString, QString>; -// Global debugger options that are not stored as saved action. -class GlobalDebuggerOptions +class SourcePathMapAspect : public Utils::BaseAspect { public: - void toSettings() const; - void fromSettings(); + SourcePathMapAspect(); + ~SourcePathMapAspect() override; + + void fromMap(const QVariantMap &map) override; + void toMap(QVariantMap &map) const override; + + void addToLayout(Utils::LayoutBuilder &builder) override; + + QVariant volatileValue() const override; + void setVolatileValue(const QVariant &val) override; - // Entries starting with '(' are considered regular expressions in the ElfReader. - // This is useful when there are multiple build machines with different - // path, and the user would like to match anything up to some known - // directory to his local project. - // Syntax: (/home/.*)/KnownSubdir -> /home/my/project + void readSettings(const QSettings *settings) override; + void writeSettings(QSettings *settings) const override; - SourcePathMap sourcePathMap; + SourcePathMap value() const; + +private: + SourcePathMapAspectPrivate *d = nullptr; }; class GeneralSettings @@ -85,6 +99,8 @@ public: Utils::BoolAspect showQmlObjectTree; Utils::BoolAspect stationaryEditorWhileStepping; + SourcePathMapAspect sourcePathMap; + // Page 2: GDB Utils::IntegerAspect gdbWatchdogTimeout; Utils::BoolAspect skipKnownFrames; @@ -189,3 +205,5 @@ DebuggerSettings *debuggerSettings(); } // namespace Internal } // namespace Debugger + +Q_DECLARE_METATYPE(Debugger::Internal::SourcePathMap) diff --git a/src/plugins/debugger/debuggercore.h b/src/plugins/debugger/debuggercore.h index 634053acaaa..bd228b2560f 100644 --- a/src/plugins/debugger/debuggercore.h +++ b/src/plugins/debugger/debuggercore.h @@ -42,8 +42,6 @@ namespace Utils { class BaseTreeView; } namespace Debugger { namespace Internal { -class GlobalDebuggerOptions; - enum TestCases { // Gdb @@ -53,8 +51,6 @@ enum TestCases // Some convenience. void openTextEditor(const QString &titlePattern, const QString &contents); -GlobalDebuggerOptions *globalDebuggerOptions(); - bool isTestRun(); QAction *addAction(QMenu *menu, const QString &display, bool on, diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 7c13b96385c..29fc47dc9a0 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -2812,10 +2812,10 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp) bool hasEmbeddedInfo = elfData.indexOf(".debug_info") >= 0; bool hasLink = elfData.indexOf(".gnu_debuglink") >= 0; if (hasEmbeddedInfo) { - const GlobalDebuggerOptions *options = Internal::globalDebuggerOptions(); + const SourcePathMap sourcePathMap = debuggerSettings()->sourcePathMap.value(); QList<QPair<QRegularExpression, QString>> globalRegExpSourceMap; - globalRegExpSourceMap.reserve(options->sourcePathMap.size()); - for (auto it = options->sourcePathMap.begin(), end = options->sourcePathMap.end(); it != end; ++it) { + globalRegExpSourceMap.reserve(sourcePathMap.size()); + for (auto it = sourcePathMap.begin(), end = sourcePathMap.end(); it != end; ++it) { if (it.key().startsWith('(')) { const QString expanded = Utils::globalMacroExpander()->expand(it.value()); if (!expanded.isEmpty()) diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 25f4dc8eee9..7afdd8df24a 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -694,7 +694,6 @@ public: Console m_console; // ensure Debugger Console is created before settings are taken into account DebuggerSettings m_debuggerSettings; QStringList m_arguments; - GlobalDebuggerOptions m_globalDebuggerOptions; DebuggerItemManager m_debuggerItemManager; @@ -1189,8 +1188,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) this, &DebuggerPluginPrivate::updatePresetState); connect(EngineManager::instance(), &EngineManager::currentEngineChanged, this, &DebuggerPluginPrivate::updatePresetState); - - m_globalDebuggerOptions.fromSettings(); } @@ -2054,11 +2051,6 @@ void openTextEditor(const QString &titlePattern0, const QString &contents) QTC_ASSERT(editor, return); } -Internal::GlobalDebuggerOptions *globalDebuggerOptions() -{ - return &dd->m_globalDebuggerOptions; -} - /////////////////////////////////////////////////////////////////////// // // DebuggerPlugin diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index d73fd47dac4..2dc1ea932b1 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -24,33 +24,74 @@ ****************************************************************************/ #include "debuggersourcepathmappingwidget.h" + +#include "debuggeractions.h" #include "debuggerengine.h" #include <utils/buildablehelperlibrary.h> #include <utils/fancylineedit.h> #include <utils/hostosinfo.h> +#include <utils/layoutbuilder.h> #include <utils/pathchooser.h> #include <utils/qtcassert.h> #include <utils/synchronousprocess.h> #include <utils/variablechooser.h> -#include <QStandardItemModel> -#include <QTreeView> -#include <QLineEdit> -#include <QPushButton> -#include <QFormLayout> #include <QFileDialog> +#include <QFormLayout> +#include <QGroupBox> #include <QLabel> +#include <QLineEdit> +#include <QPushButton> +#include <QSettings> +#include <QStandardItemModel> +#include <QTreeView> using namespace Utils; -enum { SourceColumn, TargetColumn, ColumnCount }; - namespace Debugger { namespace Internal { +class SourcePathMappingModel; + +enum { SourceColumn, TargetColumn, ColumnCount }; + using Mapping = QPair<QString, QString>; -using SourcePathMap = DebuggerSourcePathMappingWidget::SourcePathMap; + +class DebuggerSourcePathMappingWidget : public QGroupBox +{ + Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::DebuggerSourcePathMappingWidget) + +public: + DebuggerSourcePathMappingWidget(); + + SourcePathMap sourcePathMap() const; + void setSourcePathMap(const SourcePathMap &); + +private: + void slotAdd(); + void slotAddQt(); + void slotRemove(); + void slotCurrentRowChanged(const QModelIndex &,const QModelIndex &); + void slotEditSourceFieldChanged(); + void slotEditTargetFieldChanged(); + + void resizeColumns(); + void updateEnabled(); + QString editSourceField() const; + QString editTargetField() const; + void setEditFieldMapping(const QPair<QString, QString> &m); + int currentRow() const; + void setCurrentRow(int r); + + SourcePathMappingModel *m_model; + QTreeView *m_treeView; + QPushButton *m_addButton; + QPushButton *m_addQtButton; + QPushButton *m_removeButton; + QLineEdit *m_sourceLineEdit; + Utils::PathChooser *m_targetChooser; +}; // Qt's various build paths for unpatched versions. QStringList qtBuildPaths() @@ -198,8 +239,7 @@ void SourcePathMappingModel::setTarget(int row, const QString &t) Path mappings to be applied using source path substitution in GDB. */ -DebuggerSourcePathMappingWidget::DebuggerSourcePathMappingWidget(QWidget *parent) : - QGroupBox(parent), +DebuggerSourcePathMappingWidget::DebuggerSourcePathMappingWidget() : m_model(new SourcePathMappingModel(this)), m_treeView(new QTreeView(this)), m_addButton(new QPushButton(tr("Add"), this)), @@ -431,11 +471,8 @@ static QString findQtInstallPath(const FilePath &qmakePath) return QString(); } -/* Merge settings for an installed Qt (unless another setting - * is already in the map. */ -DebuggerSourcePathMappingWidget::SourcePathMap - DebuggerSourcePathMappingWidget::mergePlatformQtPath(const DebuggerRunParameters &sp, - const SourcePathMap &in) +/* Merge settings for an installed Qt (unless another setting is already in the map. */ +SourcePathMap mergePlatformQtPath(const DebuggerRunParameters &sp, const SourcePathMap &in) { const FilePath qmake = BuildableHelperLibrary::findSystemQt(sp.inferior.environment); // FIXME: Get this from the profile? @@ -456,5 +493,105 @@ DebuggerSourcePathMappingWidget::SourcePathMap return rc; } +// +// SourcePathMapAspect +// + +class SourcePathMapAspectPrivate +{ +public: + QPointer<DebuggerSourcePathMappingWidget> m_widget; +}; + + +SourcePathMapAspect::SourcePathMapAspect() + : d(new SourcePathMapAspectPrivate) +{ +} + +SourcePathMapAspect::~SourcePathMapAspect() +{ + delete d; +} + +void SourcePathMapAspect::fromMap(const QVariantMap &) +{ + QTC_CHECK(false); // This is only used via read/writeSettings +} + +void SourcePathMapAspect::toMap(QVariantMap &) const +{ + QTC_CHECK(false); +} + +void SourcePathMapAspect::addToLayout(LayoutBuilder &builder) +{ + QTC_CHECK(!d->m_widget); + d->m_widget = createSubWidget<DebuggerSourcePathMappingWidget>(); + d->m_widget->setSourcePathMap(value()); + builder.addRow(d->m_widget.data()); +} + +QVariant SourcePathMapAspect::volatileValue() const +{ + QTC_CHECK(!isAutoApply()); + QTC_ASSERT(d->m_widget, return {}); + return QVariant::fromValue(d->m_widget->sourcePathMap()); +} + +void SourcePathMapAspect::setVolatileValue(const QVariant &val) +{ + QTC_CHECK(!isAutoApply()); + if (d->m_widget) + d->m_widget->setSourcePathMap(val.value<SourcePathMap>()); +} + +const char sourcePathMappingArrayNameC[] = "SourcePathMappings"; +const char sourcePathMappingSourceKeyC[] = "Source"; +const char sourcePathMappingTargetKeyC[] = "Target"; + +SourcePathMap SourcePathMapAspect::value() const +{ + return BaseAspect::value().value<SourcePathMap>(); +} + +void SourcePathMapAspect::writeSettings(QSettings *s) const +{ + const SourcePathMap sourcePathMap = value(); + s->beginWriteArray(sourcePathMappingArrayNameC); + if (!sourcePathMap.isEmpty()) { + const QString sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); + const QString sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); + int i = 0; + for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); + it != cend; + ++it, ++i) { + s->setArrayIndex(i); + s->setValue(sourcePathMappingSourceKey, it.key()); + s->setValue(sourcePathMappingTargetKey, it.value()); + } + } + s->endArray(); +} + +void SourcePathMapAspect::readSettings(const QSettings *settings) +{ + // Eeks. But legitimate, this operates on ICore::settings(); + QSettings *s = const_cast<QSettings *>(settings); + SourcePathMap sourcePathMap; + if (const int count = s->beginReadArray(sourcePathMappingArrayNameC)) { + const QString sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); + const QString sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); + for (int i = 0; i < count; ++i) { + s->setArrayIndex(i); + const QString key = s->value(sourcePathMappingSourceKey).toString(); + const QString value = s->value(sourcePathMappingTargetKey).toString(); + sourcePathMap.insert(key, value); + } + } + s->endArray(); + setValue(QVariant::fromValue(sourcePathMap)); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.h b/src/plugins/debugger/debuggersourcepathmappingwidget.h index 5d6624bb6d1..a1f07a67c5f 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.h +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.h @@ -25,67 +25,17 @@ #pragma once -#include <QGroupBox> #include <QMap> - -QT_BEGIN_NAMESPACE -class QStandardItemModel; -class QTreeView; -class QLineEdit; -class QPushButton; -class QLineEdit; -class QModelIndex; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } +#include <QString> namespace Debugger { namespace Internal { class DebuggerRunParameters; -class SourcePathMappingModel; - -class DebuggerSourcePathMappingWidget : public QGroupBox -{ - Q_OBJECT - -public: - using SourcePathMap = QMap<QString, QString>; - - explicit DebuggerSourcePathMappingWidget(QWidget *parent = nullptr); - - SourcePathMap sourcePathMap() const; - void setSourcePathMap(const SourcePathMap &); - - /* Merge settings for an installed Qt (unless another setting - * is already in the map. */ - static SourcePathMap mergePlatformQtPath(const DebuggerRunParameters &sp, - const SourcePathMap &in); - -private: - void slotAdd(); - void slotAddQt(); - void slotRemove(); - void slotCurrentRowChanged(const QModelIndex &,const QModelIndex &); - void slotEditSourceFieldChanged(); - void slotEditTargetFieldChanged(); - - void resizeColumns(); - void updateEnabled(); - QString editSourceField() const; - QString editTargetField() const; - void setEditFieldMapping(const QPair<QString, QString> &m); - int currentRow() const; - void setCurrentRow(int r); - - SourcePathMappingModel *m_model; - QTreeView *m_treeView; - QPushButton *m_addButton; - QPushButton *m_addQtButton; - QPushButton *m_removeButton; - QLineEdit *m_sourceLineEdit; - Utils::PathChooser *m_targetChooser; -}; +using SourcePathMap = QMap<QString, QString>; +/* Merge settings for an installed Qt (unless another setting + * is already in the map. */ +SourcePathMap mergePlatformQtPath(const DebuggerRunParameters &sp, const SourcePathMap &in); } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index db516a72afe..5f1ea477ea9 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3949,8 +3949,7 @@ void GdbEngine::setupEngine() // Apply source path mappings from global options. //showMessage(_("Assuming Qt is installed at %1").arg(qtInstallPath)); const SourcePathMap sourcePathMap = - DebuggerSourcePathMappingWidget::mergePlatformQtPath(rp, - Internal::globalDebuggerOptions()->sourcePathMap); + mergePlatformQtPath(rp, debuggerSettings()->sourcePathMap.value()); const SourcePathMap completeSourcePathMap = mergeStartParametersSourcePathMap(rp, sourcePathMap); for (auto it = completeSourcePathMap.constBegin(), cend = completeSourcePathMap.constEnd(); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 4c91bcae77e..7618fee0a21 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -267,8 +267,7 @@ void LldbEngine::setupEngine() const DebuggerRunParameters &rp = runParameters(); const SourcePathMap sourcePathMap = - DebuggerSourcePathMappingWidget::mergePlatformQtPath(rp, - Internal::globalDebuggerOptions()->sourcePathMap); + mergePlatformQtPath(rp, debuggerSettings()->sourcePathMap.value()); for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) { |