ClangTools: Add diagnostic mark class

These marks can now be disabled.
This greys out the annotation color and the icon of the mark.

Change-Id: I5af4591db4baaaef55c986252f77d5d977427b56
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2020-07-21 13:11:01 +02:00
parent 4314795992
commit 7f562c4d33
11 changed files with 258 additions and 129 deletions

View File

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

View File

@@ -29,6 +29,7 @@ SOURCES += \
clangtoolssettings.cpp \ clangtoolssettings.cpp \
clangtoolsutils.cpp \ clangtoolsutils.cpp \
diagnosticconfigswidget.cpp \ diagnosticconfigswidget.cpp \
diagnosticmark.cpp \
executableinfo.cpp \ executableinfo.cpp \
filterdialog.cpp \ filterdialog.cpp \
runsettingswidget.cpp \ runsettingswidget.cpp \
@@ -54,6 +55,7 @@ HEADERS += \
clangtoolssettings.h \ clangtoolssettings.h \
clangtoolsutils.h \ clangtoolsutils.h \
diagnosticconfigswidget.h \ diagnosticconfigswidget.h \
diagnosticmark.h \
executableinfo.h \ executableinfo.h \
filterdialog.h \ filterdialog.h \
runsettingswidget.h \ runsettingswidget.h \

View File

@@ -66,6 +66,8 @@ QtcPlugin {
"clazychecks.ui", "clazychecks.ui",
"diagnosticconfigswidget.cpp", "diagnosticconfigswidget.cpp",
"diagnosticconfigswidget.h", "diagnosticconfigswidget.h",
"diagnosticmark.cpp",
"diagnosticmark.h",
"executableinfo.cpp", "executableinfo.cpp",
"executableinfo.h", "executableinfo.h",
"filterdialog.cpp", "filterdialog.cpp",

View File

@@ -25,6 +25,8 @@
#include "clangtoolsdiagnostic.h" #include "clangtoolsdiagnostic.h"
#include <utils/utilsicons.h>
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
@@ -47,6 +49,19 @@ bool Diagnostic::isValid() const
return !description.isEmpty(); return !description.isEmpty();
} }
QIcon Diagnostic::icon() const
{
if (type == "warning")
return Utils::Icons::CODEMODEL_WARNING.icon();
if (type == "error" || type == "fatal")
return Utils::Icons::CODEMODEL_ERROR.icon();
if (type == "note")
return Utils::Icons::INFO.icon();
if (type == "fix-it")
return Utils::Icons::CODEMODEL_FIXIT.icon();
return {};
}
quint32 qHash(const Diagnostic &diagnostic) quint32 qHash(const Diagnostic &diagnostic)
{ {
return qHash(diagnostic.name) return qHash(diagnostic.name)

View File

@@ -53,6 +53,7 @@ class Diagnostic
{ {
public: public:
bool isValid() const; bool isValid() const;
QIcon icon() const;
QString name; QString name;
QString description; QString description;

View File

@@ -28,6 +28,7 @@
#include "clangtoolsdiagnosticview.h" #include "clangtoolsdiagnosticview.h"
#include "clangtoolsprojectsettings.h" #include "clangtoolsprojectsettings.h"
#include "clangtoolsutils.h" #include "clangtoolsutils.h"
#include "diagnosticmark.h"
#include <coreplugin/fileiconprovider.h> #include <coreplugin/fileiconprovider.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
@@ -199,95 +200,11 @@ void ClangToolsDiagnosticModel::addWatchedPath(const QString &path)
m_filesWatcher->addPath(path); m_filesWatcher->addPath(path);
} }
static QString fixitStatus(FixitStatus status)
{
switch (status) {
case FixitStatus::NotAvailable:
return ClangToolsDiagnosticModel::tr("No Fixits");
case FixitStatus::NotScheduled:
return ClangToolsDiagnosticModel::tr("Not Scheduled");
case FixitStatus::Invalidated:
return ClangToolsDiagnosticModel::tr("Invalidated");
case FixitStatus::Scheduled:
return ClangToolsDiagnosticModel::tr("Scheduled");
case FixitStatus::FailedToApply:
return ClangToolsDiagnosticModel::tr("Failed to Apply");
case FixitStatus::Applied:
return ClangToolsDiagnosticModel::tr("Applied");
}
return QString();
}
static QString lineColumnString(const Debugger::DiagnosticLocation &location) static QString lineColumnString(const Debugger::DiagnosticLocation &location)
{ {
return QString("%1:%2").arg(QString::number(location.line), QString::number(location.column)); return QString("%1:%2").arg(QString::number(location.line), QString::number(location.column));
} }
static QString createDiagnosticToolTipString(
const Diagnostic &diagnostic,
Utils::optional<FixitStatus> fixItStatus = Utils::nullopt,
bool showSteps = true)
{
using StringPair = QPair<QString, QString>;
QList<StringPair> lines;
if (!diagnostic.category.isEmpty()) {
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Category:"),
diagnostic.category.toHtmlEscaped());
}
if (!diagnostic.type.isEmpty()) {
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Type:"),
diagnostic.type.toHtmlEscaped());
}
if (!diagnostic.description.isEmpty()) {
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Description:"),
diagnostic.description.toHtmlEscaped());
}
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Location:"),
createFullLocationString(diagnostic.location));
if (fixItStatus.has_value()) {
lines << qMakePair(QCoreApplication::translate("ClangTools::Diagnostic", "Fixit status:"),
fixitStatus(fixItStatus.value()));
}
if (showSteps && !diagnostic.explainingSteps.isEmpty()) {
StringPair steps;
steps.first = QCoreApplication::translate("ClangTools::Diagnostic", "Steps:");
for (const ExplainingStep &step : diagnostic.explainingSteps) {
if (!steps.second.isEmpty())
steps.second += "<br>";
steps.second += QString("%1:%2: %3")
.arg(step.location.filePath,
lineColumnString(step.location),
step.message);
}
lines << steps;
}
QString html = QLatin1String("<html>"
"<head>"
"<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n"
"<body><dl>");
foreach (const StringPair &pair, lines) {
html += QLatin1String("<dt>");
html += pair.first;
html += QLatin1String("</dt><dd>");
html += pair.second;
html += QLatin1String("</dd>\n");
}
html += QLatin1String("</dl></body></html>");
return html;
}
static QString createExplainingStepToolTipString(const ExplainingStep &step) static QString createExplainingStepToolTipString(const ExplainingStep &step)
{ {
using StringPair = QPair<QString, QString>; using StringPair = QPair<QString, QString>;
@@ -365,47 +282,17 @@ static QString fullText(const Diagnostic &diagnostic)
return text; return text;
} }
static Utils::optional<QIcon> iconForType(const QString &type)
{
if (type == "warning")
return Utils::Icons::CODEMODEL_WARNING.icon();
if (type == "error" || type == "fatal")
return Utils::Icons::CODEMODEL_ERROR.icon();
if (type == "note")
return Utils::Icons::INFO.icon();
if (type == "fix-it")
return Utils::Icons::CODEMODEL_FIXIT.icon();
return Utils::nullopt;
}
static TextEditor::TextMark *generateDiagnosticTextMark(const Diagnostic &diag)
{
auto mark = new TextEditor::TextMark(Utils::FilePath::fromString(diag.location.filePath),
diag.location.line,
Utils::Id("ClangTool.DiagnosticMark"));
if (diag.type == "error" || diag.type == "fatal")
mark->setColor(Utils::Theme::CodeModel_Error_TextMarkColor);
else
mark->setColor(Utils::Theme::CodeModel_Warning_TextMarkColor);
mark->setPriority(TextEditor::TextMark::HighPriority);
mark->setIcon(iconForType(diag.type).value_or(Utils::Icons::CODEMODEL_WARNING.icon()));
mark->setToolTip(createDiagnosticToolTipString(diag));
mark->setLineAnnotation(diag.description);
return mark;
}
DiagnosticItem::DiagnosticItem(const Diagnostic &diag, DiagnosticItem::DiagnosticItem(const Diagnostic &diag,
const OnFixitStatusChanged &onFixitStatusChanged, const OnFixitStatusChanged &onFixitStatusChanged,
ClangToolsDiagnosticModel *parent) ClangToolsDiagnosticModel *parent)
: m_diagnostic(diag) : m_diagnostic(diag)
, m_onFixitStatusChanged(onFixitStatusChanged) , m_onFixitStatusChanged(onFixitStatusChanged)
, m_parentModel(parent) , m_parentModel(parent)
, m_mark(new DiagnosticMark(diag))
{ {
if (diag.hasFixits) if (diag.hasFixits)
m_fixitStatus = FixitStatus::NotScheduled; m_fixitStatus = FixitStatus::NotScheduled;
m_mark = generateDiagnosticTextMark(diag);
// Don't show explaining steps if they add no information. // Don't show explaining steps if they add no information.
if (diag.explainingSteps.count() == 1) { if (diag.explainingSteps.count() == 1) {
const ExplainingStep &step = diag.explainingSteps.first(); const ExplainingStep &step = diag.explainingSteps.first();
@@ -434,10 +321,10 @@ Qt::ItemFlags DiagnosticItem::flags(int column) const
return itemFlags; return itemFlags;
} }
static QVariant iconData(const QString &type) static QVariant iconData(const Diagnostic &diagnostic)
{ {
Utils::optional<QIcon> icon = iconForType(type); QIcon icon = diagnostic.icon();
return icon.has_value() ? QVariant(*icon) : QVariant(); return icon.isNull() ? QVariant() : QVariant(icon);
} }
QVariant DiagnosticItem::data(int column, int role) const QVariant DiagnosticItem::data(int column, int role) const
@@ -485,7 +372,7 @@ QVariant DiagnosticItem::data(int column, int role) const
case Qt::ToolTipRole: case Qt::ToolTipRole:
return createDiagnosticToolTipString(m_diagnostic, m_fixitStatus, false); return createDiagnosticToolTipString(m_diagnostic, m_fixitStatus, false);
case Qt::DecorationRole: case Qt::DecorationRole:
return iconData(m_diagnostic.type); return iconData(m_diagnostic);
default: default:
return QVariant(); return QVariant();
} }

View File

@@ -28,6 +28,7 @@
#include "clangfixitsrefactoringchanges.h" #include "clangfixitsrefactoringchanges.h"
#include "clangtoolsdiagnostic.h" #include "clangtoolsdiagnostic.h"
#include "clangtoolsprojectsettings.h" #include "clangtoolsprojectsettings.h"
#include "clangtoolsutils.h"
#include <debugger/analyzer/detailederrorview.h> #include <debugger/analyzer/detailederrorview.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
@@ -48,15 +49,6 @@ namespace ProjectExplorer { class Project; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
enum class FixitStatus {
NotAvailable,
NotScheduled,
Scheduled,
Applied,
FailedToApply,
Invalidated,
};
class ClangToolsDiagnosticModel; class ClangToolsDiagnosticModel;
class FilePathItem : public Utils::TreeItem class FilePathItem : public Utils::TreeItem

View File

@@ -42,7 +42,6 @@
#include <QFileInfo> #include <QFileInfo>
#include <cpptools/clangdiagnosticconfigsmodel.h>
#include <cpptools/clangdiagnosticconfigsmodel.h> #include <cpptools/clangdiagnosticconfigsmodel.h>
using namespace CppTools; using namespace CppTools;
@@ -50,6 +49,96 @@ using namespace CppTools;
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
static QString lineColumnString(const Debugger::DiagnosticLocation &location)
{
return QString("%1:%2").arg(QString::number(location.line), QString::number(location.column));
}
static QString fixitStatus(FixitStatus status)
{
switch (status) {
case FixitStatus::NotAvailable:
return QCoreApplication::translate("ClangToolsDiagnosticModel", "No Fixits");
case FixitStatus::NotScheduled:
return QCoreApplication::translate("ClangToolsDiagnosticModel", "Not Scheduled");
case FixitStatus::Invalidated:
return QCoreApplication::translate("ClangToolsDiagnosticModel", "Invalidated");
case FixitStatus::Scheduled:
return QCoreApplication::translate("ClangToolsDiagnosticModel", "Scheduled");
case FixitStatus::FailedToApply:
return QCoreApplication::translate("ClangToolsDiagnosticModel", "Failed to Apply");
case FixitStatus::Applied:
return QCoreApplication::translate("ClangToolsDiagnosticModel", "Applied");
}
return QString();
}
QString createDiagnosticToolTipString(
const Diagnostic &diagnostic,
Utils::optional<FixitStatus> status,
bool showSteps)
{
using StringPair = QPair<QString, QString>;
QList<StringPair> lines;
if (!diagnostic.category.isEmpty()) {
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Category:"),
diagnostic.category.toHtmlEscaped());
}
if (!diagnostic.type.isEmpty()) {
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Type:"),
diagnostic.type.toHtmlEscaped());
}
if (!diagnostic.description.isEmpty()) {
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Description:"),
diagnostic.description.toHtmlEscaped());
}
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Location:"),
createFullLocationString(diagnostic.location));
if (status) {
lines << qMakePair(QCoreApplication::translate("ClangTools::Diagnostic", "Fixit status:"),
fixitStatus(*status));
}
if (showSteps && !diagnostic.explainingSteps.isEmpty()) {
StringPair steps;
steps.first = QCoreApplication::translate("ClangTools::Diagnostic", "Steps:");
for (const ExplainingStep &step : diagnostic.explainingSteps) {
if (!steps.second.isEmpty())
steps.second += "<br>";
steps.second += QString("%1:%2: %3")
.arg(step.location.filePath,
lineColumnString(step.location),
step.message);
}
lines << steps;
}
QString html = QLatin1String("<html>"
"<head>"
"<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>"
"</head>\n"
"<body><dl>");
for (const StringPair &pair : qAsConst(lines)) {
html += QLatin1String("<dt>");
html += pair.first;
html += QLatin1String("</dt><dd>");
html += pair.second;
html += QLatin1String("</dd>\n");
}
html += QLatin1String("</dl></body></html>");
return html;
}
QString createFullLocationString(const Debugger::DiagnosticLocation &location) QString createFullLocationString(const Debugger::DiagnosticLocation &location)
{ {
return location.filePath + QLatin1Char(':') + QString::number(location.line) return location.filePath + QLatin1Char(':') + QString::number(location.line)

View File

@@ -27,6 +27,8 @@
#include <cpptools/clangdiagnosticconfig.h> #include <cpptools/clangdiagnosticconfig.h>
#include <utils/optional.h>
#include <QtGlobal> #include <QtGlobal>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -39,6 +41,22 @@ namespace Debugger { class DiagnosticLocation; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
class Diagnostic;
enum class FixitStatus {
NotAvailable,
NotScheduled,
Scheduled,
Applied,
FailedToApply,
Invalidated,
};
QString createDiagnosticToolTipString(
const Diagnostic &diagnostic,
Utils::optional<FixitStatus> status = Utils::nullopt,
bool showSteps = true);
QString createFullLocationString(const Debugger::DiagnosticLocation &location); QString createFullLocationString(const Debugger::DiagnosticLocation &location);
QString hintAboutBuildBeforeAnalysis(); QString hintAboutBuildBeforeAnalysis();

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "diagnosticmark.h"
#include "clangtoolsutils.h"
#include <utils/utilsicons.h>
namespace ClangTools {
namespace Internal {
DiagnosticMark::DiagnosticMark(const Diagnostic &diagnostic)
: TextEditor::TextMark(Utils::FilePath::fromString(diagnostic.location.filePath),
diagnostic.location.line,
Utils::Id("ClangTool.DiagnosticMark"))
, m_diagnostic(diagnostic)
{
if (diagnostic.type == "error" || diagnostic.type == "fatal")
setColor(Utils::Theme::CodeModel_Error_TextMarkColor);
else
setColor(Utils::Theme::CodeModel_Warning_TextMarkColor);
setPriority(TextEditor::TextMark::HighPriority);
QIcon markIcon = diagnostic.icon();
setIcon(markIcon.isNull() ? Utils::Icons::CODEMODEL_WARNING.icon() : markIcon);
setToolTip(createDiagnosticToolTipString(diagnostic, Utils::nullopt, true));
setLineAnnotation(diagnostic.description);
}
void DiagnosticMark::disable()
{
if (!m_enabled)
return;
m_enabled = false;
if (m_diagnostic.type == "error" || m_diagnostic.type == "fatal")
setIcon(Utils::Icons::CODEMODEL_DISABLED_ERROR.icon());
else
setIcon(Utils::Icons::CODEMODEL_DISABLED_WARNING.icon());
setColor(Utils::Theme::Color::IconsDisabledColor);
updateMarker();
}
bool DiagnosticMark::enabled() const
{
return m_enabled;
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "clangtoolsdiagnostic.h"
#include <texteditor/textmark.h>
namespace ClangTools {
namespace Internal {
class DiagnosticMark : public TextEditor::TextMark
{
public:
explicit DiagnosticMark(const Diagnostic &diagnostic);
void disable();
bool enabled() const;
private:
const Diagnostic m_diagnostic;
bool m_enabled = true;
};
} // namespace Internal
} // namespace ClangTools