forked from qt-creator/qt-creator
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:
@@ -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;
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user