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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Links links;
|
||||
|
@@ -68,6 +68,35 @@ QIcon iconForIssue(const QString &prefix)
|
||||
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
|
||||
{
|
||||
if (kind.isEmpty())
|
||||
@@ -83,6 +112,10 @@ QString IssueListSearch::toQuery() const
|
||||
result.append(QString("&end=%1").arg(
|
||||
QString::fromUtf8(QUrl::toPercentEncoding(versionEnd))));
|
||||
}
|
||||
if (!filter_path.isEmpty()) {
|
||||
result.append(QString("&filter_path=%1").arg(
|
||||
QString::fromUtf8(QUrl::toPercentEncoding(filter_path))));
|
||||
}
|
||||
if (computeTotalRowCount)
|
||||
result.append("&computeTotalRowCount=true");
|
||||
return result;
|
||||
@@ -108,6 +141,7 @@ public:
|
||||
std::optional<Dto::ProjectInfoDto> m_currentProjectInfo;
|
||||
bool m_runningQuery = false;
|
||||
TaskTreeRunner m_taskTreeRunner;
|
||||
std::unordered_map<IDocument *, std::unique_ptr<TaskTree>> m_docMarksTrees;
|
||||
};
|
||||
|
||||
static AxivionPluginPrivate *dd = nullptr;
|
||||
@@ -438,15 +472,16 @@ Group tableInfoRecipe(const QString &prefix, const TableInfoHandler &handler)
|
||||
|
||||
void AxivionPluginPrivate::fetchRuleInfo(const QString &id)
|
||||
{
|
||||
if (!m_currentProjectInfo)
|
||||
return;
|
||||
|
||||
if (m_runningQuery) {
|
||||
QTimer::singleShot(3000, this, [this, id] { fetchRuleInfo(id); });
|
||||
return;
|
||||
}
|
||||
|
||||
const QStringList args = id.split(':');
|
||||
QTC_ASSERT(args.size() == 2, return);
|
||||
m_runningQuery = true;
|
||||
AxivionQuery query(AxivionQuery::RuleInfo, args);
|
||||
AxivionQuery query(AxivionQuery::RuleInfo, {m_currentProjectInfo->name, "SV" + id});
|
||||
AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
|
||||
connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){
|
||||
m_runningQuery = false;
|
||||
@@ -487,16 +522,48 @@ void AxivionPluginPrivate::onDocumentOpened(IDocument *doc)
|
||||
if (!doc || !project->isKnownFile(doc->filePath()))
|
||||
return;
|
||||
|
||||
const FilePath relative = doc->filePath().relativeChildPath(project->projectDirectory());
|
||||
// for now only style violations
|
||||
const AxivionQuery query(AxivionQuery::IssuesForFileList, {m_currentProjectInfo->name, "SV",
|
||||
relative.path()});
|
||||
AxivionQueryRunner *runner = new AxivionQueryRunner(query, this);
|
||||
connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){
|
||||
handleIssuesForFile(ResultParser::parseIssuesList(result));
|
||||
IssueListSearch search;
|
||||
search.kind = "SV";
|
||||
search.filter_path = doc->filePath().relativeChildPath(project->projectDirectory()).path();
|
||||
|
||||
const auto issuesHandler = [this](const Dto::IssueTableDto &dto) {
|
||||
IssuesList issues;
|
||||
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(); });
|
||||
runner->start();
|
||||
taskTree->start();
|
||||
}
|
||||
|
||||
void AxivionPluginPrivate::onDocumentClosed(IDocument *doc)
|
||||
@@ -505,6 +572,10 @@ void AxivionPluginPrivate::onDocumentClosed(IDocument *doc)
|
||||
if (!document)
|
||||
return;
|
||||
|
||||
const auto it = m_docMarksTrees.find(doc);
|
||||
if (it != m_docMarksTrees.end())
|
||||
m_docMarksTrees.erase(it);
|
||||
|
||||
const TextEditor::TextMarks marks = document->marks();
|
||||
for (auto m : marks) {
|
||||
if (m->category().id == AxivionTextMarkId)
|
||||
|
@@ -31,6 +31,7 @@ struct IssueListSearch
|
||||
QString versionEnd;
|
||||
QString owner;
|
||||
QString pathglob;
|
||||
QString filter_path;
|
||||
int offset = 0;
|
||||
int limit = 30;
|
||||
bool computeTotalRowCount = false;
|
||||
@@ -64,6 +65,7 @@ std::optional<Dto::ProjectInfoDto> projectInfo();
|
||||
bool handleCertificateIssue();
|
||||
|
||||
QIcon iconForIssue(const QString &prefix);
|
||||
QString anyToSimpleString(const Dto::Any &any);
|
||||
|
||||
} // Axivion::Internal
|
||||
|
||||
|
@@ -54,66 +54,6 @@ static BaseResult prehandleHeader(const QByteArray &header, const QByteArray &bo
|
||||
|
||||
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!
|
||||
{
|
||||
auto [header, body] = splitHeaderAndBody(input);
|
||||
|
@@ -43,7 +43,6 @@ public:
|
||||
|
||||
namespace ResultParser {
|
||||
|
||||
IssuesList parseIssuesList(const QByteArray &input);
|
||||
QString parseRuleInfo(const QByteArray &input);
|
||||
|
||||
} // ResultParser
|
||||
|
Reference in New Issue
Block a user