diff --git a/src/plugins/clangtools/CMakeLists.txt b/src/plugins/clangtools/CMakeLists.txt index c45e7afa5a3..88b25830d7e 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 decad6ca1de..9b390ae8302 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 #include @@ -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({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(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(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 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 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 2630c0d5101..375027a3032 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 @@ -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 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 586add71215..9b1076fbda5 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 293da3a8c9a..16d9455eb4f 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 5e850659bf5..1623d2f6b45 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 99df078fdc5..5c41ec87162 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 1e8780a1cc3..07030687d0b 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()) { + 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 ClangToolsDiagnosticModel::diagnostics() const return m_diagnostics; } +QSet ClangToolsDiagnosticModel::allChecks() const +{ + QSet 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(parentItem); auto diagnosticItem = static_cast(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 c6ca1a6e319..ecd3e21e91f 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -124,6 +125,8 @@ public: CheckBoxEnabledRole }; + QSet allChecks() const; + void clear(); void removeWatchedPath(const QString &path); void addWatchedPath(const QString &path); @@ -144,6 +147,12 @@ private: std::unique_ptr m_filesWatcher; }; +class FilterOptions { +public: + QSet checks; +}; +using OptionalFilterOptions = Utils::optional; + 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 5df289b8e86..3a42ef7fe01 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp @@ -32,8 +32,12 @@ #include #include +#include + #include #include +#include +#include #include #include @@ -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 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 d8698f846f2..6e591bb376f 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.h @@ -27,8 +27,6 @@ #include -#include - 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 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 customActions() const override; - bool eventFilter(QObject *watched, QEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - - QAction *m_suppressAction; - std::unique_ptr m_style; - std::unique_ptr 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 8c4211544b2..fc80a1b8299 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 00000000000..6fc19915d85 --- /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 +#include + +#include + +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 +{ + 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 FilterDialog::selectedChecks() const +{ + QSet 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 00000000000..74bbf603e10 --- /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 + +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; + +class FilterDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FilterDialog(const Checks &selectedChecks, QWidget *parent = nullptr); + ~FilterDialog(); + + QSet 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 00000000000..d1f4b773888 --- /dev/null +++ b/src/plugins/clangtools/filterdialog.ui @@ -0,0 +1,99 @@ + + + ClangTools::Internal::FilterDialog + + + + 0 + 0 + 400 + 400 + + + + Filter Diagnostics + + + + + + Select the diagnostics to display. + + + + + + + + + Select All + + + + + + + Select All With Fixits + + + + + + + Clear Selection + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ClangTools::Internal::FilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ClangTools::Internal::FilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +