ProjectExplorer: Give the Task class an explicit summary

We'd like to create more useful tasks from compiler output, that is, try
harder to identify consecutive lines that refer to the same issue and
create one task for them, rather than one for each line. In such
"aggregate" tasks, the first line will not necessarily carry the main
information. Therefore, we make it explicit what this main information
is by introducing a dedicated summary member.
Also streamline the font handling for compile tasks.

Change-Id: I933f2643a13c710dab1ab548c56669b129026eb5
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-05-12 16:26:34 +02:00
parent e35f945758
commit b02f6b5d30
29 changed files with 89 additions and 157 deletions

View File

@@ -28,9 +28,6 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <QRegularExpression>
using namespace ProjectExplorer;
@@ -69,28 +66,6 @@ void IarParser::newTask(const Task &task)
m_lines = 1;
}
void IarParser::amendDescription()
{
while (!m_descriptionParts.isEmpty())
m_lastTask.description.append(m_descriptionParts.takeFirst());
while (!m_snippets.isEmpty()) {
const QString snippet = m_snippets.takeFirst();
const int start = m_lastTask.description.count() + 1;
m_lastTask.description.append('\n');
m_lastTask.description.append(snippet);
QTextLayout::FormatRange fr;
fr.start = start;
fr.length = m_lastTask.description.count() + 1;
fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font());
fr.format.setFontStyleHint(QFont::Monospace);
m_lastTask.formats.append(fr);
++m_lines;
}
}
void IarParser::amendFilePath()
{
if (m_filePathParts.isEmpty())
@@ -251,7 +226,12 @@ void IarParser::flush()
if (m_lastTask.isNull())
return;
amendDescription();
while (!m_descriptionParts.isEmpty())
m_lastTask.summary.append(m_descriptionParts.takeFirst());
m_lastTask.details = m_snippets;
m_snippets.clear();
m_lines += m_lastTask.details.count();
setMonospacedDetailsFormat(m_lastTask);
amendFilePath();
m_expectSnippet = true;

View File

@@ -28,9 +28,6 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <QRegularExpression>
using namespace ProjectExplorer;
@@ -71,26 +68,6 @@ void KeilParser::newTask(const Task &task)
m_lines = 1;
}
void KeilParser::amendDescription()
{
while (!m_snippets.isEmpty()) {
const QString snippet = m_snippets.takeFirst();
const int start = m_lastTask.description.count() + 1;
m_lastTask.description.append('\n');
m_lastTask.description.append(snippet);
QTextLayout::FormatRange fr;
fr.start = start;
fr.length = m_lastTask.description.count() + 1;
fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font());
fr.format.setFontStyleHint(QFont::Monospace);
m_lastTask.formats.append(fr);
++m_lines;
}
}
// ARM compiler specific parsers.
OutputLineParser::Result KeilParser::parseArmWarningOrErrorDetailsMessage(const QString &lne)
@@ -278,8 +255,10 @@ void KeilParser::flush()
if (m_lastTask.isNull())
return;
amendDescription();
m_lastTask.details = m_snippets;
m_snippets.clear();
m_lines += m_lastTask.details.count();
setMonospacedDetailsFormat(m_lastTask);
Task t = m_lastTask;
m_lastTask.clear();
scheduleTask(t, m_lines, 1);

View File

@@ -43,7 +43,6 @@ public:
private:
void newTask(const ProjectExplorer::Task &task);
void amendDescription();
// ARM compiler specific parsers.
Result parseArmWarningOrErrorDetailsMessage(const QString &lne);

View File

@@ -28,9 +28,6 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/task.h>
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <QRegularExpression>
using namespace ProjectExplorer;
@@ -73,17 +70,7 @@ void SdccParser::newTask(const Task &task)
void SdccParser::amendDescription(const QString &desc)
{
const int start = m_lastTask.description.count() + 1;
m_lastTask.description.append('\n');
m_lastTask.description.append(desc);
QTextLayout::FormatRange fr;
fr.start = start;
fr.length = m_lastTask.description.count() + 1;
fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font());
fr.format.setFontStyleHint(QFont::Monospace);
m_lastTask.formats.append(fr);
m_lastTask.details.append(desc);
++m_lines;
}
@@ -165,6 +152,7 @@ void SdccParser::flush()
if (m_lastTask.isNull())
return;
setMonospacedDetailsFormat(m_lastTask);
Task t = m_lastTask;
m_lastTask.clear();
scheduleTask(t, m_lines, 1);

View File

@@ -101,9 +101,9 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm
m_lines = 1;
return {Status::InProgress, linkSpecs};
} else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) {
if (!m_lastTask.description.isEmpty())
m_lastTask.description.append(QLatin1Char(' '));
m_lastTask.description.append(trimmedLine.trimmed());
if (!m_lastTask.summary.isEmpty())
m_lastTask.summary.append(' ');
m_lastTask.summary.append(trimmedLine.trimmed());
++m_lines;
return Status::InProgress;
} else if (trimmedLine.endsWith(QLatin1String("in cmake code at"))) {
@@ -136,7 +136,7 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm
return {Status::InProgress, linkSpecs};
}
case LINE_DESCRIPTION:
m_lastTask.description = trimmedLine;
m_lastTask.summary = trimmedLine;
if (trimmedLine.endsWith(QLatin1Char('\"')))
m_expectTripleLineErrorData = LINE_DESCRIPTION2;
else {
@@ -146,8 +146,7 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm
}
return Status::InProgress;
case LINE_DESCRIPTION2:
m_lastTask.description.append(QLatin1Char('\n'));
m_lastTask.description.append(trimmedLine);
m_lastTask.details.append(trimmedLine);
m_expectTripleLineErrorData = NONE;
flush();
return Status::Done;

View File

@@ -977,7 +977,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
const Tasks tasks = DebuggerKitAspect::validateDebugger(kit);
for (const Task &t : tasks) {
if (t.type != Task::Warning)
m_runParameters.validationErrors.append(t.description);
m_runParameters.validationErrors.append(t.description());
}
RunConfiguration *runConfig = runControl->runConfiguration();

View File

@@ -36,15 +36,12 @@ using namespace Help::Internal;
bool SearchTaskHandler::canHandle(const ProjectExplorer::Task &task) const
{
return !task.description.isEmpty()
&& !task.description.startsWith(QLatin1Char('\n'));
return !task.summary.isEmpty();
}
void SearchTaskHandler::handle(const ProjectExplorer::Task &task)
{
const int eol = task.description.indexOf(QLatin1Char('\n'));
const QUrl url(QLatin1String("https://www.google.com/search?q=") + task.description.left(eol));
emit search(url);
emit search(QUrl("https://www.google.com/search?q=" + task.summary));
}
QAction *SearchTaskHandler::createAction(QObject *parent) const

View File

@@ -112,7 +112,7 @@ OutputLineParser::Result ClangParser::handleLine(const QString &line, OutputForm
}
if (m_expectSnippet) {
amendDescription(lne, true);
amendDescription(lne);
return Status::InProgress;
}

View File

@@ -44,7 +44,7 @@ ConfigTaskHandler::ConfigTaskHandler(const Task &pattern, Core::Id page) :
bool ConfigTaskHandler::canHandle(const Task &task) const
{
return task.description == m_pattern.description
return task.description() == m_pattern.description()
&& task.category == m_pattern.category;
}

View File

@@ -54,7 +54,7 @@ void CopyTaskHandler::handle(const Task &task)
QApplication::clipboard()->setText(task.file.toUserOutput() + QLatin1Char(':') +
QString::number(task.line) + QLatin1String(": ")
+ type + task.description);
+ type + task.description());
}
Core::Id CopyTaskHandler::actionManagerId() const

View File

@@ -292,7 +292,7 @@ void ExtraCompilerPrivate::updateIssues()
const auto fontSettings = TextEditor::TextEditorSettings::instance()->fontSettings();
selection.format = fontSettings.toTextCharFormat(issue.type == Task::Warning ?
TextEditor::C_WARNING : TextEditor::C_ERROR);
selection.format.setToolTip(issue.description);
selection.format.setToolTip(issue.description());
selections.append(selection);
}

View File

@@ -30,8 +30,6 @@
#include "projectexplorerconstants.h"
#include "buildmanager.h"
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
@@ -82,27 +80,19 @@ void GccParser::flush()
{
if (m_currentTask.isNull())
return;
setMonospacedDetailsFormat(m_currentTask);
Task t = m_currentTask;
m_currentTask.clear();
scheduleTask(t, m_lines, 1);
m_lines = 0;
}
void GccParser::amendDescription(const QString &desc, bool monospaced)
void GccParser::amendDescription(const QString &desc)
{
if (m_currentTask.isNull())
return;
int start = m_currentTask.description.count() + 1;
m_currentTask.description.append(QLatin1Char('\n'));
m_currentTask.description.append(desc);
if (monospaced) {
QTextLayout::FormatRange fr;
fr.start = start;
fr.length = desc.count() + 1;
fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font());
fr.format.setFontStyleHint(QFont::Monospace);
m_currentTask.formats.append(fr);
}
m_currentTask.details.append(desc);
++m_lines;
return;
}
@@ -174,7 +164,7 @@ OutputLineParser::Result GccParser::handleLine(const QString &line, OutputFormat
newTask(CompileTask(Task::Unknown, lne.trimmed() /* description */, filePath, lineNo));
return {Status::InProgress, linkSpecs};
} else if (lne.startsWith(' ') && !m_currentTask.isNull()) {
amendDescription(lne, true);
amendDescription(lne);
return Status::InProgress;
}

View File

@@ -48,7 +48,7 @@ protected:
void newTask(const Task &task);
void flush() override;
void amendDescription(const QString &desc, bool monospaced);
void amendDescription(const QString &desc);
private:
Result handleLine(const QString &line, Utils::OutputFormat type) override;

View File

@@ -28,6 +28,9 @@
#include "task.h"
#include "taskhub.h"
#include <texteditor/fontsettings.h>
#include <texteditor/texteditorsettings.h>
/*!
\class ProjectExplorer::OutputTaskParser
@@ -91,6 +94,18 @@ void OutputTaskParser::scheduleTask(const Task &task, int outputLines, int skipp
QTC_CHECK(d->scheduledTasks.size() <= 2);
}
void OutputTaskParser::setMonospacedDetailsFormat(Task &task)
{
if (task.details.isEmpty())
return;
QTextLayout::FormatRange fr;
fr.start = task.summary.length() + 1;
fr.length = task.details.join('\n').length();
fr.format.setFont(TextEditor::TextEditorSettings::fontSettings().font());
fr.format.setFontStyleHint(QFont::Monospace);
task.formats = {fr};
}
void OutputTaskParser::runPostPrintActions()
{
for (const TaskInfo &t : qAsConst(d->scheduledTasks))

View File

@@ -54,6 +54,7 @@ public:
protected:
void scheduleTask(const Task &task, int outputLines, int skippedLines = 0);
void setMonospacedDetailsFormat(Task &task);
private:
void runPostPrintActions() override;

View File

@@ -77,7 +77,7 @@ Utils::OutputLineParser::Result LdParser::handleLine(const QString &line, Utils:
return Status::InProgress;
}
if (!m_incompleteTask.isNull() && lne.startsWith(" ")) {
m_incompleteTask.description.append('\n').append(lne);
m_incompleteTask.details.append(lne);
static const QRegularExpression locRegExp(" (?<symbol>\\S+) in (?<file>\\S+)");
const QRegularExpressionMatch match = locRegExp.match(lne);
LinkSpecs linkSpecs;

View File

@@ -92,18 +92,7 @@ OutputLineParser::Result LinuxIccParser::handleLine(const QString &line, OutputF
return Status::InProgress;
}
if (!m_expectFirstLine && m_caretLine.indexIn(line) != -1) {
// Format the last line as code
QTextLayout::FormatRange fr;
fr.start = m_temporary.description.lastIndexOf(QLatin1Char('\n')) + 1;
fr.length = m_temporary.description.length() - fr.start;
fr.format.setFontItalic(true);
m_temporary.formats.append(fr);
QTextLayout::FormatRange fr2;
fr2.start = fr.start + line.indexOf(QLatin1Char('^')) - m_indent;
fr2.length = 1;
fr2.format.setFontWeight(QFont::Bold);
m_temporary.formats.append(fr2);
// FIXME: m_temporary.details.append(line);
return Status::InProgress;
}
if (!m_expectFirstLine && line.trimmed().isEmpty()) { // last Line
@@ -113,11 +102,7 @@ OutputLineParser::Result LinuxIccParser::handleLine(const QString &line, OutputF
return Status::Done;
}
if (!m_expectFirstLine && m_continuationLines.indexIn(line) != -1) {
m_temporary.description.append(QLatin1Char('\n'));
m_indent = 0;
while (m_indent < line.length() && line.at(m_indent).isSpace())
m_indent++;
m_temporary.description.append(m_continuationLines.cap(1).trimmed());
m_temporary.details.append(m_continuationLines.cap(1).trimmed());
++m_lines;
return Status::InProgress;
}
@@ -139,6 +124,8 @@ void LinuxIccParser::flush()
{
if (m_temporary.isNull())
return;
setMonospacedDetailsFormat(m_temporary);
Task t = m_temporary;
m_temporary.clear();
scheduleTask(t, m_lines, 1);

View File

@@ -53,7 +53,6 @@ private:
QRegExp m_pchInfoLine;
bool m_expectFirstLine = true;
int m_indent = 0;
Task m_temporary;
int m_lines = 0;
};

View File

@@ -113,26 +113,7 @@ OutputLineParser::Result MsvcParser::handleLine(const QString &line, OutputForma
if (m_lastTask.isNull())
return Status::NotHandled;
m_lastTask.description.append('\n');
m_lastTask.description.append(line.mid(8));
// trim trailing spaces:
int i = 0;
for (i = m_lastTask.description.length() - 1; i >= 0; --i) {
if (!m_lastTask.description.at(i).isSpace())
break;
}
m_lastTask.description.truncate(i + 1);
if (m_lastTask.formats.isEmpty()) {
QTextLayout::FormatRange fr;
fr.start = m_lastTask.description.indexOf('\n') + 1;
fr.length = m_lastTask.description.length() - fr.start;
fr.format.setFontItalic(true);
m_lastTask.formats.append(fr);
} else {
m_lastTask.formats[0].length = m_lastTask.description.length()
- m_lastTask.formats[0].start;
}
m_lastTask.details.append(rightTrimmed(line.mid(8)));
++m_lines;
return Status::InProgress;
}
@@ -195,6 +176,7 @@ void MsvcParser::flush()
if (m_lastTask.isNull())
return;
setMonospacedDetailsFormat(m_lastTask);
Task t = m_lastTask;
m_lastTask.clear();
scheduleTask(t, m_lines, 1);
@@ -275,8 +257,7 @@ OutputLineParser::Result ClangClParser::handleLine(const QString &line, OutputFo
flush();
return Status::Done;
}
m_lastTask.description.append('\n');
m_lastTask.description.append(trimmed);
m_lastTask.details.append(trimmed);
++m_linkedLines;
return Status::InProgress;
}

View File

@@ -83,7 +83,7 @@ void OutputParserTester::testParsing(const QString &lines,
if (m_receivedTasks.size() == tasks.size()) {
for (int i = 0; i < tasks.size(); ++i) {
QCOMPARE(m_receivedTasks.at(i).category, tasks.at(i).category);
QCOMPARE(m_receivedTasks.at(i).description, tasks.at(i).description);
QCOMPARE(m_receivedTasks.at(i).description(), tasks.at(i).description());
QVERIFY2(m_receivedTasks.at(i).file == tasks.at(i).file,
msgFileComparisonFail(m_receivedTasks.at(i).file, tasks.at(i).file));
QCOMPARE(m_receivedTasks.at(i).line, tasks.at(i).line);

View File

@@ -388,7 +388,7 @@ QPair<Task::TaskType, QString> TargetSetupWidget::findIssues(const BuildInfo &in
highestType = Task::Warning;
severity = tr("<b>Warning:</b> ", "Severity is Task::Warning");
}
text.append(severity + t.description);
text.append(severity + t.description());
}
if (!text.isEmpty())
text = QLatin1String("<nobr>") + text;

View File

@@ -62,15 +62,20 @@ unsigned int Task::s_nextId = 1;
\sa ProjectExplorer::TaskHub
*/
Task::Task(TaskType type_, const QString &description_,
Task::Task(TaskType type_, const QString &description,
const Utils::FilePath &file_, int line_, Core::Id category_,
const QIcon &icon, Options options) :
taskId(s_nextId), type(type_), options(options), description(description_),
taskId(s_nextId), type(type_), options(options), summary(description),
line(line_), movedLine(line_), category(category_),
icon(icon.isNull() ? taskTypeIcon(type_) : icon)
{
++s_nextId;
setFile(file_);
QStringList desc = description.split('\n');
if (desc.length() > 1) {
summary = desc.first();
details = desc.mid(1);
}
}
Task Task::compilerMissingTask()
@@ -97,7 +102,8 @@ void Task::clear()
{
taskId = 0;
type = Task::Unknown;
description.clear();
summary.clear();
details.clear();
file = Utils::FilePath();
line = -1;
movedLine = -1;
@@ -119,6 +125,14 @@ void Task::setFile(const Utils::FilePath &file_)
}
}
QString Task::description() const
{
QString desc = summary;
if (!details.isEmpty())
desc.append('\n').append(details.join('\n'));
return desc;
}
//
// functions
//
@@ -173,7 +187,7 @@ QString toHtml(const Tasks &issues)
default:
break;
}
str << "</b>" << t.description << "<br>";
str << "</b>" << t.description() << "<br>";
}
return result;
}

View File

@@ -32,6 +32,7 @@
#include <QIcon>
#include <QMetaType>
#include <QStringList>
#include <QTextLayout>
namespace TextEditor {
@@ -72,11 +73,13 @@ public:
bool isNull() const;
void clear();
void setFile(const Utils::FilePath &file);
QString description() const;
unsigned int taskId = 0;
TaskType type = Unknown;
Options options = AddTextMark | FlashWorthy;
QString description;
QString summary;
QStringList details;
Utils::FilePath file;
Utils::FilePaths fileCandidates;
int line = -1;

View File

@@ -74,9 +74,9 @@ public:
if (task.category == Constants::TASK_CATEGORY_COMPILE) {
setToolTip("<html><body><b>" + QApplication::translate("TaskHub", "Build Issue")
+ "</b><br/><code style=\"white-space:pre;font-family:monospace\">"
+ task.description.toHtmlEscaped() + "</code></body></html>");
+ task.description().toHtmlEscaped() + "</code></body></html>");
} else {
setToolTip(task.description);
setToolTip(task.description());
}
setIcon(task.icon);
setVisible(!task.icon.isNull());
@@ -152,7 +152,7 @@ void TaskHub::addTask(Task::TaskType type, const QString &description, Core::Id
void TaskHub::addTask(Task task)
{
QTC_ASSERT(m_registeredCategories.contains(task.category), return);
QTC_ASSERT(!task.description.isEmpty(), return);
QTC_ASSERT(!task.description().isEmpty(), return);
QTC_ASSERT(!task.isNull(), return);
QTC_ASSERT(task.m_mark.isNull(), return);

View File

@@ -247,7 +247,7 @@ QVariant TaskModel::data(const QModelIndex &index, int role) const
else if (role == TaskModel::MovedLine)
return m_tasks.at(index.row()).movedLine;
else if (role == TaskModel::Description)
return m_tasks.at(index.row()).description;
return m_tasks.at(index.row()).description();
else if (role == TaskModel::FileNotFound)
return m_fileNotFound.value(m_tasks.at(index.row()).file.toString());
else if (role == TaskModel::Type)
@@ -405,7 +405,7 @@ bool TaskFilterModel::filterAcceptsTask(const Task &task) const
return m_filterStringIsRegexp ? m_filterRegexp.isValid() && s.contains(m_filterRegexp)
: s.contains(m_filterText, m_filterCaseSensitivity);
};
if ((accepts(task.file.toString()) || accepts(task.description)) == m_filterIsInverted)
if ((accepts(task.file.toString()) || accepts(task.description())) == m_filterIsInverted)
accept = false;
}

View File

@@ -99,9 +99,9 @@ private:
m_tasks.append({Task::Warning, text.trimmed(), {}, -1, category});
} else {
Task &task = m_tasks.back();
if (!task.description.isEmpty())
task.description += ' ';
task.description += text.trimmed();
if (!task.summary.isEmpty())
task.summary += ' ';
task.summary += text.trimmed();
}
} else {
// The actual exception. This ends the traceback.

View File

@@ -313,7 +313,7 @@ void QmakeBuildConfiguration::updateProblemLabel()
}
if (!text.endsWith(QLatin1String("br>")))
text.append(QLatin1String("<br>"));
text.append(type + task.description);
text.append(type + task.description());
}
buildDirectoryAspect()->setProblem(text);
return;

View File

@@ -78,7 +78,7 @@ OutputLineParser::Result QtTestParser::handleLine(const QString &line, OutputFor
emitCurrentTask();
return {Status::Done, linkSpecs};
}
m_currentTask.description.append('\n').append(theLine);
m_currentTask.details.append(theLine);
return Status::InProgress;
}

View File

@@ -90,5 +90,5 @@ void CompilerOutputProcessor::handleTask(const ProjectExplorer::Task &task)
*m_ostream << ':' << task.line;
*m_ostream << ": ";
}
*m_ostream << task.description << '\n';
*m_ostream << task.description() << '\n';
}