ClangTools: Improve filtering

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

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

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

View File

@@ -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);