File indexing completed on 2024-05-05 04:39:01
0001 /* 0002 SPDX-FileCopyrightText: 2013-2014 Maciej Poleski 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "bzrannotatejob.h" 0008 0009 #include <functional> 0010 0011 #include <QTimer> 0012 #include <QDateTime> 0013 #include <QDir> 0014 0015 #include <vcs/dvcs/dvcsjob.h> 0016 #include <vcs/vcsannotation.h> 0017 #include <vcs/vcsrevision.h> 0018 #include <interfaces/iplugin.h> 0019 0020 using namespace KDevelop; 0021 0022 BzrAnnotateJob::BzrAnnotateJob(const QDir& workingDir, const QString& revisionSpec, const QUrl& localLocation, KDevelop::IPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity) 0023 : VcsJob(parent, verbosity), m_workingDir(workingDir), m_revisionSpec(revisionSpec), m_localLocation(localLocation), m_vcsPlugin(parent), m_status(KDevelop::VcsJob::JobNotStarted) 0024 { 0025 setType(JobType::Annotate); 0026 setCapabilities(Killable); 0027 } 0028 0029 bool BzrAnnotateJob::doKill() 0030 { 0031 m_status = KDevelop::VcsJob::JobCanceled; 0032 if (m_job) 0033 return m_job->kill(KJob::Quietly); 0034 else 0035 return true; 0036 } 0037 0038 void BzrAnnotateJob::start() 0039 { 0040 if (m_status != KDevelop::VcsJob::JobNotStarted) 0041 return; 0042 auto* job = new KDevelop::DVcsJob(m_workingDir, m_vcsPlugin, KDevelop::OutputJob::Silent); 0043 *job << "bzr" << "annotate" << "--all" << m_revisionSpec << m_localLocation; 0044 connect(job, &DVcsJob::readyForParsing, this, &BzrAnnotateJob::parseBzrAnnotateOutput); 0045 m_status = VcsJob::JobRunning; 0046 m_job = job; 0047 job->start(); 0048 } 0049 0050 void BzrAnnotateJob::parseBzrAnnotateOutput(KDevelop::DVcsJob* job) 0051 { 0052 m_outputLines = job->output().split(QLatin1Char('\n')); 0053 m_currentLine = 0; 0054 if (m_status == KDevelop::VcsJob::JobRunning) 0055 QTimer::singleShot(0, this, &BzrAnnotateJob::parseNextLine); 0056 } 0057 0058 void BzrAnnotateJob::parseNextLine() 0059 { 0060 for(;;) 0061 { 0062 Q_ASSERT(m_currentLine<=m_outputLines.size()); 0063 if (m_currentLine == m_outputLines.size()) { 0064 m_status = KDevelop::VcsJob::JobSucceeded; 0065 emitResult(); 0066 emit resultsReady(this); 0067 break; 0068 } 0069 QString currentLine = m_outputLines[m_currentLine]; 0070 if (currentLine.isEmpty()) { 0071 ++m_currentLine; 0072 continue; 0073 } 0074 bool revOk; 0075 auto revision = currentLine.leftRef(currentLine.indexOf(QLatin1Char(' '))).toULong(&revOk); 0076 if (!revOk) { 0077 // Future compatibility - not a revision yet 0078 ++m_currentLine; 0079 continue; 0080 } 0081 auto i = m_commits.find(revision); 0082 if (i != m_commits.end()) { 0083 KDevelop::VcsAnnotationLine line; 0084 line.setAuthor(i.value().author()); 0085 line.setCommitMessage(i.value().message()); 0086 line.setDate(i.value().date()); 0087 line.setLineNumber(m_currentLine); 0088 line.setRevision(i.value().revision()); 0089 m_results.append(QVariant::fromValue(line)); 0090 ++m_currentLine; 0091 continue; 0092 } else { 0093 prepareCommitInfo(revision); 0094 break; //Will reenter this function when commit info will be ready 0095 } 0096 } 0097 } 0098 0099 void BzrAnnotateJob::prepareCommitInfo(std::size_t revision) 0100 { 0101 if (m_status != KDevelop::VcsJob::JobRunning) 0102 return; 0103 auto* job = new KDevelop::DVcsJob(m_workingDir, m_vcsPlugin, KDevelop::OutputJob::Silent); 0104 job->setType(KDevelop::VcsJob::Log); 0105 *job << "bzr" << "log" << "--long" << "-r" << QString::number(revision); 0106 connect(job, &DVcsJob::readyForParsing, this, &BzrAnnotateJob::parseBzrLog); 0107 m_job = job; 0108 job->start(); 0109 } 0110 0111 /* 0112 * This is slightly different from BazaarUtils::parseBzrLogPart(...). 0113 * This function parses only commit general info. It does not parse signle 0114 * actions. In fact output parsed by this function is slightly different 0115 * from output parsed by BazaarUtils. As a result parsing this output using 0116 * BazaarUtils would yield different results. 0117 * NOTE: This is all about parsing 'message'. 0118 */ 0119 void BzrAnnotateJob::parseBzrLog(KDevelop::DVcsJob* job) 0120 { 0121 const QStringList outputLines = job->output().split(QLatin1Char('\n')); 0122 KDevelop::VcsEvent commitInfo; 0123 int revision=-1; 0124 bool atMessage = false; 0125 QString message; 0126 for (const QString& line : outputLines) { 0127 if (!atMessage) { 0128 if (line.startsWith(QLatin1String("revno"))) { 0129 QString revno = line.mid(QStringLiteral("revno: ").length()); 0130 // In future there is possibility that "revno: " will change to 0131 // "revno??". If that's all, then we recover matching only 0132 // "revno" prefix and assuming placeholder of length 2 (": " or 0133 // "??"). 0134 // The same below with exception of "committer" which possibly 0135 // can have also some suffix which changes meaning like 0136 // "committer-some_property: "... 0137 revno = revno.left(revno.indexOf(QLatin1Char(' '))); 0138 revision = revno.toInt(); 0139 KDevelop::VcsRevision revision; 0140 revision.setRevisionValue(revno.toLongLong(), KDevelop::VcsRevision::GlobalNumber); 0141 commitInfo.setRevision(revision); 0142 } else if (line.startsWith(QLatin1String("committer: "))) { 0143 QString commiter = line.mid(QStringLiteral("committer: ").length()); 0144 commitInfo.setAuthor(commiter); // Author goes after committer, but only if is different 0145 } else if (line.startsWith(QLatin1String("author"))) { 0146 QString author = line.mid(QStringLiteral("author: ").length()); 0147 commitInfo.setAuthor(author); // It may override committer (In fact committer is not supported by VcsEvent) 0148 } else if (line.startsWith(QLatin1String("timestamp"))) { 0149 const QString formatString = QStringLiteral("yyyy-MM-dd hh:mm:ss"); 0150 QString timestamp = line.mid(QStringLiteral("timestamp: ddd ").length(), formatString.length()); 0151 commitInfo.setDate(QDateTime::fromString(timestamp, formatString)); 0152 } else if (line.startsWith(QLatin1String("message"))) { 0153 atMessage = true; 0154 } 0155 } else { 0156 message += line.trimmed() + QLatin1Char('\n'); 0157 } 0158 } 0159 if (atMessage) 0160 commitInfo.setMessage(message.trimmed()); 0161 Q_ASSERT(revision!=-1); 0162 m_commits[revision] = commitInfo; 0163 // Invoke from event loop to protect against stack overflow (it could happen 0164 // on very big files with very big history of changes if tail-recursion 0165 // optimization had failed here). 0166 QTimer::singleShot(0, this, &BzrAnnotateJob::parseNextLine); 0167 } 0168 0169 QVariant BzrAnnotateJob::fetchResults() 0170 { 0171 return m_results; 0172 } 0173 0174 KDevelop::VcsJob::JobStatus BzrAnnotateJob::status() const 0175 { 0176 return m_status; 0177 } 0178 0179 KDevelop::IPlugin* BzrAnnotateJob::vcsPlugin() const 0180 { 0181 return m_vcsPlugin; 0182 } 0183 0184 #include "moc_bzrannotatejob.cpp"