Axivion: Use task tree for fetching opened doc issues

Change-Id: I34a694a25cebc312b5ce32eccfa1b5ad04680b01
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Jarek Kobus
2024-01-30 15:15:50 +01:00
parent ba4df4555b
commit 41de43fa0a
5 changed files with 87 additions and 104 deletions

View File

@@ -390,35 +390,6 @@ void IssuesWidget::setTableDto(const Dto::TableInfoDto &dto)
m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header)); m_issuesView->setColumnHidden(counter++, hiddenColumns.contains(header));
} }
static QString anyToSimpleString(const Dto::Any &any)
{
if (any.isString())
return any.getString();
if (any.isBool())
return QString("%1").arg(any.getBool());
if (any.isDouble())
return QString::number(any.getDouble());
if (any.isNull())
return QString(); // or NULL??
if (any.isList()) {
const std::vector<Dto::Any> anyList = any.getList();
QStringList list;
for (const Dto::Any &inner : anyList)
list << anyToSimpleString(inner);
return list.join(',');
}
if (any.isMap()) { // TODO
const std::map<QString, Dto::Any> anyMap = any.getMap();
auto value = anyMap.find("displayName");
if (value != anyMap.end())
return anyToSimpleString(value->second);
value = anyMap.find("name");
if (value != anyMap.end())
return anyToSimpleString(value->second);
}
return QString();
}
static Links linksForIssue(const std::map<QString, Dto::Any> &issueRow) static Links linksForIssue(const std::map<QString, Dto::Any> &issueRow)
{ {
Links links; Links links;

View File

@@ -68,6 +68,35 @@ QIcon iconForIssue(const QString &prefix)
return it.value(); return it.value();
} }
QString anyToSimpleString(const Dto::Any &any)
{
if (any.isString())
return any.getString();
if (any.isBool())
return QString("%1").arg(any.getBool());
if (any.isDouble())
return QString::number(any.getDouble());
if (any.isNull())
return QString(); // or NULL??
if (any.isList()) {
const std::vector<Dto::Any> anyList = any.getList();
QStringList list;
for (const Dto::Any &inner : anyList)
list << anyToSimpleString(inner);
return list.join(',');
}
if (any.isMap()) { // TODO
const std::map<QString, Dto::Any> anyMap = any.getMap();
auto value = anyMap.find("displayName");
if (value != anyMap.end())
return anyToSimpleString(value->second);
value = anyMap.find("name");
if (value != anyMap.end())
return anyToSimpleString(value->second);
}
return {};
}
QString IssueListSearch::toQuery() const QString IssueListSearch::toQuery() const
{ {
if (kind.isEmpty()) if (kind.isEmpty())
@@ -83,6 +112,10 @@ QString IssueListSearch::toQuery() const
result.append(QString("&end=%1").arg( result.append(QString("&end=%1").arg(
QString::fromUtf8(QUrl::toPercentEncoding(versionEnd)))); QString::fromUtf8(QUrl::toPercentEncoding(versionEnd))));
} }
if (!filter_path.isEmpty()) {
result.append(QString("&filter_path=%1").arg(
QString::fromUtf8(QUrl::toPercentEncoding(filter_path))));
}
if (computeTotalRowCount) if (computeTotalRowCount)
result.append("&computeTotalRowCount=true"); result.append("&computeTotalRowCount=true");
return result; return result;
@@ -108,6 +141,7 @@ public:
std::optional<Dto::ProjectInfoDto> m_currentProjectInfo; std::optional<Dto::ProjectInfoDto> m_currentProjectInfo;
bool m_runningQuery = false; bool m_runningQuery = false;
TaskTreeRunner m_taskTreeRunner; TaskTreeRunner m_taskTreeRunner;
std::unordered_map<IDocument *, std::unique_ptr<TaskTree>> m_docMarksTrees;
}; };
static AxivionPluginPrivate *dd = nullptr; static AxivionPluginPrivate *dd = nullptr;
@@ -438,15 +472,16 @@ Group tableInfoRecipe(const QString &prefix, const TableInfoHandler &handler)
void AxivionPluginPrivate::fetchRuleInfo(const QString &id) void AxivionPluginPrivate::fetchRuleInfo(const QString &id)
{ {
if (!m_currentProjectInfo)
return;
if (m_runningQuery) { if (m_runningQuery) {
QTimer::singleShot(3000, this, [this, id] { fetchRuleInfo(id); }); QTimer::singleShot(3000, this, [this, id] { fetchRuleInfo(id); });
return; return;
} }
const QStringList args = id.split(':');
QTC_ASSERT(args.size() == 2, return);
m_runningQuery = true; m_runningQuery = true;
AxivionQuery query(AxivionQuery::RuleInfo, args); AxivionQuery query(AxivionQuery::RuleInfo, {m_currentProjectInfo->name, "SV" + id});
AxivionQueryRunner *runner = new AxivionQueryRunner(query, this); AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){ connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){
m_runningQuery = false; m_runningQuery = false;
@@ -487,16 +522,48 @@ void AxivionPluginPrivate::onDocumentOpened(IDocument *doc)
if (!doc || !project->isKnownFile(doc->filePath())) if (!doc || !project->isKnownFile(doc->filePath()))
return; return;
const FilePath relative = doc->filePath().relativeChildPath(project->projectDirectory()); IssueListSearch search;
// for now only style violations search.kind = "SV";
const AxivionQuery query(AxivionQuery::IssuesForFileList, {m_currentProjectInfo->name, "SV", search.filter_path = doc->filePath().relativeChildPath(project->projectDirectory()).path();
relative.path()});
AxivionQueryRunner *runner = new AxivionQueryRunner(query, this); const auto issuesHandler = [this](const Dto::IssueTableDto &dto) {
connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){ IssuesList issues;
handleIssuesForFile(ResultParser::parseIssuesList(result)); const std::vector<std::map<QString, Dto::Any>> &rows = dto.rows;
for (const auto &row : rows) {
ShortIssue issue;
for (auto it = row.cbegin(); it != row.cend(); ++it) {
if (it->first == "id")
issue.id = anyToSimpleString(it->second);
else if (it->first == "state")
issue.state = anyToSimpleString(it->second);
else if (it->first == "errorNumber")
issue.errorNumber = anyToSimpleString(it->second);
else if (it->first == "message")
issue.message = anyToSimpleString(it->second);
else if (it->first == "entity")
issue.entity = anyToSimpleString(it->second);
else if (it->first == "path")
issue.filePath = anyToSimpleString(it->second);
else if (it->first == "severity")
issue.severity = anyToSimpleString(it->second);
else if (it->first == "line")
issue.lineNumber = anyToSimpleString(it->second).toInt();
}
issues.issues << issue;
}
handleIssuesForFile(issues);
};
TaskTree *taskTree = new TaskTree;
taskTree->setRecipe(issueTableRecipe(search, issuesHandler));
m_docMarksTrees.insert_or_assign(doc, std::unique_ptr<TaskTree>(taskTree));
connect(taskTree, &TaskTree::done, this, [this, doc] {
const auto it = m_docMarksTrees.find(doc);
QTC_ASSERT(it != m_docMarksTrees.end(), return);
it->second.release()->deleteLater();
m_docMarksTrees.erase(it);
}); });
connect(runner, &AxivionQueryRunner::finished, [runner]{ runner->deleteLater(); }); taskTree->start();
runner->start();
} }
void AxivionPluginPrivate::onDocumentClosed(IDocument *doc) void AxivionPluginPrivate::onDocumentClosed(IDocument *doc)
@@ -505,6 +572,10 @@ void AxivionPluginPrivate::onDocumentClosed(IDocument *doc)
if (!document) if (!document)
return; return;
const auto it = m_docMarksTrees.find(doc);
if (it != m_docMarksTrees.end())
m_docMarksTrees.erase(it);
const TextEditor::TextMarks marks = document->marks(); const TextEditor::TextMarks marks = document->marks();
for (auto m : marks) { for (auto m : marks) {
if (m->category().id == AxivionTextMarkId) if (m->category().id == AxivionTextMarkId)

View File

@@ -31,6 +31,7 @@ struct IssueListSearch
QString versionEnd; QString versionEnd;
QString owner; QString owner;
QString pathglob; QString pathglob;
QString filter_path;
int offset = 0; int offset = 0;
int limit = 30; int limit = 30;
bool computeTotalRowCount = false; bool computeTotalRowCount = false;
@@ -64,6 +65,7 @@ std::optional<Dto::ProjectInfoDto> projectInfo();
bool handleCertificateIssue(); bool handleCertificateIssue();
QIcon iconForIssue(const QString &prefix); QIcon iconForIssue(const QString &prefix);
QString anyToSimpleString(const Dto::Any &any);
} // Axivion::Internal } // Axivion::Internal

View File

@@ -54,66 +54,6 @@ static BaseResult prehandleHeader(const QByteArray &header, const QByteArray &bo
namespace ResultParser { namespace ResultParser {
static QRegularExpression issueCsvLineRegex(const QByteArray &firstCsvLine)
{
QString pattern = "^";
for (const QByteArray &part : firstCsvLine.split(',')) {
const QString cleaned = QString::fromUtf8(part).remove(' ').chopped(1).mid(1);
pattern.append(QString("\"(?<" + cleaned + ">.*)\","));
}
pattern.chop(1); // remove last comma
pattern.append('$');
const QRegularExpression regex(pattern);
QTC_ASSERT(regex.isValid(), return {});
return regex;
}
static void parseCsvIssue(const QByteArray &csv, QList<ShortIssue> *issues)
{
QTC_ASSERT(issues, return);
bool first = true;
std::optional<QRegularExpression> regex;
for (auto &line : csv.split('\n')) {
if (first) {
regex.emplace(issueCsvLineRegex(line));
first = false;
if (regex.value().pattern().isEmpty())
return;
continue;
}
if (line.isEmpty())
continue;
const QRegularExpressionMatch match = regex->match(QString::fromUtf8(line));
QTC_ASSERT(match.hasMatch(), continue);
// FIXME: some of these are not present for all issue kinds! Limited to SV for now
ShortIssue issue;
issue.id = match.captured("Id");
issue.state = match.captured("State");
issue.errorNumber = match.captured("ErrorNumber");
issue.message = match.captured("Message");
issue.entity = match.captured("Entity");
issue.filePath = match.captured("Path");
issue.severity = match.captured("Severity");
issue.lineNumber = match.captured("Line").toInt();
issues->append(issue);
}
}
IssuesList parseIssuesList(const QByteArray &input)
{
IssuesList result;
auto [header, body] = splitHeaderAndBody(input);
BaseResult headerResult = prehandleHeader(header, body);
if (!headerResult.error.isEmpty()) {
result.error = headerResult.error;
return result;
}
parseCsvIssue(body, &result.issues);
return result;
}
QString parseRuleInfo(const QByteArray &input) // html result! QString parseRuleInfo(const QByteArray &input) // html result!
{ {
auto [header, body] = splitHeaderAndBody(input); auto [header, body] = splitHeaderAndBody(input);

View File

@@ -43,7 +43,6 @@ public:
namespace ResultParser { namespace ResultParser {
IssuesList parseIssuesList(const QByteArray &input);
QString parseRuleInfo(const QByteArray &input); QString parseRuleInfo(const QByteArray &input);
} // ResultParser } // ResultParser