forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -71,6 +71,9 @@ QtcPlugin {
|
||||
"diagnosticconfigswidget.h",
|
||||
"executableinfo.cpp",
|
||||
"executableinfo.h",
|
||||
"filterdialog.cpp",
|
||||
"filterdialog.h",
|
||||
"filterdialog.ui",
|
||||
"runsettingswidget.cpp",
|
||||
"runsettingswidget.h",
|
||||
"runsettingswidget.ui",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -54,6 +54,7 @@ class Diagnostic
|
||||
public:
|
||||
bool isValid() const;
|
||||
|
||||
QString name;
|
||||
QString description;
|
||||
QString category;
|
||||
QString type;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
141
src/plugins/clangtools/filterdialog.cpp
Normal file
141
src/plugins/clangtools/filterdialog.cpp
Normal 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"
|
||||
63
src/plugins/clangtools/filterdialog.h
Normal file
63
src/plugins/clangtools/filterdialog.h
Normal 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
|
||||
99
src/plugins/clangtools/filterdialog.ui
Normal file
99
src/plugins/clangtools/filterdialog.ui
Normal 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>
|
||||
Reference in New Issue
Block a user