ClangTools: Improve filtering

Replace the filter line edit in the toolbar by a tool button that pop
ups a dialog. In the dialog, the available checkers can be
selectd/unselected to filter the diagnostic view. Also, the diagnostic
view can be limited to diagnostics with fixits so that these can be
selected and applied as the next step.

For convience, add also some context menu entries to modify the filter
with regard to the current diagnostic.

Change-Id: Ifba3028805840658d72a39516c2b02da9864d4a6
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Nikolai Kosjar
2019-11-04 14:44:36 +01:00
parent 92bb42dd36
commit 07ec6de8d9
15 changed files with 554 additions and 62 deletions

View File

@@ -31,6 +31,7 @@ add_qtc_plugin(ClangTools
clazychecks.ui
diagnosticconfigswidget.cpp diagnosticconfigswidget.h
executableinfo.cpp executableinfo.h
filterdialog.cpp filterdialog.h filterdialog.ui
runsettingswidget.cpp runsettingswidget.h runsettingswidget.ui
settingswidget.cpp settingswidget.h settingswidget.ui
tidychecks.ui

View File

@@ -37,6 +37,7 @@
#include "clangtoolsprojectsettings.h"
#include "clangtoolssettings.h"
#include "clangtoolsutils.h"
#include "filterdialog.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -462,6 +463,14 @@ ClangTool::ClangTool()
m_diagnosticView->setSortingEnabled(true);
m_diagnosticView->sortByColumn(Debugger::DetailedErrorView::DiagnosticColumn,
Qt::AscendingOrder);
connect(m_diagnosticView, &DiagnosticView::showFilter,
this, &ClangTool::filter);
connect(m_diagnosticView, &DiagnosticView::clearFilter,
this, &ClangTool::clearFilter);
connect(m_diagnosticView, &DiagnosticView::filterForCurrentKind,
this, &ClangTool::filterForCurrentKind);
connect(m_diagnosticView, &DiagnosticView::filterOutCurrentKind,
this, &ClangTool::filterOutCurrentKind);
foreach (auto * const model,
QList<QAbstractItemModel *>({m_diagnosticModel, m_diagnosticFilterModel})) {
@@ -526,15 +535,13 @@ ClangTool::ClangTool()
});
m_expandCollapse = action;
// Filter line edit
m_filterLineEdit = new Utils::FancyLineEdit();
m_filterLineEdit->setFiltering(true);
m_filterLineEdit->setPlaceholderText(tr("Filter Diagnostics"));
m_filterLineEdit->setHistoryCompleter("CppTools.ClangTidyClazyIssueFilter", true);
connect(m_filterLineEdit, &Utils::FancyLineEdit::filterChanged, [this](const QString &filter) {
m_diagnosticFilterModel->setFilterRegExp(
QRegExp(filter, Qt::CaseSensitive, QRegExp::WildcardUnix));
});
// Filter button
action = m_showFilter = new QAction(this);
action->setIcon(
Utils::Icon({{":/utils/images/filtericon.png", Utils::Theme::IconsBaseColor}}).icon());
action->setToolTip(tr("Filter Diagnostics"));
action->setCheckable(true);
connect(action, &QAction::triggered, this, &ClangTool::filter);
// Schedule/Unschedule all fixits
m_selectFixitsCheckBox = new SelectFixitsCheckBox;
@@ -542,8 +549,7 @@ ClangTool::ClangTool()
m_selectFixitsCheckBox->setEnabled(false);
m_selectFixitsCheckBox->setTristate(true);
connect(m_selectFixitsCheckBox, &QCheckBox::clicked, this, [this]() {
auto view = static_cast<DiagnosticView *>(m_diagnosticView.data());
view->scheduleAllFixits(m_selectFixitsCheckBox->isChecked());
m_diagnosticView->scheduleAllFixits(m_selectFixitsCheckBox->isChecked());
});
// Apply fixits button
@@ -625,12 +631,12 @@ ClangTool::ClangTool()
m_perspective.addToolbarSeparator();
m_perspective.addToolBarAction(m_loadExported);
m_perspective.addToolBarAction(m_clear);
m_perspective.addToolBarAction(m_expandCollapse);
m_perspective.addToolbarSeparator();
m_perspective.addToolBarAction(m_expandCollapse);
m_perspective.addToolBarAction(m_goBack);
m_perspective.addToolBarAction(m_goNext);
m_perspective.addToolBarWidget(m_filterLineEdit);
m_perspective.addToolbarSeparator();
m_perspective.addToolBarAction(m_showFilter);
m_perspective.addToolBarWidget(m_selectFixitsCheckBox);
m_perspective.addToolBarWidget(m_applyFixitsButton);
@@ -644,11 +650,6 @@ ClangTool::ClangTool()
this, &ClangTool::update);
}
ClangTool::~ClangTool()
{
delete m_diagnosticView;
}
void ClangTool::selectPerspective()
{
m_perspective.select();
@@ -860,6 +861,20 @@ void ClangTool::loadDiagnosticsFromFiles()
setState(State::ImportFinished);
}
DiagnosticItem *ClangTool::diagnosticItem(const QModelIndex &index) const
{
if (!index.isValid())
return {};
TreeItem *item = m_diagnosticModel->itemForIndex(m_diagnosticFilterModel->mapToSource(index));
if (item->level() == 3)
item = item->parent();
if (item->level() == 2)
return static_cast<DiagnosticItem *>(item);
return {};
}
void ClangTool::showOutputPane()
{
ProjectExplorerPlugin::showOutputPaneForRunControl(m_runControl);
@@ -868,11 +883,13 @@ void ClangTool::showOutputPane()
void ClangTool::reset()
{
m_clear->setEnabled(false);
m_showFilter->setEnabled(false);
m_showFilter->setChecked(false);
m_selectFixitsCheckBox->setEnabled(false);
m_applyFixitsButton->setEnabled(false);
m_diagnosticModel->clear();
m_diagnosticFilterModel->resetCounters();
m_diagnosticFilterModel->reset();
m_infoBarWidget->reset();
@@ -956,6 +973,71 @@ void ClangTool::updateForInitialState()
}
}
void ClangTool::setFilterOptions(const OptionalFilterOptions &filterOptions)
{
m_diagnosticFilterModel->setFilterOptions(filterOptions);
const bool isFilterActive = filterOptions
? (filterOptions->checks != m_diagnosticModel->allChecks())
: false;
m_showFilter->setChecked(isFilterActive);
}
void ClangTool::filter()
{
const OptionalFilterOptions filterOptions = m_diagnosticFilterModel->filterOptions();
// Collect available and currently shown checks
QHash<QString, Check> checks;
m_diagnosticModel->forItemsAtLevel<2>([&](DiagnosticItem *item) {
const QString checkName = item->diagnostic().name;
Check &check = checks[checkName];
if (check.name.isEmpty()) {
check.name = checkName;
check.displayName = checkName;
const QString clangDiagPrefix = "clang-diagnostic-";
if (check.displayName.startsWith(clangDiagPrefix))
check.displayName = QString("-W%1").arg(check.name.mid(clangDiagPrefix.size()));
check.count = 1;
check.isShown = filterOptions ? filterOptions->checks.contains(checkName) : true;
check.hasFixit = check.hasFixit || item->diagnostic().hasFixits;
checks.insert(check.name, check);
} else {
++check.count;
}
});
// Show dialog
FilterDialog dialog(checks.values());
if (dialog.exec() == QDialog::Rejected)
return;
// Apply filter
setFilterOptions(FilterOptions{dialog.selectedChecks()});
}
void ClangTool::clearFilter()
{
m_diagnosticFilterModel->setFilterOptions({});
m_showFilter->setChecked(false);
}
void ClangTool::filterForCurrentKind()
{
if (DiagnosticItem *item = diagnosticItem(m_diagnosticView->currentIndex()))
setFilterOptions(FilterOptions{{item->diagnostic().name}});
}
void ClangTool::filterOutCurrentKind()
{
if (DiagnosticItem *item = diagnosticItem(m_diagnosticView->currentIndex())) {
const OptionalFilterOptions filterOpts = m_diagnosticFilterModel->filterOptions();
QSet<QString> checks = filterOpts ? filterOpts->checks : m_diagnosticModel->allChecks();
checks.remove(item->diagnostic().name);
setFilterOptions(FilterOptions{checks});
}
}
void ClangTool::onBuildFailed()
{
m_infoBarWidget->setError(InfoBarWidget::Error,
@@ -1070,8 +1152,6 @@ void ClangTool::onNewDiagnosticsAvailable(const Diagnostics &diagnostics)
{
QTC_ASSERT(m_diagnosticModel, return);
m_diagnosticModel->addDiagnostics(diagnostics);
if (!m_diagnosticFilterModel->filterRegExp().pattern().isEmpty())
m_diagnosticFilterModel->invalidateFilter();
}
void ClangTool::updateForCurrentState()
@@ -1103,6 +1183,7 @@ void ClangTool::updateForCurrentState()
m_clear->setEnabled(!isRunning);
m_expandCollapse->setEnabled(issuesVisible);
m_loadExported->setEnabled(!isRunning);
m_showFilter->setEnabled(issuesFound > 1);
// Diagnostic view
m_diagnosticView->setCursor(isRunning ? Qt::BusyCursor : Qt::ArrowCursor);

View File

@@ -27,6 +27,7 @@
#include "clangfileinfo.h"
#include "clangtoolsdiagnostic.h"
#include "clangtoolsdiagnosticmodel.h"
#include "clangtoolslogfilereader.h"
#include <debugger/debuggermainwindow.h>
@@ -61,6 +62,7 @@ class ClangToolsDiagnosticModel;
class ClangToolRunWorker;
class Diagnostic;
class DiagnosticFilterModel;
class DiagnosticView;
class RunSettings;
class SelectFixitsCheckBox;
@@ -74,7 +76,6 @@ public:
static ClangTool *instance();
ClangTool();
~ClangTool() override;
void selectPerspective();
@@ -125,6 +126,12 @@ private:
void updateForCurrentState();
void updateForInitialState();
void filter();
void clearFilter();
void filterForCurrentKind();
void filterOutCurrentKind();
void setFilterOptions(const OptionalFilterOptions &filterOptions);
void onBuildFailed();
void onStartFailed();
void onStarted();
@@ -133,6 +140,7 @@ private:
void initDiagnosticView();
void loadDiagnosticsFromFiles();
DiagnosticItem *diagnosticItem(const QModelIndex &index) const;
void showOutputPane();
void reset();
@@ -145,7 +153,7 @@ private:
ClangToolRunWorker *m_runWorker = nullptr;
InfoBarWidget *m_infoBarWidget = nullptr;
QPointer<Debugger::DetailedErrorView> m_diagnosticView;
DiagnosticView *m_diagnosticView = nullptr;;
QAction *m_startAction = nullptr;
QAction *m_startOnCurrentFileAction = nullptr;
@@ -155,7 +163,7 @@ private:
DiagnosticFilterModel *m_diagnosticFilterModel = nullptr;
Utils::FancyLineEdit *m_filterLineEdit = nullptr;
QAction *m_showFilter = nullptr;
SelectFixitsCheckBox *m_selectFixitsCheckBox = nullptr;
QToolButton *m_applyFixitsButton = nullptr;

View File

@@ -33,6 +33,7 @@ SOURCES += \
clangtoolsutils.cpp \
diagnosticconfigswidget.cpp \
executableinfo.cpp \
filterdialog.cpp \
runsettingswidget.cpp \
settingswidget.cpp \
@@ -57,6 +58,7 @@ HEADERS += \
clangtoolsutils.h \
diagnosticconfigswidget.h \
executableinfo.h \
filterdialog.h \
runsettingswidget.h \
settingswidget.h \
@@ -64,6 +66,7 @@ FORMS += \
clangselectablefilesdialog.ui \
clangtoolsprojectsettingswidget.ui \
clazychecks.ui \
filterdialog.ui \
runsettingswidget.ui \
settingswidget.ui \
tidychecks.ui \

View File

@@ -71,6 +71,9 @@ QtcPlugin {
"diagnosticconfigswidget.h",
"executableinfo.cpp",
"executableinfo.h",
"filterdialog.cpp",
"filterdialog.h",
"filterdialog.ui",
"runsettingswidget.cpp",
"runsettingswidget.h",
"runsettingswidget.ui",

View File

@@ -49,7 +49,8 @@ bool Diagnostic::isValid() const
quint32 qHash(const Diagnostic &diagnostic)
{
return qHash(diagnostic.description)
return qHash(diagnostic.name)
^ qHash(diagnostic.description)
^ qHash(diagnostic.location.filePath)
^ diagnostic.location.line
^ diagnostic.location.column;
@@ -57,7 +58,8 @@ quint32 qHash(const Diagnostic &diagnostic)
bool operator==(const Diagnostic &lhs, const Diagnostic &rhs)
{
return lhs.description == rhs.description
return lhs.name == rhs.name
&& lhs.description == rhs.description
&& lhs.category == rhs.category
&& lhs.type == rhs.type
&& lhs.location == rhs.location

View File

@@ -54,6 +54,7 @@ class Diagnostic
public:
bool isValid() const;
QString name;
QString description;
QString category;
QString type;

View File

@@ -84,12 +84,14 @@ ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent)
: ClangToolsDiagnosticModelBase(parent)
, m_filesWatcher(std::make_unique<QFileSystemWatcher>())
{
setRootItem(new Utils::StaticTreeItem(QString()));
connectFileWatcher();
}
QDebug operator<<(QDebug debug, const Diagnostic &d)
{
return debug << "category:" << d.category
return debug << "name:" << d.name
<< "category:" << d.category
<< "type:" << d.type
<< "hasFixits:" << d.hasFixits
<< "explainingSteps:" << d.explainingSteps.size()
@@ -120,7 +122,6 @@ void ClangToolsDiagnosticModel::addDiagnostics(const Diagnostics &diagnostics)
if (!filePathItem) {
filePathItem = new FilePathItem(filePath);
rootItem()->appendChild(filePathItem);
addWatchedPath(d.location.filePath);
}
@@ -135,6 +136,16 @@ QSet<Diagnostic> ClangToolsDiagnosticModel::diagnostics() const
return m_diagnostics;
}
QSet<QString> ClangToolsDiagnosticModel::allChecks() const
{
QSet<QString> checks;
forItemsAtLevel<2>([&](DiagnosticItem *item) {
checks.insert(item->diagnostic().name);
});
return checks;
}
void ClangToolsDiagnosticModel::clear()
{
beginResetModel();
@@ -553,7 +564,7 @@ DiagnosticFilterModel::DiagnosticFilterModel(QObject *parent)
setProject(project);
});
connect(this, &QAbstractItemModel::modelReset, this, [this]() {
resetCounters();
reset();
emit fixitCountersChanged(m_fixitsScheduled, m_fixitsScheduable);
});
connect(this, &QAbstractItemModel::rowsInserted,
@@ -595,11 +606,6 @@ void DiagnosticFilterModel::addSuppressedDiagnostic(const SuppressedDiagnostic &
invalidate();
}
void DiagnosticFilterModel::invalidateFilter()
{
QSortFilterProxyModel::invalidateFilter();
}
void DiagnosticFilterModel::onFixitStatusChanged(const QModelIndex &sourceIndex,
FixitStatus oldStatus,
FixitStatus newStatus)
@@ -618,8 +624,10 @@ void DiagnosticFilterModel::onFixitStatusChanged(const QModelIndex &sourceIndex,
emit fixitCountersChanged(m_fixitsScheduled, m_fixitsScheduable);
}
void DiagnosticFilterModel::resetCounters()
void DiagnosticFilterModel::reset()
{
m_filterOptions.reset();
m_fixitsScheduled = 0;
m_fixitsScheduable = 0;
m_diagnostics = 0;
@@ -673,9 +681,13 @@ bool DiagnosticFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s
if (parentItem->level() == 1) {
auto filePathItem = static_cast<FilePathItem *>(parentItem);
auto diagnosticItem = static_cast<DiagnosticItem *>(filePathItem->childAt(sourceRow));
// Is the diagnostic explicitly suppressed?
const Diagnostic &diag = diagnosticItem->diagnostic();
// Filtered out?
if (m_filterOptions && !m_filterOptions->checks.contains(diag.name))
return false;
// Explicitly suppressed?
foreach (const SuppressedDiagnostic &d, m_suppressedDiagnostics) {
if (d.description != diag.description)
continue;
@@ -687,8 +699,7 @@ bool DiagnosticFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s
return false;
}
// Does the diagnostic match the filter?
return diag.description.contains(filterRegExp());
return true;
}
return true; // ExplainingStepItem
@@ -744,5 +755,16 @@ void DiagnosticFilterModel::handleSuppressedDiagnosticsChanged()
invalidate();
}
OptionalFilterOptions DiagnosticFilterModel::filterOptions() const
{
return m_filterOptions;
}
void DiagnosticFilterModel::setFilterOptions(const OptionalFilterOptions &filterOptions)
{
m_filterOptions = filterOptions;
invalidateFilter();
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -31,6 +31,7 @@
#include <debugger/analyzer/detailederrorview.h>
#include <utils/fileutils.h>
#include <utils/optional.h>
#include <utils/treemodel.h>
#include <QFileSystemWatcher>
@@ -124,6 +125,8 @@ public:
CheckBoxEnabledRole
};
QSet<QString> allChecks() const;
void clear();
void removeWatchedPath(const QString &path);
void addWatchedPath(const QString &path);
@@ -144,6 +147,12 @@ private:
std::unique_ptr<QFileSystemWatcher> m_filesWatcher;
};
class FilterOptions {
public:
QSet<QString> checks;
};
using OptionalFilterOptions = Utils::optional<FilterOptions>;
class DiagnosticFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
@@ -155,13 +164,14 @@ public:
void addSuppressedDiagnostic(const SuppressedDiagnostic &diag);
ProjectExplorer::Project *project() const { return m_project; }
void invalidateFilter();
OptionalFilterOptions filterOptions() const;
void setFilterOptions(const OptionalFilterOptions &filterOptions);
void onFixitStatusChanged(const QModelIndex &sourceIndex,
FixitStatus oldStatus,
FixitStatus newStatus);
void resetCounters();
void reset();
int diagnostics() const { return m_diagnostics; }
int fixitsScheduable() const { return m_fixitsScheduable; }
int fixitsScheduled() const { return m_fixitsScheduled; }
@@ -183,6 +193,8 @@ private:
Utils::FilePath m_lastProjectDirectory;
SuppressedDiagnosticsList m_suppressedDiagnostics;
OptionalFilterOptions m_filterOptions;
int m_diagnostics = 0;
int m_fixitsScheduable = 0;
int m_fixitsScheduled = 0;

View File

@@ -32,8 +32,12 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/manhattanstyle.h>
#include <debugger/analyzer/diagnosticlocation.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QApplication>
@@ -95,8 +99,9 @@ class DiagnosticViewDelegate : public QStyledItemDelegate
Q_OBJECT
public:
DiagnosticViewDelegate(DiagnosticViewStyle *style)
: m_style(style)
DiagnosticViewDelegate(DiagnosticViewStyle *style, QObject *parent)
: QStyledItemDelegate(parent)
, m_style(style)
{}
void paint(QPainter *painter,
@@ -119,16 +124,41 @@ private:
DiagnosticView::DiagnosticView(QWidget *parent)
: Debugger::DetailedErrorView(parent)
, m_style(new DiagnosticViewStyle)
, m_delegate(new DiagnosticViewDelegate(m_style.get()))
, m_delegate(new DiagnosticViewDelegate(m_style, this))
{
header()->hide();
const QIcon filterIcon
= Utils::Icon({{":/utils/images/filtericon.png", Utils::Theme::IconsBaseColor}}).icon();
m_showFilter = new QAction(tr("Filter..."), this);
m_showFilter->setIcon(filterIcon);
connect(m_showFilter, &QAction::triggered,
this, &DiagnosticView::showFilter);
m_clearFilter = new QAction(tr("Clear Filter"), this);
m_clearFilter->setIcon(filterIcon);
connect(m_clearFilter, &QAction::triggered,
this, &DiagnosticView::clearFilter);
m_filterForCurrentKind = new QAction(tr("Filter for This Diagnostic Kind"), this);
m_filterForCurrentKind->setIcon(filterIcon);
connect(m_filterForCurrentKind, &QAction::triggered,
this, &DiagnosticView::filterForCurrentKind);
m_filterOutCurrentKind = new QAction(tr("Filter out This Diagnostic Kind"), this);
m_filterOutCurrentKind->setIcon(filterIcon);
connect(m_filterOutCurrentKind, &QAction::triggered,
this, &DiagnosticView::filterOutCurrentKind);
m_separator = new QAction(this);
m_separator->setSeparator(true);
m_suppressAction = new QAction(tr("Suppress This Diagnostic"), this);
connect(m_suppressAction, &QAction::triggered,
this, &DiagnosticView::suppressCurrentDiagnostic);
installEventFilter(this);
setStyle(m_style.get());
setItemDelegate(m_delegate.get());
setStyle(m_style);
setItemDelegate(m_delegate);
}
void DiagnosticView::scheduleAllFixits(bool schedule)
@@ -147,7 +177,10 @@ void DiagnosticView::scheduleAllFixits(bool schedule)
}
}
DiagnosticView::~DiagnosticView() = default;
DiagnosticView::~DiagnosticView()
{
delete m_style;
}
void DiagnosticView::suppressCurrentDiagnostic()
{
@@ -225,7 +258,20 @@ QModelIndex DiagnosticView::getTopLevelIndex(const QModelIndex &index, Direction
QList<QAction *> DiagnosticView::customActions() const
{
return {m_suppressAction};
const QModelIndex currentIndex = selectionModel()->currentIndex();
const bool isDiagnosticItem = currentIndex.parent().isValid();
m_filterForCurrentKind->setEnabled(isDiagnosticItem);
m_filterOutCurrentKind->setEnabled(isDiagnosticItem);
m_suppressAction->setEnabled(isDiagnosticItem);
return {
m_showFilter,
m_clearFilter,
m_filterForCurrentKind,
m_filterOutCurrentKind,
m_separator,
m_suppressAction,
};
}
bool DiagnosticView::eventFilter(QObject *watched, QEvent *event)

View File

@@ -27,8 +27,6 @@
#include <debugger/analyzer/detailederrorview.h>
#include <memory>
namespace ClangTools {
namespace Internal {
@@ -45,23 +43,35 @@ public:
void scheduleAllFixits(bool schedule);
private:
void openEditorForCurrentIndex();
void suppressCurrentDiagnostic();
signals:
void showFilter();
void clearFilter();
void filterForCurrentKind();
void filterOutCurrentKind();
private:
bool eventFilter(QObject *watched, QEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
QList<QAction *> customActions() const override;
void goNext() override;
void goBack() override;
void openEditorForCurrentIndex();
void suppressCurrentDiagnostic();
enum Direction { Next = 1, Previous = -1 };
QModelIndex getIndex(const QModelIndex &index, Direction direction) const;
QModelIndex getTopLevelIndex(const QModelIndex &index, Direction direction) const;
QList<QAction *> customActions() const override;
bool eventFilter(QObject *watched, QEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
QAction *m_suppressAction;
std::unique_ptr<DiagnosticViewStyle> m_style;
std::unique_ptr<DiagnosticViewDelegate> m_delegate;
private:
QAction *m_showFilter = nullptr;
QAction *m_clearFilter = nullptr;
QAction *m_filterForCurrentKind = nullptr;
QAction *m_filterOutCurrentKind = nullptr;
QAction *m_separator = nullptr;
QAction *m_suppressAction = nullptr;
DiagnosticViewStyle *m_style = nullptr;
DiagnosticViewDelegate *m_delegate = nullptr;
bool m_ignoreSetSelectedFixItsCount = false;
};

View File

@@ -432,8 +432,8 @@ Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath,
Diagnostic diag;
diag.location = loc.toDiagnosticLocation();
diag.type = "warning";
diag.description = asString(node["Message"]) + " ["
+ (asString(diagNode["DiagnosticName"])) + "]";
diag.name = asString(diagNode["DiagnosticName"]);
diag.description = asString(node["Message"]) + " [" + diag.name + "]";
// Process fixits/replacements
const YAML::Node &replacementsNode = node["Replacements"];

View File

@@ -0,0 +1,141 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "filterdialog.h"
#include "ui_filterdialog.h"
#include <utils/algorithm.h>
#include <utils/treemodel.h>
#include <QItemSelectionModel>
namespace ClangTools {
namespace Internal {
enum Columns { CheckName, Count };
class CheckItem : public Utils::TreeItem
{
public:
CheckItem(const Check &check) : check(check) {}
QVariant data(int column, int role) const override
{
if (role != Qt::DisplayRole)
return {};
switch (column) {
case Columns::CheckName: return check.displayName;
case Columns::Count: return check.count;
}
return {};
}
Check check;
};
static QItemSelectionModel::SelectionFlags selectionFlags()
{
return QItemSelectionModel::Select | QItemSelectionModel::Rows;
}
class FilterChecksModel : public Utils::TreeModel<Utils::TreeItem, CheckItem>
{
Q_OBJECT
public:
FilterChecksModel(const Checks &checks)
{
Checks sortedChecks = checks;
Utils::sort(sortedChecks, [](const Check &lhs, const Check &rhs) {
return lhs.displayName < rhs.displayName;
});
setHeader({tr("Check"), "#"});
setRootItem(new Utils::StaticTreeItem(QString()));
for (const Check &check : sortedChecks)
m_root->appendChild(new CheckItem(check));
}
};
FilterDialog::FilterDialog(const Checks &checks, QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::FilterDialog)
{
m_ui->setupUi(this);
m_model = new FilterChecksModel(checks);
// View
m_ui->view->setModel(m_model);
m_ui->view->header()->setStretchLastSection(false);
m_ui->view->header()->setSectionResizeMode(Columns::CheckName, QHeaderView::Stretch);
m_ui->view->header()->setSectionResizeMode(Columns::Count, QHeaderView::ResizeToContents);
m_ui->view->setSelectionMode(QAbstractItemView::MultiSelection);
m_ui->view->setSelectionBehavior(QAbstractItemView::SelectRows);
m_ui->view->setIndentation(0);
connect(m_ui->view->selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](){
const bool hasSelection = !m_ui->view->selectionModel()->selectedRows().isEmpty();
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(hasSelection);
});
// Buttons
connect(m_ui->selectNone, &QPushButton::clicked, m_ui->view, &QTreeView::clearSelection);
connect(m_ui->selectAll, &QPushButton::clicked, m_ui->view, &QTreeView::selectAll);
connect(m_ui->selectWithFixits, &QPushButton::clicked, m_ui->view, [this](){
m_ui->view->clearSelection();
m_model->forItemsAtLevel<1>([&](CheckItem *item) {
if (item->check.hasFixit)
m_ui->view->selectionModel()->select(item->index(), selectionFlags());
});
});
m_ui->selectWithFixits->setEnabled(
Utils::anyOf(checks, [](const Check &c) { return c.hasFixit; }));
// Select checks that are not filtered out
m_model->forItemsAtLevel<1>([this](CheckItem *item) {
if (item->check.isShown)
m_ui->view->selectionModel()->select(item->index(), selectionFlags());
});
}
FilterDialog::~FilterDialog()
{
delete m_ui;
}
QSet<QString> FilterDialog::selectedChecks() const
{
QSet<QString> checks;
m_model->forItemsAtLevel<1>([&](CheckItem *item) {
if (m_ui->view->selectionModel()->isSelected(item->index()))
checks << item->check.name;
});
return checks;
}
} // namespace Internal
} // namespace ClangTools
#include "filterdialog.moc"

View File

@@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QDialog>
namespace ClangTools {
namespace Internal {
namespace Ui { class FilterDialog; }
class FilterChecksModel;
class Check {
public:
QString name;
QString displayName;
int count = 0;
bool isShown = false;
bool hasFixit = false;
};
using Checks = QList<Check>;
class FilterDialog : public QDialog
{
Q_OBJECT
public:
explicit FilterDialog(const Checks &selectedChecks, QWidget *parent = nullptr);
~FilterDialog();
QSet<QString> selectedChecks() const;
private:
Ui::FilterDialog *m_ui;
FilterChecksModel *m_model;
};
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ClangTools::Internal::FilterDialog</class>
<widget class="QDialog" name="ClangTools::Internal::FilterDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Filter Diagnostics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Select the diagnostics to display.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="selectAll">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectWithFixits">
<property name="text">
<string>Select All With Fixits</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectNone">
<property name="text">
<string>Clear Selection</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="view"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ClangTools::Internal::FilterDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ClangTools::Internal::FilterDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>