ClangTools: Add "edit as string" feature also for clazy

Fixes: QTCREATORBUG-24846
Change-Id: Id9931c0539ff1e03ab9cac7fb9b76f9694250536
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2024-01-23 15:31:22 +01:00
parent 1cc1d98601
commit 4dba2a2f87
2 changed files with 92 additions and 71 deletions

View File

@@ -116,6 +116,7 @@ public:
QListView *topicsView; QListView *topicsView;
QGroupBox *checksGroupBox; QGroupBox *checksGroupBox;
QCheckBox *enableLowerLevelsCheckBox; QCheckBox *enableLowerLevelsCheckBox;
QPushButton plainTextEditButton;
QTreeView *checksView; QTreeView *checksView;
ClazyChecksWidget() ClazyChecksWidget()
@@ -146,6 +147,7 @@ public:
enableLowerLevelsCheckBox->setToolTip(Tr::tr("When enabling a level explicitly, " enableLowerLevelsCheckBox->setToolTip(Tr::tr("When enabling a level explicitly, "
"also enable lower levels (Clazy semantic).")); "also enable lower levels (Clazy semantic)."));
plainTextEditButton.setText(Tr::tr("Edit Checks as String..."));
checksView = new QTreeView; checksView = new QTreeView;
auto invalidExecutablePage = new QWidget; auto invalidExecutablePage = new QWidget;
@@ -183,6 +185,7 @@ public:
Column { Column {
enableLowerLevelsCheckBox, enableLowerLevelsCheckBox,
&plainTextEditButton,
checksView, checksView,
}.attachTo(checksGroupBox); }.attachTo(checksGroupBox);
@@ -489,8 +492,33 @@ public:
return indexForName(QModelIndex(), name); return indexForName(QModelIndex(), name);
} }
protected: void selectChecks(const QString &checks)
bool m_enabled = true; {
m_root->checked = Qt::Unchecked;
propagateDown(index(0, 0, QModelIndex()));
QStringList checksList = checks.simplified().remove(" ")
.split(",", Qt::SkipEmptyParts);
for (QString &check : checksList) {
Qt::CheckState state;
if (check.startsWith("-")) {
check = check.right(check.length() - 1);
state = Qt::Unchecked;
} else {
state = Qt::Checked;
}
const QModelIndex index = indexForCheck(check);
if (!index.isValid())
continue;
auto *node = static_cast<ProjectExplorer::Tree *>(index.internalPointer());
node->checked = state;
propagateUp(index);
propagateDown(index);
}
}
virtual QString selectedChecks() const = 0;
private: private:
QModelIndex indexForName(const QModelIndex &current, const QString &name) const QModelIndex indexForName(const QModelIndex &current, const QString &name) const
@@ -518,6 +546,10 @@ private:
} }
return {}; return {};
} }
virtual QModelIndex indexForCheck(const QString &check) const { return indexForName(check); }
bool m_enabled = true;
}; };
static void openUrl(QAbstractItemModel *model, const QModelIndex &index) static void openUrl(QAbstractItemModel *model, const QModelIndex &index)
@@ -539,39 +571,13 @@ public:
buildTree(nullptr, m_root, ClangTidyPrefixTree::Node::fromCheckList(supportedChecks)); buildTree(nullptr, m_root, ClangTidyPrefixTree::Node::fromCheckList(supportedChecks));
} }
QString selectedChecks() const QString selectedChecks() const override
{ {
QString checks; QString checks;
collectChecks(m_root, checks); collectChecks(m_root, checks);
return "-*" + checks; return "-*" + checks;
} }
void selectChecks(const QString &checks)
{
m_root->checked = Qt::Unchecked;
propagateDown(index(0, 0, QModelIndex()));
QStringList checksList = checks.simplified().remove(" ")
.split(",", Qt::SkipEmptyParts);
for (QString &check : checksList) {
Qt::CheckState state;
if (check.startsWith("-")) {
check = check.right(check.length() - 1);
state = Qt::Unchecked;
} else {
state = Qt::Checked;
}
const QModelIndex index = indexForCheck(check);
if (!index.isValid())
continue;
auto *node = static_cast<ProjectExplorer::Tree *>(index.internalPointer());
node->checked = state;
propagateUp(index);
propagateDown(index);
}
}
QVariant data(const QModelIndex &fullIndex, int role = Qt::DisplayRole) const final QVariant data(const QModelIndex &fullIndex, int role = Qt::DisplayRole) const final
{ {
if (!fullIndex.isValid() || role == Qt::DecorationRole) if (!fullIndex.isValid() || role == Qt::DecorationRole)
@@ -614,7 +620,7 @@ public:
private: private:
int columnCount(const QModelIndex &) const final { return 3; } int columnCount(const QModelIndex &) const final { return 3; }
QModelIndex indexForCheck(const QString &check) const { QModelIndex indexForCheck(const QString &check) const override {
if (check == "*") if (check == "*")
return index(0, 0, QModelIndex()); return index(0, 0, QModelIndex());
@@ -781,6 +787,8 @@ private:
return ProjectExplorer::SelectableFilesModel::data(index, role); return ProjectExplorer::SelectableFilesModel::data(index, role);
} }
QString selectedChecks() const override { return enabledChecks().join(','); }
static QString levelDescription(int level) static QString levelDescription(int level)
{ {
switch (level) { switch (level) {
@@ -840,6 +848,7 @@ private:
if (root->checked == Qt::Unchecked) if (root->checked == Qt::Unchecked)
return; return;
if (root->checked == Qt::Checked && !root->isDir) { if (root->checked == Qt::Checked && !root->isDir) {
// TODO: Use shortcuts: "level0", "level1", "level2"
checks.append(root->name); checks.append(root->name);
return; return;
} }
@@ -1041,46 +1050,10 @@ DiagnosticConfigsWidget::DiagnosticConfigsWidget(const ClangDiagnosticConfigs &c
}); });
connect(m_tidyChecks->plainTextEditButton, &QPushButton::clicked, this, [this] { connect(m_tidyChecks->plainTextEditButton, &QPushButton::clicked, this, [this] {
const bool readOnly = currentConfig().isReadOnly(); handleChecksAsStringsButtonClicked(m_tidyTreeModel.get());
QDialog dialog;
dialog.setWindowTitle(Tr::tr("Checks"));
const QString initialChecks = m_tidyTreeModel->selectedChecks();
auto textEdit = new QTextEdit(&dialog);
textEdit->setReadOnly(readOnly);
textEdit->setPlainText(initialChecks);
auto buttonsBox = new QDialogButtonBox(QDialogButtonBox::Ok
| (readOnly ? QDialogButtonBox::NoButton
: QDialogButtonBox::Cancel));
using namespace Layouting;
Column {
textEdit,
buttonsBox
}.attachTo(&dialog);
QObject::connect(&dialog, &QDialog::accepted, this, [=, &initialChecks] {
const QString updatedChecks = textEdit->toPlainText();
if (updatedChecks == initialChecks)
return;
disconnectClangTidyItemChanged();
// Also throws away invalid options.
m_tidyTreeModel->selectChecks(updatedChecks);
onClangTidyTreeChanged();
connectClangTidyItemChanged();
}); });
connect(&m_clazyChecks->plainTextEditButton, &QPushButton::clicked, this, [this] {
QObject::connect(buttonsBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); handleChecksAsStringsButtonClicked(m_clazyTreeModel.get());
QObject::connect(buttonsBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.exec();
}); });
connectClangTidyItemChanged(); connectClangTidyItemChanged();
@@ -1153,6 +1126,8 @@ void DiagnosticConfigsWidget::syncClazyWidgets(const ClangDiagnosticConfig &conf
m_clazyChecks->topicsView->clearSelection(); m_clazyChecks->topicsView->clearSelection();
m_clazyChecks->topicsView->setEnabled(enabled); m_clazyChecks->topicsView->setEnabled(enabled);
m_clazyTreeModel->setEnabled(enabled); m_clazyTreeModel->setEnabled(enabled);
m_clazyChecks->plainTextEditButton.setText(enabled ? Tr::tr("Edit Checks as String...")
: Tr::tr("View Checks as String..."));
connectClazyItemChanged(); connectClazyItemChanged();
} }
@@ -1196,6 +1171,50 @@ void DiagnosticConfigsWidget::disconnectClazyItemChanged()
this, &DiagnosticConfigsWidget::onClazyTreeChanged); this, &DiagnosticConfigsWidget::onClazyTreeChanged);
} }
void DiagnosticConfigsWidget::handleChecksAsStringsButtonClicked(BaseChecksTreeModel *model)
{
const bool readOnly = currentConfig().isReadOnly();
QDialog dialog;
dialog.setWindowTitle(Tr::tr("Checks"));
const QString initialChecks = model->selectedChecks();
auto textEdit = new QTextEdit(&dialog);
textEdit->setReadOnly(readOnly);
textEdit->setPlainText(initialChecks);
auto buttonsBox = new QDialogButtonBox(QDialogButtonBox::Ok
| (readOnly ? QDialogButtonBox::NoButton
: QDialogButtonBox::Cancel));
using namespace Layouting;
Column {
textEdit,
buttonsBox
}.attachTo(&dialog);
QObject::connect(&dialog, &QDialog::accepted, this, [=, &initialChecks] {
const QString updatedChecks = textEdit->toPlainText();
if (updatedChecks == initialChecks)
return;
disconnectClangTidyItemChanged();
// Also throws away invalid options.
model->selectChecks(updatedChecks);
onClangTidyTreeChanged();
connectClangTidyItemChanged();
});
QObject::connect(buttonsBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
QObject::connect(buttonsBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.exec();
}
void DiagnosticConfigsWidget::onClangTidyTreeChanged() void DiagnosticConfigsWidget::onClangTidyTreeChanged()
{ {
ClangDiagnosticConfig config = currentConfig(); ClangDiagnosticConfig config = currentConfig();

View File

@@ -15,6 +15,7 @@ class ClazyChecksSortFilterModel;
class ClazyChecksTreeModel; class ClazyChecksTreeModel;
class ClazyChecksWidget; class ClazyChecksWidget;
class Diagnostic; class Diagnostic;
class BaseChecksTreeModel;
class TidyChecksTreeModel; class TidyChecksTreeModel;
class TidyChecksWidget; class TidyChecksWidget;
@@ -50,7 +51,8 @@ private:
void connectClazyItemChanged(); void connectClazyItemChanged();
void disconnectClazyItemChanged(); void disconnectClazyItemChanged();
private: void handleChecksAsStringsButtonClicked(BaseChecksTreeModel *model);
// Clang-Tidy // Clang-Tidy
TidyChecksWidget *m_tidyChecks = nullptr; TidyChecksWidget *m_tidyChecks = nullptr;
std::unique_ptr<TidyChecksTreeModel> m_tidyTreeModel; std::unique_ptr<TidyChecksTreeModel> m_tidyTreeModel;