File indexing completed on 2024-05-05 16:46:07
0001 /* 0002 SPDX-FileCopyrightText: 2010 Morten Danielsen Volden <mvolden2@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "perforceplugin.h" 0008 #include "ui/perforceimportmetadatawidget.h" 0009 #include "debug.h" 0010 0011 #include <KLocalizedString> 0012 #include <QFileInfo> 0013 #include <QDateTime> 0014 #include <QDir> 0015 #include <QProcessEnvironment> 0016 #include <QMenu> 0017 0018 #include <KMessageBox> 0019 #include <vcs/vcsjob.h> 0020 #include <vcs/vcsrevision.h> 0021 #include <vcs/vcsstatusinfo.h> 0022 #include <vcs/vcsevent.h> 0023 #include <vcs/dvcs/dvcsjob.h> 0024 #include <vcs/vcsannotation.h> 0025 #include <vcs/widgets/standardvcslocationwidget.h> 0026 0027 #include <interfaces/context.h> 0028 #include <interfaces/contextmenuextension.h> 0029 #include <interfaces/icore.h> 0030 #include <interfaces/iruncontroller.h> 0031 0032 #include <vcs/vcspluginhelper.h> 0033 0034 using namespace KDevelop; 0035 0036 namespace 0037 { 0038 QString toRevisionName(const KDevelop::VcsRevision& rev, const QString& currentRevision=QString()) 0039 { 0040 bool ok; 0041 int previous = currentRevision.toInt(&ok); 0042 previous--; 0043 QString tmp; 0044 switch(rev.revisionType()) { 0045 case VcsRevision::Special: 0046 switch(rev.revisionValue().value<VcsRevision::RevisionSpecialType>()) { 0047 case VcsRevision::Head: 0048 return QStringLiteral("#head"); 0049 case VcsRevision::Base: 0050 return QStringLiteral("#have"); 0051 case VcsRevision::Working: 0052 return QStringLiteral("#have"); 0053 case VcsRevision::Previous: 0054 Q_ASSERT(!currentRevision.isEmpty()); 0055 tmp.setNum(previous); 0056 tmp.prepend(QLatin1Char('#')); 0057 return tmp; 0058 case VcsRevision::Start: 0059 return QString(); 0060 case VcsRevision::UserSpecialType: //Not used 0061 Q_ASSERT(false && "i don't know how to do that"); 0062 } 0063 break; 0064 case VcsRevision::GlobalNumber: 0065 tmp.append(QLatin1Char('#') + rev.revisionValue().toString()); 0066 return tmp; 0067 case VcsRevision::Date: 0068 case VcsRevision::FileNumber: 0069 case VcsRevision::Invalid: 0070 case VcsRevision::UserType: 0071 Q_ASSERT(false); 0072 } 0073 return QString(); 0074 } 0075 0076 0077 VcsItemEvent::Actions actionsFromString(QString const& changeDescription) 0078 { 0079 if(changeDescription == QLatin1String("add")) 0080 return VcsItemEvent::Added; 0081 if(changeDescription == QLatin1String("delete")) 0082 return VcsItemEvent::Deleted; 0083 return VcsItemEvent::Modified; 0084 } 0085 0086 QDir urlDir(const QUrl& url) 0087 { 0088 QFileInfo f(url.toLocalFile()); 0089 if(f.isDir()) 0090 return QDir(url.toLocalFile()); 0091 else 0092 return f.absoluteDir(); 0093 } 0094 0095 } 0096 0097 PerforcePlugin::PerforcePlugin(QObject* parent, const QVariantList&): 0098 KDevelop::IPlugin(QStringLiteral("kdevperforce"), parent) 0099 , m_common(new KDevelop::VcsPluginHelper(this, this)) 0100 , m_perforceConfigName(QStringLiteral("p4config.txt")) 0101 , m_perforceExecutable(QStringLiteral("p4")) 0102 , m_edit_action(nullptr) 0103 { 0104 QProcessEnvironment currentEviron(QProcessEnvironment::systemEnvironment()); 0105 QString tmp(currentEviron.value(QStringLiteral("P4CONFIG"))); 0106 if (tmp.isEmpty()) { 0107 // We require the P4CONFIG variable to be set because the perforce command line client will need it 0108 setErrorDescription(i18n("The variable P4CONFIG is not set. Is perforce installed on the system?")); 0109 return; 0110 } else { 0111 m_perforceConfigName = tmp; 0112 } 0113 qCDebug(PLUGIN_PERFORCE) << "The value of P4CONFIG is : " << tmp; 0114 } 0115 0116 PerforcePlugin::~PerforcePlugin() 0117 { 0118 } 0119 0120 QString PerforcePlugin::name() const 0121 { 0122 return i18n("Perforce"); 0123 } 0124 0125 KDevelop::VcsImportMetadataWidget* PerforcePlugin::createImportMetadataWidget(QWidget* parent) 0126 { 0127 return new PerforceImportMetadataWidget(parent); 0128 } 0129 0130 bool PerforcePlugin::isValidRemoteRepositoryUrl(const QUrl& remoteLocation) 0131 { 0132 Q_UNUSED(remoteLocation); 0133 // TODO 0134 return false; 0135 } 0136 0137 bool PerforcePlugin::isValidDirectory(const QUrl & dirPath) 0138 { 0139 const QFileInfo finfo(dirPath.toLocalFile()); 0140 QDir dir = finfo.isDir() ? QDir(dirPath.toLocalFile()) : finfo.absoluteDir(); 0141 0142 do { 0143 if (dir.exists(m_perforceConfigName)) { 0144 return true; 0145 } 0146 } while (dir.cdUp()); 0147 return false; 0148 } 0149 0150 bool PerforcePlugin::isVersionControlled(const QUrl& localLocation) 0151 { 0152 QFileInfo fsObject(localLocation.toLocalFile()); 0153 if (fsObject.isDir()) { 0154 return isValidDirectory(localLocation); 0155 } 0156 return parseP4fstat(fsObject, KDevelop::OutputJob::Silent); 0157 } 0158 0159 DVcsJob* PerforcePlugin::p4fstatJob(const QFileInfo& curFile, OutputJob::OutputJobVerbosity verbosity) 0160 { 0161 auto* job = new DVcsJob(curFile.absolutePath(), this, verbosity); 0162 setEnvironmentForJob(job, curFile); 0163 *job << m_perforceExecutable << "fstat" << curFile.fileName(); 0164 return job; 0165 } 0166 0167 bool PerforcePlugin::parseP4fstat(const QFileInfo& curFile, OutputJob::OutputJobVerbosity verbosity) 0168 { 0169 QScopedPointer<DVcsJob> job(p4fstatJob(curFile, verbosity)); 0170 if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) { 0171 qCDebug(PLUGIN_PERFORCE) << "Perforce returned: " << job->output(); 0172 if (!job->output().isEmpty()) 0173 return true; 0174 } 0175 return false; 0176 } 0177 0178 QString PerforcePlugin::getRepositoryName(const QFileInfo& curFile) 0179 { 0180 const QString DEPOT_FILE_STR(QStringLiteral("... depotFile ")); 0181 QString ret; 0182 QScopedPointer<DVcsJob> job(p4fstatJob(curFile, KDevelop::OutputJob::Silent)); 0183 if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) { 0184 if (!job->output().isEmpty()) { 0185 const QStringList outputLines = job->output().split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0186 for (const QString& line : outputLines) { 0187 int idx(line.indexOf(DEPOT_FILE_STR)); 0188 if (idx != -1) { 0189 ret = line.mid(DEPOT_FILE_STR.size()); 0190 return ret; 0191 } 0192 } 0193 } 0194 } 0195 0196 return ret; 0197 } 0198 0199 KDevelop::VcsJob* PerforcePlugin::repositoryLocation(const QUrl& /*localLocation*/) 0200 { 0201 return nullptr; 0202 } 0203 0204 KDevelop::VcsJob* PerforcePlugin::add(const QList<QUrl>& localLocations, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0205 { 0206 QFileInfo curFile(localLocations.front().toLocalFile()); 0207 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0208 setEnvironmentForJob(job, curFile); 0209 *job << m_perforceExecutable << "add" << localLocations; 0210 0211 return job; 0212 } 0213 0214 KDevelop::VcsJob* PerforcePlugin::remove(const QList<QUrl>& /*localLocations*/) 0215 { 0216 return nullptr; 0217 } 0218 0219 KDevelop::VcsJob* PerforcePlugin::copy(const QUrl& /*localLocationSrc*/, const QUrl& /*localLocationDstn*/) 0220 { 0221 return nullptr; 0222 } 0223 0224 KDevelop::VcsJob* PerforcePlugin::move(const QUrl& /*localLocationSrc*/, const QUrl& /*localLocationDst*/) 0225 { 0226 return nullptr; 0227 } 0228 0229 KDevelop::VcsJob* PerforcePlugin::status(const QList<QUrl>& localLocations, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0230 { 0231 if (localLocations.count() != 1) { 0232 KMessageBox::error(nullptr, i18n("Please select only one item for this operation")); 0233 return nullptr; 0234 } 0235 0236 QFileInfo curFile(localLocations.front().toLocalFile()); 0237 0238 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0239 setEnvironmentForJob(job, curFile); 0240 *job << m_perforceExecutable << "fstat" << curFile.fileName(); 0241 connect(job, &DVcsJob::readyForParsing, this, &PerforcePlugin::parseP4StatusOutput); 0242 0243 return job; 0244 } 0245 0246 KDevelop::VcsJob* PerforcePlugin::revert(const QList<QUrl>& localLocations, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0247 { 0248 if (localLocations.count() != 1) { 0249 KMessageBox::error(nullptr, i18n("Please select only one item for this operation")); 0250 return nullptr; 0251 } 0252 0253 QFileInfo curFile(localLocations.front().toLocalFile()); 0254 0255 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0256 setEnvironmentForJob(job, curFile); 0257 *job << m_perforceExecutable << "revert" << curFile.fileName(); 0258 0259 return job; 0260 0261 } 0262 0263 KDevelop::VcsJob* PerforcePlugin::update(const QList<QUrl>& localLocations, const KDevelop::VcsRevision& /*rev*/, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0264 { 0265 QFileInfo curFile(localLocations.front().toLocalFile()); 0266 0267 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0268 setEnvironmentForJob(job, curFile); 0269 //*job << m_perforceExecutable << "-p" << "127.0.0.1:1666" << "info"; - Let's keep this for now it's very handy for debugging 0270 QString fileOrDirectory; 0271 if (curFile.isDir()) 0272 fileOrDirectory = curFile.absolutePath() + QLatin1String("/..."); 0273 else 0274 fileOrDirectory = curFile.fileName(); 0275 *job << m_perforceExecutable << "sync" << fileOrDirectory; 0276 return job; 0277 } 0278 0279 KDevelop::VcsJob* PerforcePlugin::commit(const QString& message, const QList<QUrl>& localLocations, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0280 { 0281 if (localLocations.empty() || message.isEmpty()) 0282 return errorsFound(i18n("No files or message specified")); 0283 0284 0285 QFileInfo curFile(localLocations.front().toLocalFile()); 0286 0287 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0288 setEnvironmentForJob(job, curFile); 0289 *job << m_perforceExecutable << "submit" << "-d" << message << localLocations; 0290 0291 return job; 0292 } 0293 0294 KDevelop::VcsJob* PerforcePlugin::diff(const QUrl& fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0295 { 0296 QFileInfo curFile(fileOrDirectory.toLocalFile()); 0297 QString depotSrcFileName = getRepositoryName(curFile); 0298 QString depotDstFileName = depotSrcFileName; 0299 depotSrcFileName.append(toRevisionName(srcRevision, dstRevision.prettyValue())); // dstRevision actually contains the number that we want to take previous of 0300 0301 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0302 setEnvironmentForJob(job, curFile); 0303 switch (dstRevision.revisionType()) { 0304 case VcsRevision::FileNumber: 0305 case VcsRevision::GlobalNumber: 0306 depotDstFileName.append(QLatin1Char('#') + dstRevision.prettyValue()); 0307 *job << m_perforceExecutable << "diff2" << "-u" << depotSrcFileName << depotDstFileName; 0308 break; 0309 case VcsRevision::Special: 0310 switch (dstRevision.revisionValue().value<VcsRevision::RevisionSpecialType>()) { 0311 case VcsRevision::Working: 0312 *job << m_perforceExecutable << "diff" << "-du" << depotSrcFileName; 0313 break; 0314 case VcsRevision::Start: 0315 case VcsRevision::UserSpecialType: 0316 default: 0317 break; 0318 } 0319 break; 0320 default: 0321 break; 0322 } 0323 0324 connect(job, &DVcsJob::readyForParsing, this, &PerforcePlugin::parseP4DiffOutput); 0325 return job; 0326 } 0327 0328 KDevelop::VcsJob* PerforcePlugin::log(const QUrl& localLocation, const KDevelop::VcsRevision& rev, long unsigned int limit) 0329 { 0330 static QString lastSeenChangeList; 0331 QFileInfo curFile(localLocation.toLocalFile()); 0332 QString localLocationAndRevStr = localLocation.toLocalFile(); 0333 0334 auto* job = new DVcsJob(urlDir(localLocation), this, KDevelop::OutputJob::Verbose); 0335 setEnvironmentForJob(job, curFile); 0336 *job << m_perforceExecutable << "filelog" << "-lit"; 0337 if(limit > 0) 0338 *job << QStringLiteral("-m %1").arg(limit); 0339 0340 if (curFile.isDir()) { 0341 localLocationAndRevStr.append(QLatin1String("/...")); 0342 } 0343 QString revStr = toRevisionName(rev, QString()); 0344 if(!revStr.isEmpty()) { 0345 // This is not too nice, but perforce argument for restricting output from filelog does not Work :-( 0346 // So putting this in so we do not end up in infinite loop calling log, 0347 if(revStr == lastSeenChangeList) { 0348 localLocationAndRevStr.append(QLatin1String("#none")); 0349 lastSeenChangeList.clear(); 0350 } else { 0351 localLocationAndRevStr.append(revStr); 0352 lastSeenChangeList = revStr; 0353 } 0354 } 0355 *job << localLocationAndRevStr; 0356 qCDebug(PLUGIN_PERFORCE) << "Issuing the following command to p4: " << job->dvcsCommand(); 0357 connect(job, &DVcsJob::readyForParsing, this, &PerforcePlugin::parseP4LogOutput); 0358 return job; 0359 } 0360 0361 KDevelop::VcsJob* PerforcePlugin::log(const QUrl& localLocation, const KDevelop::VcsRevision& /*rev*/, const KDevelop::VcsRevision& /*limit*/) 0362 { 0363 QFileInfo curFile(localLocation.toLocalFile()); 0364 if (curFile.isDir()) { 0365 KMessageBox::error(nullptr, i18n("Please select a file for this operation")); 0366 return errorsFound(i18n("Directory not supported for this operation")); 0367 } 0368 0369 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0370 setEnvironmentForJob(job, curFile); 0371 *job << m_perforceExecutable << "filelog" << "-lit" << localLocation; 0372 0373 connect(job, &DVcsJob::readyForParsing, this , &PerforcePlugin::parseP4LogOutput); 0374 return job; 0375 } 0376 0377 KDevelop::VcsJob* PerforcePlugin::annotate(const QUrl& localLocation, const KDevelop::VcsRevision& /*rev*/) 0378 { 0379 QFileInfo curFile(localLocation.toLocalFile()); 0380 if (curFile.isDir()) { 0381 KMessageBox::error(nullptr, i18n("Please select a file for this operation")); 0382 return errorsFound(i18n("Directory not supported for this operation")); 0383 } 0384 0385 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0386 setEnvironmentForJob(job, curFile); 0387 *job << m_perforceExecutable << "annotate" << "-qi" << localLocation; 0388 0389 connect(job, &DVcsJob::readyForParsing, this , &PerforcePlugin::parseP4AnnotateOutput); 0390 return job; 0391 } 0392 0393 KDevelop::VcsJob* PerforcePlugin::resolve(const QList<QUrl>& /*localLocations*/, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0394 { 0395 return nullptr; 0396 } 0397 0398 KDevelop::VcsJob* PerforcePlugin::createWorkingCopy(const KDevelop::VcsLocation& /*sourceRepository*/, const QUrl& /*destinationDirectory*/, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) 0399 { 0400 return nullptr; 0401 } 0402 0403 KDevelop::VcsLocationWidget* PerforcePlugin::vcsLocation(QWidget* parent) const 0404 { 0405 return new StandardVcsLocationWidget(parent); 0406 } 0407 0408 0409 KDevelop::VcsJob* PerforcePlugin::edit(const QList<QUrl>& localLocations) 0410 { 0411 QFileInfo curFile(localLocations.front().toLocalFile()); 0412 0413 auto* job = new DVcsJob(curFile.dir(), this, KDevelop::OutputJob::Verbose); 0414 setEnvironmentForJob(job, curFile); 0415 *job << m_perforceExecutable << "edit" << localLocations; 0416 0417 return job; 0418 } 0419 0420 KDevelop::VcsJob* PerforcePlugin::edit(const QUrl& /*localLocation*/) 0421 { 0422 return nullptr; 0423 } 0424 0425 KDevelop::VcsJob* PerforcePlugin::unedit(const QUrl& /*localLocation*/) 0426 { 0427 return nullptr; 0428 } 0429 0430 KDevelop::VcsJob* PerforcePlugin::localRevision(const QUrl& /*localLocation*/, KDevelop::VcsRevision::RevisionType) 0431 { 0432 return nullptr; 0433 } 0434 0435 KDevelop::VcsJob* PerforcePlugin::import(const QString& /*commitMessage*/, const QUrl& /*sourceDirectory*/, const KDevelop::VcsLocation& /*destinationRepository*/) 0436 { 0437 return nullptr; 0438 } 0439 0440 KDevelop::ContextMenuExtension PerforcePlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) 0441 { 0442 m_common->setupFromContext(context); 0443 0444 const QList<QUrl> & ctxUrlList = m_common->contextUrlList(); 0445 0446 bool hasVersionControlledEntries = false; 0447 for( const QUrl& url : ctxUrlList) { 0448 if (isValidDirectory(url)) { 0449 hasVersionControlledEntries = true; 0450 break; 0451 } 0452 } 0453 0454 if (!hasVersionControlledEntries) 0455 return IPlugin::contextMenuExtension(context, parent); 0456 0457 QMenu * perforceMenu = m_common->commonActions(parent); 0458 perforceMenu->addSeparator(); 0459 0460 perforceMenu->addSeparator(); 0461 if (!m_edit_action) { 0462 m_edit_action = new QAction(i18nc("@action::inmenu", "Edit"), this); 0463 connect(m_edit_action, &QAction::triggered, this, & PerforcePlugin::ctxEdit); 0464 } 0465 perforceMenu->addAction(m_edit_action); 0466 0467 ContextMenuExtension menuExt; 0468 menuExt.addAction(ContextMenuExtension::VcsGroup, perforceMenu->menuAction()); 0469 0470 return menuExt; 0471 } 0472 0473 void PerforcePlugin::ctxEdit() 0474 { 0475 QList<QUrl> const & ctxUrlList = m_common->contextUrlList(); 0476 KDevelop::ICore::self()->runController()->registerJob(edit(ctxUrlList)); 0477 } 0478 0479 void PerforcePlugin::setEnvironmentForJob(DVcsJob* job, const QFileInfo& curFile) 0480 { 0481 KProcess* jobproc = job->process(); 0482 jobproc->setEnv(QStringLiteral("P4CONFIG"), m_perforceConfigName); 0483 if (curFile.isDir()) { 0484 jobproc->setEnv(QStringLiteral("PWD"), curFile.filePath()); 0485 } else { 0486 jobproc->setEnv(QStringLiteral("PWD"), curFile.absolutePath()); 0487 } 0488 } 0489 0490 QList<QVariant> PerforcePlugin::getQvariantFromLogOutput(QStringList const& outputLines) 0491 { 0492 const QString LOGENTRY_START(QStringLiteral("... #")); 0493 const QString DEPOTMESSAGE_START(QStringLiteral("... .")); 0494 QMap<int, VcsEvent> changes; 0495 QList<QVariant> commits; 0496 QString currentFileName; 0497 QString changeNumberStr, author,changeDescription, commitMessage; 0498 VcsEvent currentVcsEvent; 0499 VcsItemEvent currentRepoFile; 0500 VcsRevision rev; 0501 int indexofAt; 0502 int changeNumber = 0; 0503 0504 for (const QString& line : outputLines) { 0505 if (!line.startsWith(LOGENTRY_START) && !line.startsWith(DEPOTMESSAGE_START) 0506 && !line.startsWith(QLatin1Char('\t'))) { 0507 currentFileName = line; 0508 } 0509 if(line.indexOf(LOGENTRY_START) != -1) 0510 { 0511 // expecting the Logentry line to be of the form: 0512 //... #5 change 10 edit on 2010/12/06 12:07:31 by mvo@testbed (text) 0513 changeNumberStr = line.section(QLatin1Char(' '), 3, 3); // We use global change number 0514 changeNumber = changeNumberStr.toInt(); 0515 author = line.section(QLatin1Char(' '), 9, 9); 0516 changeDescription = line.section(QLatin1Char(' '), 4, 4); 0517 indexofAt = author.indexOf(QLatin1Char('@')); 0518 author.remove(indexofAt, author.size()); // Only keep the username itself 0519 rev.setRevisionValue(changeNumberStr, KDevelop::VcsRevision::GlobalNumber); 0520 0521 changes[changeNumber].setRevision(rev); 0522 changes[changeNumber].setAuthor(author); 0523 changes[changeNumber].setDate( 0524 QDateTime::fromString(line.section(QLatin1Char(' '), 6, 7), QStringLiteral("yyyy/MM/dd hh:mm:ss"))); 0525 currentRepoFile.setRepositoryLocation(currentFileName); 0526 currentRepoFile.setActions( actionsFromString(changeDescription) ); 0527 changes[changeNumber].addItem(currentRepoFile); 0528 commitMessage.clear(); // We have a new entry, clear message 0529 } 0530 if (line.startsWith(QLatin1Char('\t')) || line.startsWith(DEPOTMESSAGE_START)) { 0531 commitMessage += line.trimmed() + QLatin1Char('\n'); 0532 changes[changeNumber].setMessage(commitMessage); 0533 } 0534 } 0535 0536 for(const auto& item : qAsConst(changes)) { 0537 commits.prepend(QVariant::fromValue(item)); 0538 } 0539 return commits; 0540 } 0541 0542 void PerforcePlugin::parseP4StatusOutput(DVcsJob* job) 0543 { 0544 const QStringList outputLines = job->output().split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0545 QVariantList statuses; 0546 const QString ACTION_STR(QStringLiteral("... action ")); 0547 const QString CLIENT_FILE_STR(QStringLiteral("... clientFile ")); 0548 0549 0550 0551 VcsStatusInfo status; 0552 status.setState(VcsStatusInfo::ItemUserState); 0553 for (const QString& line : outputLines) { 0554 int idx(line.indexOf(ACTION_STR)); 0555 if (idx != -1) { 0556 QString curr = line.mid(ACTION_STR.size()); 0557 0558 if (curr == QLatin1String("edit")) { 0559 status.setState(VcsStatusInfo::ItemModified); 0560 } else if (curr == QLatin1String("add")) { 0561 status.setState(VcsStatusInfo::ItemAdded); 0562 } else { 0563 status.setState(VcsStatusInfo::ItemUserState); 0564 } 0565 continue; 0566 } 0567 idx = line.indexOf(CLIENT_FILE_STR); 0568 if (idx != -1) { 0569 QUrl fileUrl = QUrl::fromLocalFile(line.mid(CLIENT_FILE_STR.size())); 0570 0571 status.setUrl(fileUrl); 0572 } 0573 } 0574 statuses.append(QVariant::fromValue<VcsStatusInfo>(status)); 0575 job->setResults(statuses); 0576 } 0577 0578 void PerforcePlugin::parseP4LogOutput(KDevelop::DVcsJob* job) 0579 { 0580 QList<QVariant> commits(getQvariantFromLogOutput(job->output().split(QLatin1Char('\n'), Qt::SkipEmptyParts))); 0581 0582 job->setResults(commits); 0583 } 0584 0585 void PerforcePlugin::parseP4DiffOutput(DVcsJob* job) 0586 { 0587 VcsDiff diff; 0588 diff.setDiff(job->output()); 0589 QDir dir(job->directory()); 0590 0591 do { 0592 if (dir.exists(m_perforceConfigName)) { 0593 break; 0594 } 0595 } while (dir.cdUp()); 0596 0597 diff.setBaseDiff(QUrl::fromLocalFile(dir.absolutePath())); 0598 0599 job->setResults(QVariant::fromValue(diff)); 0600 } 0601 0602 void PerforcePlugin::parseP4AnnotateOutput(DVcsJob *job) 0603 { 0604 QVariantList results; 0605 /// First get the changelists for this file 0606 QStringList strList(job->dvcsCommand()); 0607 QString localLocation(strList.last()); /// ASSUMPTION WARNING - localLocation is the last in the annotate command 0608 KDevelop::VcsRevision dummyRev; 0609 QScopedPointer<DVcsJob> logJob(new DVcsJob(job->directory(), this, OutputJob::Silent)); 0610 QFileInfo curFile(localLocation); 0611 setEnvironmentForJob(logJob.data(), curFile); 0612 *logJob << m_perforceExecutable << "filelog" << "-lit" << localLocation; 0613 0614 QList<QVariant> commits; 0615 if (logJob->exec() && logJob->status() == KDevelop::VcsJob::JobSucceeded) { 0616 if (!job->output().isEmpty()) { 0617 commits = getQvariantFromLogOutput(logJob->output().split(QLatin1Char('\n'), Qt::SkipEmptyParts)); 0618 } 0619 } 0620 0621 VcsEvent item; 0622 QMap<qlonglong, VcsEvent> globalCommits; 0623 /// Move the VcsEvents to a more suitable data structure 0624 for (auto& commit : qAsConst(commits)) { 0625 if (commit.canConvert<VcsEvent>()) { 0626 item = commit.value<VcsEvent>(); 0627 } 0628 globalCommits.insert(item.revision().revisionValue().toLongLong(), item); 0629 } 0630 0631 const QStringList lines = job->output().split(QLatin1Char('\n')); 0632 0633 int lineNumber = 0; 0634 QMap<qlonglong, VcsEvent>::iterator currentEvent; 0635 bool convertToIntOk(false); 0636 int globalRevisionInt(0); 0637 QString globalRevision; 0638 for (auto& line : lines) { 0639 if (line.isEmpty()) { 0640 continue; 0641 } 0642 0643 globalRevision = line.left(line.indexOf(QLatin1Char(':'))); 0644 0645 VcsAnnotationLine annotation; 0646 annotation.setLineNumber(lineNumber); 0647 VcsRevision rev; 0648 rev.setRevisionValue(globalRevision, KDevelop::VcsRevision::GlobalNumber); 0649 annotation.setRevision(rev); 0650 // Find the other info in the commits list 0651 globalRevisionInt = globalRevision.toLongLong(&convertToIntOk); 0652 if(convertToIntOk) 0653 { 0654 currentEvent = globalCommits.find(globalRevisionInt); 0655 annotation.setAuthor(currentEvent->author()); 0656 annotation.setCommitMessage(currentEvent->message()); 0657 annotation.setDate(currentEvent->date()); 0658 } 0659 0660 results += QVariant::fromValue(annotation); 0661 ++lineNumber; 0662 } 0663 0664 job->setResults(results); 0665 } 0666 0667 0668 KDevelop::VcsJob* PerforcePlugin::errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity) 0669 { 0670 auto* j = new DVcsJob(QDir::temp(), this, verbosity); 0671 *j << "echo" << i18n("error: %1", error) << "-n"; 0672 return j; 0673 } 0674 0675 #include "moc_perforceplugin.cpp"