summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@qt.io>2019-11-04 14:44:36 +0100
committerNikolai Kosjar <nikolai.kosjar@qt.io>2019-12-04 14:07:04 +0000
commit07ec6de8d94e1498407158525baba4be441a791c (patch)
tree78f7fd864dcd7857f86e020c7485871501c928b4
parent92bb42dd36394162a9badecf029234e1c343b5a7 (diff)
downloadqt-creator-07ec6de8d94e1498407158525baba4be441a791c.tar.gz
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>
-rw-r--r--src/plugins/clangtools/CMakeLists.txt1
-rw-r--r--src/plugins/clangtools/clangtool.cpp123
-rw-r--r--src/plugins/clangtools/clangtool.h14
-rw-r--r--src/plugins/clangtools/clangtools.pro3
-rw-r--r--src/plugins/clangtools/clangtools.qbs3
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnostic.cpp6
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnostic.h1
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp48
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticmodel.h16
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticview.cpp60
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticview.h32
-rw-r--r--src/plugins/clangtools/clangtoolslogfilereader.cpp4
-rw-r--r--src/plugins/clangtools/filterdialog.cpp141
-rw-r--r--src/plugins/clangtools/filterdialog.h63
-rw-r--r--src/plugins/clangtools/filterdialog.ui99
15 files changed, 553 insertions, 61 deletions
diff --git a/src/plugins/clangtools/CMakeLists.txt b/src/plugins/clangtools/CMakeLists.txt
index c45e7afa5a..88b25830d7 100644
--- a/src/plugins/clangtools/CMakeLists.txt
+++ b/src/plugins/clangtools/CMakeLists.txt
@@ -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
diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp
index decad6ca1d..9b390ae830 100644
--- a/src/plugins/clangtools/clangtool.cpp
+++ b/src/plugins/clangtools/clangtool.cpp
@@ -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);
diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h
index 2630c0d510..375027a303 100644
--- a/src/plugins/clangtools/clangtool.h
+++ b/src/plugins/clangtools/clangtool.h
@@ -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;
diff --git a/src/plugins/clangtools/clangtools.pro b/src/plugins/clangtools/clangtools.pro
index 586add7121..9b1076fbda 100644
--- a/src/plugins/clangtools/clangtools.pro
+++ b/src/plugins/clangtools/clangtools.pro
@@ -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 \
diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs
index 293da3a8c9..16d9455eb4 100644
--- a/src/plugins/clangtools/clangtools.qbs
+++ b/src/plugins/clangtools/clangtools.qbs
@@ -71,6 +71,9 @@ QtcPlugin {
"diagnosticconfigswidget.h",
"executableinfo.cpp",
"executableinfo.h",
+ "filterdialog.cpp",
+ "filterdialog.h",
+ "filterdialog.ui",
"runsettingswidget.cpp",
"runsettingswidget.h",
"runsettingswidget.ui",
diff --git a/src/plugins/clangtools/clangtoolsdiagnostic.cpp b/src/plugins/clangtools/clangtoolsdiagnostic.cpp
index 5e850659bf..1623d2f6b4 100644
--- a/src/plugins/clangtools/clangtoolsdiagnostic.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnostic.cpp
@@ -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
diff --git a/src/plugins/clangtools/clangtoolsdiagnostic.h b/src/plugins/clangtools/clangtoolsdiagnostic.h
index 99df078fdc..5c41ec8716 100644
--- a/src/plugins/clangtools/clangtoolsdiagnostic.h
+++ b/src/plugins/clangtools/clangtoolsdiagnostic.h
@@ -54,6 +54,7 @@ class Diagnostic
public:
bool isValid() const;
+ QString name;
QString description;
QString category;
QString type;
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
index 1e8780a1cc..07030687d0 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
@@ -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
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h
index c6ca1a6e31..ecd3e21e91 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h
+++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h
@@ -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;
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
index 5df289b8e8..3a42ef7fe0 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp
@@ -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)
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.h b/src/plugins/clangtools/clangtoolsdiagnosticview.h
index d8698f846f..6e591bb376 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticview.h
+++ b/src/plugins/clangtools/clangtoolsdiagnosticview.h
@@ -27,8 +27,6 @@
#include <debugger/analyzer/detailederrorview.h>
-#include <memory>
-
namespace ClangTools {
namespace Internal {
@@ -45,23 +43,35 @@ public:
void scheduleAllFixits(bool schedule);
+signals:
+ void showFilter();
+ void clearFilter();
+ void filterForCurrentKind();
+ void filterOutCurrentKind();
+
private:
- void openEditorForCurrentIndex();
- void suppressCurrentDiagnostic();
+ 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;
};
diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp
index 8c4211544b..fc80a1b829 100644
--- a/src/plugins/clangtools/clangtoolslogfilereader.cpp
+++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp
@@ -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"];
diff --git a/src/plugins/clangtools/filterdialog.cpp b/src/plugins/clangtools/filterdialog.cpp
new file mode 100644
index 0000000000..6fc19915d8
--- /dev/null
+++ b/src/plugins/clangtools/filterdialog.cpp
@@ -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"
diff --git a/src/plugins/clangtools/filterdialog.h b/src/plugins/clangtools/filterdialog.h
new file mode 100644
index 0000000000..74bbf603e1
--- /dev/null
+++ b/src/plugins/clangtools/filterdialog.h
@@ -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
diff --git a/src/plugins/clangtools/filterdialog.ui b/src/plugins/clangtools/filterdialog.ui
new file mode 100644
index 0000000000..d1f4b77388
--- /dev/null
+++ b/src/plugins/clangtools/filterdialog.ui
@@ -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>