/*************************************************************************** * Copyright 2013-2014 Maciej Poleski * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "bzrannotatejob.h" #include #include #include #include #include #include #include #include BzrAnnotateJob::BzrAnnotateJob(const QDir& workingDir, const QString& revisionSpec, const KUrl& localLocation, KDevelop::IPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity), m_workingDir(workingDir), m_revisionSpec(revisionSpec), m_localLocation(localLocation), m_vcsPlugin(parent), m_status(KDevelop::VcsJob::JobNotStarted) { setType(JobType::Annotate); setCapabilities(Killable); } bool BzrAnnotateJob::doKill() { m_status = KDevelop::VcsJob::JobCanceled; if (m_job) return m_job->kill(KJob::Quietly); else return true; } void BzrAnnotateJob::start() { if (m_status != KDevelop::VcsJob::JobNotStarted) return; KDevelop::DVcsJob* job = new KDevelop::DVcsJob(m_workingDir, m_vcsPlugin, KDevelop::OutputJob::Silent); *job << "bzr" << "annotate" << "--all" << m_revisionSpec << m_localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseBzrAnnotateOutput(KDevelop::DVcsJob*))); m_status = KDevelop::VcsJob::JobRunning; m_job = job; job->start(); } void BzrAnnotateJob::parseBzrAnnotateOutput(KDevelop::DVcsJob* job) { m_outputLines = job->output().split('\n'); m_currentLine = 0; if (m_status == KDevelop::VcsJob::JobRunning) QTimer::singleShot(0, this, SLOT(parseNextLine())); } void BzrAnnotateJob::parseNextLine() { for(;;) { Q_ASSERT(m_currentLine<=m_outputLines.size()); if (m_currentLine == m_outputLines.size()) { m_status = KDevelop::VcsJob::JobSucceeded; emitResult(); emit resultsReady(this); break; } QString currentLine = m_outputLines[m_currentLine]; if (currentLine.isEmpty()) { ++m_currentLine; continue; } bool revOk; auto revision = currentLine.left(currentLine.indexOf(' ')).toULong(&revOk); if (!revOk) { // Future compatibility - not a revision yet ++m_currentLine; continue; } auto i = m_commits.find(revision); if (i != m_commits.end()) { KDevelop::VcsAnnotationLine line; line.setAuthor(i.value().author()); line.setCommitMessage(i.value().message()); line.setDate(i.value().date()); line.setLineNumber(m_currentLine); line.setRevision(i.value().revision()); m_results.append(QVariant::fromValue(line)); ++m_currentLine; continue; } else { prepareCommitInfo(revision); break; //Will reenter this function when commit info will be ready } } } void BzrAnnotateJob::prepareCommitInfo(std::size_t revision) { if (m_status != KDevelop::VcsJob::JobRunning) return; KDevelop::DVcsJob* job = new KDevelop::DVcsJob(m_workingDir, m_vcsPlugin, KDevelop::OutputJob::Silent); job->setType(KDevelop::VcsJob::Log); *job << "bzr" << "log" << "--long" << "-r" << QString::number(revision); connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseBzrLog(KDevelop::DVcsJob*))); m_job = job; job->start(); } /* * This is slightly different from BazaarUtils::parseBzrLogPart(...). * This function parses only commit general info. It does not parse signle * actions. In fact output parsed by this function is slightly different * from output parsed by BazaarUtils. As a result parsing this output using * BazaarUtils would yield different results. * NOTE: This is all about parsing 'message'. */ void BzrAnnotateJob::parseBzrLog(KDevelop::DVcsJob* job) { QStringList outputLines = job->output().split('\n'); KDevelop::VcsEvent commitInfo; int revision=-1; bool atMessage = false; QString message; for (const QString &line : outputLines) { if (!atMessage) { if (line.startsWith("revno")) { QString revno = line.mid(QString("revno: ").length()); // In future there is possibility that "revno: " will change to // "revno??". If that's all, then we recover matching only // "revno" prefix and assuming placeholder of length 2 (": " or // "??"). // The same below with exception of "commiter" which possibly // can have also some suffix which changes meaning like // "commiter-some_property: "... revno = revno.left(revno.indexOf(' ')); revision = revno.toInt(); KDevelop::VcsRevision revision; revision.setRevisionValue(revno.toLongLong(), KDevelop::VcsRevision::GlobalNumber); commitInfo.setRevision(revision); } else if (line.startsWith("committer: ")) { QString commiter = line.mid(QString("committer: ").length()); commitInfo.setAuthor(commiter); // Author goes after commiter, but only if is different } else if (line.startsWith("author")) { QString author = line.mid(QString("author: ").length()); commitInfo.setAuthor(author); // It may override commiter (In fact commiter is not supported by VcsEvent) } else if (line.startsWith("timestamp")) { const QString formatString = "yyyy-MM-dd hh:mm:ss"; QString timestamp = line.mid(QString("timestamp: ddd ").length(), formatString.length()); commitInfo.setDate(QDateTime::fromString(timestamp, formatString)); } else if (line.startsWith("message")) { atMessage = true; } } else { message += line.trimmed() + "\n"; } } if (atMessage) commitInfo.setMessage(message.trimmed()); Q_ASSERT(revision!=-1); m_commits[revision] = commitInfo; // Invoke from event loop to protect against stack overflow (it could happen // on very big files with very big history of changes if tail-recursion // optimization had failed here). QTimer::singleShot(0, this, SLOT(parseNextLine())); } QVariant BzrAnnotateJob::fetchResults() { return m_results; } KDevelop::VcsJob::JobStatus BzrAnnotateJob::status() const { return m_status; } KDevelop::IPlugin* BzrAnnotateJob::vcsPlugin() const { return m_vcsPlugin; } #include "moc_bzrannotatejob.cpp"