File 0001-ProjectExplorer-standard-custom-parser-for-build-and.patch of Package qt-creator
From 2396068b59c2516753a2e87ec9d25177934f0a6f Mon Sep 17 00:00:00 2001
From: Ralf Habacker <ralf.habacker@freenet.de>
Date: Thu, 16 Jan 2025 16:39:33 +0100
Subject: [PATCH] ProjectExplorer: standard custom parser for build and run
configurations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With this commit, a custom parser can be configured as the default
for build and run configurations of a project.
On the settings page for custom parser, additional columns are
displayed with which the relevant parser can be used as the
default for build and/or run configurations.
In the configurations, appropriately marked parsers are output
with the suffix “(project default)” and activated by default
for opened or newly created projects.
Fixes: QTCREATORBUG-32342
Change-Id: I2027fd3cc88cc4c38b08ac7ba6d921d950d448f9
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
---
.../projectexplorer/buildconfiguration.cpp | 10 +-
src/plugins/projectexplorer/customparser.cpp | 39 ++-
src/plugins/projectexplorer/customparser.h | 7 +-
.../customparserssettingspage.cpp | 283 +++++++++++++++---
4 files changed, 289 insertions(+), 50 deletions(-)
diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp
index 8acfa6d90cd..c2006f236c7 100644
--- a/src/plugins/projectexplorer/buildconfiguration.cpp
+++ b/src/plugins/projectexplorer/buildconfiguration.cpp
@@ -109,14 +109,20 @@ public:
layout->addWidget(pasteStdOutCB);
connect(pasteStdOutCB, &QCheckBox::clicked, bc, &BuildConfiguration::setParseStdOut);
- const auto selectionWidget = new CustomParsersSelectionWidget(this);
+ const auto selectionWidget =
+ new CustomParsersSelectionWidget(CustomParsersSelectionWidget::InBuildConfig, this);
layout->addWidget(selectionWidget);
+ QList<Utils::Id> parsers = bc->customParsers();
+ for (const auto &s : ProjectExplorerPlugin::customParsers()) {
+ if (s.buildDefault && !parsers.contains(s.id))
+ parsers.append(s.id);
+ }
+ selectionWidget->setSelectedParsers(parsers);
connect(selectionWidget, &CustomParsersSelectionWidget::selectionChanged, this,
[selectionWidget, bc] {
bc->setCustomParsers(selectionWidget->selectedParsers());
});
- selectionWidget->setSelectedParsers(bc->customParsers());
}
};
diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp
index ff500afb6a2..27ace59961b 100644
--- a/src/plugins/projectexplorer/customparser.cpp
+++ b/src/plugins/projectexplorer/customparser.cpp
@@ -26,6 +26,7 @@
# include "outputparser_test.h"
#endif
+using namespace ProjectExplorer::Internal;
using namespace Utils;
const char idKey[] = "Id";
@@ -38,6 +39,8 @@ const char fileNameCapKey[] = "FileNameCap";
const char messageCapKey[] = "MessageCap";
const char channelKey[] = "Channel";
const char exampleKey[] = "Example";
+const char buildDefaultKey[] = "BuildDefault";
+const char runDefaultKey[] = "RunDefault";
namespace ProjectExplorer {
@@ -136,7 +139,9 @@ void CustomParserExpression::setFileNameCap(int fileNameCap)
bool CustomParserSettings::operator ==(const CustomParserSettings &other) const
{
return id == other.id && displayName == other.displayName
- && error == other.error && warning == other.warning;
+ && error == other.error && warning == other.warning
+ && buildDefault == other.buildDefault
+ && runDefault == other.runDefault;
}
Store CustomParserSettings::toMap() const
@@ -146,6 +151,8 @@ Store CustomParserSettings::toMap() const
map.insert(nameKey, displayName);
map.insert(errorKey, variantFromStore(error.toMap()));
map.insert(warningKey, variantFromStore(warning.toMap()));
+ map.insert(buildDefaultKey, buildDefault);
+ map.insert(runDefaultKey, runDefault);
return map;
}
@@ -155,6 +162,8 @@ void CustomParserSettings::fromMap(const Store &map)
displayName = map.value(nameKey).toString();
error.fromMap(storeFromVariant(map.value(errorKey)));
warning.fromMap(storeFromVariant(map.value(warningKey)));
+ buildDefault = map.value(buildDefaultKey).toBool();
+ runDefault = map.value(runDefaultKey).toBool();
}
CustomParsersAspect::CustomParsersAspect(Target *target)
@@ -165,7 +174,12 @@ CustomParsersAspect::CustomParsersAspect(Target *target)
setDisplayName(Tr::tr("Custom Output Parsers"));
addDataExtractor(this, &CustomParsersAspect::parsers, &Data::parsers);
setConfigWidgetCreator([this] {
- const auto widget = new Internal::CustomParsersSelectionWidget;
+ const auto widget =
+ new CustomParsersSelectionWidget(CustomParsersSelectionWidget::InRunConfig);
+ for (const auto &s : ProjectExplorerPlugin::customParsers()) {
+ if (s.runDefault && !m_parsers.contains(s.id))
+ m_parsers.append(s.id);
+ }
widget->setSelectedParsers(m_parsers);
connect(widget, &Internal::CustomParsersSelectionWidget::selectionChanged,
this, [this, widget] { m_parsers = widget->selectedParsers(); });
@@ -260,7 +274,8 @@ class SelectionWidget : public QWidget
{
Q_OBJECT
public:
- SelectionWidget(QWidget *parent = nullptr) : QWidget(parent)
+ SelectionWidget(CustomParsersSelectionWidget::Embedded where, QWidget *parent)
+ : QWidget(parent), m_where(where)
{
const auto layout = new QVBoxLayout(this);
const auto explanatoryLabel = new QLabel(Tr::tr(
@@ -302,12 +317,22 @@ private:
{
const auto layout = qobject_cast<QVBoxLayout *>(this->layout());
QTC_ASSERT(layout, return);
- const QList<Utils::Id> parsers = selectedParsers();
+ QList<Utils::Id> parsers = selectedParsers();
for (const auto &p : std::as_const(parserCheckBoxes))
delete p.first;
parserCheckBoxes.clear();
for (const CustomParserSettings &s : ProjectExplorerPlugin::customParsers()) {
const auto checkBox = new QCheckBox(s.displayName, this);
+ bool isSelected = parsers.contains(s.id);
+ bool projectDefault =
+ (m_where == CustomParsersSelectionWidget::InBuildConfig && s.buildDefault)
+ || (m_where == CustomParsersSelectionWidget::InRunConfig && s.runDefault);
+ if (projectDefault) {
+ checkBox->setText(Tr::tr("%1 (Project default)").arg(s.displayName));
+ if (!isSelected)
+ parsers.append(s.id);
+ }
+ checkBox->setCheckState(parsers.contains(s.id) ? Qt::Checked : Qt::Unchecked);
connect(checkBox, &QCheckBox::stateChanged, this, &SelectionWidget::selectionChanged);
parserCheckBoxes.push_back({checkBox, s.id});
layout->addWidget(checkBox);
@@ -316,12 +341,14 @@ private:
}
QList<QPair<QCheckBox *, Utils::Id>> parserCheckBoxes;
+ const CustomParsersSelectionWidget::Embedded m_where;
};
} // anonymous namespace
-CustomParsersSelectionWidget::CustomParsersSelectionWidget(QWidget *parent) : DetailsWidget(parent)
+CustomParsersSelectionWidget::CustomParsersSelectionWidget(Embedded where, QWidget *parent)
+ : DetailsWidget(parent)
{
- const auto widget = new SelectionWidget(this);
+ const auto widget = new SelectionWidget(where, this);
connect(widget, &SelectionWidget::selectionChanged, this, [this] {
updateSummary();
emit selectionChanged();
diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h
index 383bf84eb71..27bd6e136bb 100644
--- a/src/plugins/projectexplorer/customparser.h
+++ b/src/plugins/projectexplorer/customparser.h
@@ -67,6 +67,8 @@ public:
Utils::Id id;
QString displayName;
+ bool buildDefault = false;
+ bool runDefault = false;
CustomParserExpression error;
CustomParserExpression warning;
};
@@ -119,7 +121,9 @@ class CustomParsersSelectionWidget : public Utils::DetailsWidget
{
Q_OBJECT
public:
- CustomParsersSelectionWidget(QWidget *parent = nullptr);
+ enum Embedded { InRunConfig, InBuildConfig };
+
+ CustomParsersSelectionWidget(Embedded where, QWidget *parent = nullptr);
void setSelectedParsers(const QList<Utils::Id> &parsers);
QList<Utils::Id> selectedParsers() const;
@@ -134,4 +138,5 @@ private:
} // namespace Internal
} // namespace ProjectExplorer
+Q_DECLARE_METATYPE(ProjectExplorer::CustomParserSettings);
Q_DECLARE_METATYPE(ProjectExplorer::CustomParserExpression::CustomParserChannel);
diff --git a/src/plugins/projectexplorer/customparserssettingspage.cpp b/src/plugins/projectexplorer/customparserssettingspage.cpp
index 4f3e429c91a..f64b9938893 100644
--- a/src/plugins/projectexplorer/customparserssettingspage.cpp
+++ b/src/plugins/projectexplorer/customparserssettingspage.cpp
@@ -10,25 +10,249 @@
#include "projectexplorertr.h"
#include <utils/algorithm.h>
+#include <utils/itemviews.h>
#include <utils/qtcassert.h>
+#include <QAbstractTableModel>
+#include <QHeaderView>
#include <QHBoxLayout>
#include <QLabel>
#include <QList>
-#include <QListWidget>
#include <QPushButton>
#include <QVBoxLayout>
namespace ProjectExplorer {
namespace Internal {
+class CustomParsersModel : public QAbstractTableModel
+{
+public:
+ explicit CustomParsersModel(QObject *parent = nullptr);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ bool add(const ProjectExplorer::CustomParserSettings& s);
+ bool remove(const QModelIndex &index);
+
+ void apply();
+
+protected:
+ QList<ProjectExplorer::CustomParserSettings> m_customParsers;
+};
+
+CustomParsersModel::CustomParsersModel(QObject *parent)
+ : QAbstractTableModel(parent)
+ , m_customParsers(ProjectExplorerPlugin::customParsers())
+{
+ connect(
+ ProjectExplorerPlugin::instance(),
+ &ProjectExplorerPlugin::customParsersChanged,
+ this,
+ [this] {
+ beginResetModel();
+ m_customParsers = ProjectExplorerPlugin::customParsers();
+ endResetModel();
+ });
+}
+
+QVariant CustomParsersModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ QVariant result;
+ if (orientation == Qt::Vertical)
+ return QVariant();
+
+ switch (role) {
+ case Qt::DisplayRole:
+ switch (section) {
+ case 0:
+ result = Tr::tr("Name");
+ break;
+ case 1:
+ result = Tr::tr("Build default");
+ break;
+ case 2:
+ result = Tr::tr("Run default");
+ break;
+ }
+ break;
+ case Qt::ToolTipRole:
+ switch (section) {
+ case 0:
+ result = Tr::tr("The name of the custom parser");
+ break;
+ case 1:
+ result = Tr::tr("This custom parser is used by default for all build configurations of "
+ "the project");
+ break;
+ case 2:
+ result = Tr::tr(
+ "This custom parser is used by default for all run configurations of the project");
+ break;
+ }
+ }
+ return result;
+}
+
+int CustomParsersModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return 3;
+}
+
+int CustomParsersModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_customParsers.size();
+}
+
+QVariant CustomParsersModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ const CustomParserSettings &s = m_customParsers[index.row()];
+
+ switch (role) {
+ case Qt::CheckStateRole:
+ switch (index.column()) {
+ case 1:
+ return s.buildDefault ? Qt::Checked : Qt::Unchecked;
+ case 2:
+ return s.runDefault ? Qt::Checked : Qt::Unchecked;
+ }
+ break;
+
+ case Qt::DisplayRole:
+ switch (index.column()) {
+ case 0:
+ return s.displayName;
+ }
+ break;
+
+ case Qt::EditRole:
+ switch (index.column()) {
+ case 1:
+ return s.buildDefault;
+ case 2:
+ return s.runDefault;
+ }
+ break;
+
+ case Qt::TextAlignmentRole:
+ switch (index.column()) {
+ case 1:
+ case 2:
+ return Qt::AlignCenter;
+ }
+ break;
+
+ case Qt::UserRole:
+ return QVariant::fromValue<CustomParserSettings>(s);
+ }
+
+ return QVariant();
+}
+
+bool CustomParsersModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+
+ if (index.row() >= m_customParsers.size())
+ return false;
+
+ CustomParserSettings &s = m_customParsers[index.row()];
+
+ bool result = true;
+ switch (role) {
+ case Qt::EditRole:
+ switch (index.column()) {
+ case 0:
+ s.displayName = value.toString();
+ emit dataChanged(index, index);
+ break;
+ }
+ break;
+
+ case Qt::CheckStateRole:
+ switch (index.column()) {
+ case 1:
+ s.buildDefault = value == Qt::Checked;
+ emit dataChanged(index, index);
+ break;
+ case 2:
+ s.runDefault = value == Qt::Checked;
+ emit dataChanged(index, index);
+ break;
+ }
+ break;
+
+ case Qt::UserRole:
+ if (value.canConvert<CustomParserSettings>()) {
+ s = value.value<CustomParserSettings>();
+ emit dataChanged(index, index);
+ } else
+ result = false;
+ }
+ return result;
+}
+
+Qt::ItemFlags CustomParsersModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ Qt::ItemFlags flags = Qt::ItemIsEnabled;
+
+ if (index.column() > 0)
+ flags |= Qt::ItemIsUserCheckable;
+ else
+ flags |= Qt::ItemIsEditable;
+
+ return flags;
+}
+
+bool CustomParsersModel::add(const CustomParserSettings &s)
+{
+ beginInsertRows(index(-1, -1), m_customParsers.size(), m_customParsers.size());
+ m_customParsers.append(s);
+ endInsertRows();
+ return true;
+}
+
+bool CustomParsersModel::remove(const QModelIndex &index)
+{
+ beginRemoveRows(index.parent(), index.row(), index.row());
+ m_customParsers.removeAt(index.row());
+ endRemoveRows();
+ return true;
+}
+
+void CustomParsersModel::apply()
+{
+ ProjectExplorerPlugin::setCustomParsers(m_customParsers);
+}
+
class CustomParsersSettingsWidget final : public Core::IOptionsPageWidget
{
public:
CustomParsersSettingsWidget()
{
- m_customParsers = ProjectExplorerPlugin::customParsers();
- resetListView();
+ Utils::TreeView *parserView = new Utils::TreeView(this);
+ parserView->setModel(&m_model);
+ parserView->setSelectionMode(QAbstractItemView::SingleSelection);
+ parserView->setSelectionBehavior(QAbstractItemView::SelectRows);
const auto mainLayout = new QVBoxLayout(this);
const auto widgetLayout = new QHBoxLayout;
@@ -37,7 +261,7 @@ public:
"Custom output parsers defined here can be enabled individually "
"in the project's build or run settings."));
mainLayout->addWidget(hintLabel);
- widgetLayout->addWidget(&m_parserListView);
+ widgetLayout->addWidget(parserView);
const auto buttonLayout = new QVBoxLayout;
widgetLayout->addLayout(buttonLayout);
const auto addButton = new QPushButton(Tr::tr("Add..."));
@@ -56,61 +280,38 @@ public:
CustomParserSettings newParser = dlg.settings();
newParser.id = Utils::Id::generate();
newParser.displayName = Tr::tr("New Parser");
- m_customParsers << newParser;
- resetListView();
+ m_model.add(newParser);
});
- connect(removeButton, &QPushButton::clicked, this, [this] {
- const QList<QListWidgetItem *> sel = m_parserListView.selectedItems();
- QTC_ASSERT(sel.size() == 1, return);
- m_customParsers.removeAt(m_parserListView.row(sel.first()));
- delete sel.first();
+
+ connect(removeButton, &QPushButton::clicked, this, [this, parserView] {
+ m_model.remove(parserView->currentIndex());
});
- connect(editButton, &QPushButton::clicked, this, [this] {
- const QList<QListWidgetItem *> sel = m_parserListView.selectedItems();
- QTC_ASSERT(sel.size() == 1, return);
- CustomParserSettings &s = m_customParsers[m_parserListView.row(sel.first())];
+
+ connect(editButton, &QPushButton::clicked, this, [this, parserView] {
+ CustomParserSettings s = m_model.data(parserView->currentIndex(), Qt::UserRole).value<CustomParserSettings>();
CustomParserConfigDialog dlg(this);
dlg.setSettings(s);
if (dlg.exec() != QDialog::Accepted)
return;
s.error = dlg.settings().error;
s.warning = dlg.settings().warning;
+ m_model.setData(parserView->currentIndex(), QVariant::fromValue<CustomParserSettings>(s), Qt::UserRole);
});
- connect(&m_parserListView, &QListWidget::itemChanged, this, [this](QListWidgetItem *item) {
- m_customParsers[m_parserListView.row(item)].displayName = item->text();
- resetListView();
- });
-
- const auto updateButtons = [this, removeButton, editButton] {
- const bool enable = !m_parserListView.selectedItems().isEmpty();
+ const auto updateButtons = [editButton, parserView, removeButton] {
+ const bool enable = parserView->currentIndex().isValid();
removeButton->setEnabled(enable);
editButton->setEnabled(enable);
};
updateButtons();
- connect(m_parserListView.selectionModel(), &QItemSelectionModel::selectionChanged,
- updateButtons);
+ connect(parserView->selectionModel(), &QItemSelectionModel::selectionChanged,
+ updateButtons);
}
private:
- void apply() override { ProjectExplorerPlugin::setCustomParsers(m_customParsers); }
-
- void resetListView()
- {
- m_parserListView.clear();
- Utils::sort(m_customParsers,
- [](const CustomParserSettings &s1, const CustomParserSettings &s2) {
- return s1.displayName < s2.displayName;
- });
- for (const CustomParserSettings &s : std::as_const(m_customParsers)) {
- const auto item = new QListWidgetItem(s.displayName);
- item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
- m_parserListView.addItem(item);
- }
- }
+ void apply() override { m_model.apply(); }
- QListWidget m_parserListView;
- QList<CustomParserSettings> m_customParsers;
+ CustomParsersModel m_model;
};
CustomParsersSettingsPage::CustomParsersSettingsPage()
--
2.48.1