diff --git a/src/plugins/mercurial/mercurial.pro b/src/plugins/mercurial/mercurial.pro index f532f20471d..20a1f94ceb9 100644 --- a/src/plugins/mercurial/mercurial.pro +++ b/src/plugins/mercurial/mercurial.pro @@ -6,7 +6,6 @@ SOURCES += mercurialplugin.cpp \ optionspage.cpp \ mercurialcontrol.cpp \ mercurialclient.cpp \ - mercurialjobrunner.cpp \ annotationhighlighter.cpp \ mercurialeditor.cpp \ revertdialog.cpp \ @@ -21,7 +20,6 @@ HEADERS += mercurialplugin.h \ optionspage.h \ mercurialcontrol.h \ mercurialclient.h \ - mercurialjobrunner.h \ annotationhighlighter.h \ mercurialeditor.h \ revertdialog.h \ diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index e8b8c7561f9..05e70c9e507 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -32,81 +32,25 @@ **************************************************************************/ #include "mercurialclient.h" -#include "mercurialjobrunner.h" #include "constants.h" -#include "mercurialsettings.h" -#include "mercurialplugin.h" -#include -#include - -#include -#include -#include #include +#include +#include +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Q_DECLARE_METATYPE(QVariant) - -inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property, const QString &entry) -{ - foreach (Core::IEditor *ed, core->editorManager()->openedEditors()) - if (ed->file()->property(property).toString() == entry) - return ed; - return 0; -} - -static const char nonInteractiveOptionC[] = "--noninteractive"; +#include +#include +#include +#include +#include namespace Mercurial { namespace Internal { -MercurialClient::MercurialClient() : - jobManager(0), - core(Core::ICore::instance()) +MercurialClient::MercurialClient(const VCSBase::VCSBaseClientSettings &settings) : + VCSBase::VCSBaseClient(settings) { - qRegisterMetaType(); -} - -MercurialClient::~MercurialClient() -{ - if (jobManager) { - delete jobManager; - jobManager = 0; - } -} - -bool MercurialClient::add(const QString &workingDir, const QString &filename) -{ - QStringList args; - args << QLatin1String("add") << filename; - QByteArray stdOut; - return executeHgFullySynchronously(workingDir, args, &stdOut); -} - -bool MercurialClient::remove(const QString &workingDir, const QString &filename) -{ - QStringList args; - args << QLatin1String("remove") << filename; - QByteArray stdOut; - return executeHgFullySynchronously(workingDir, args, &stdOut); -} - -bool MercurialClient::move(const QString &workingDir, const QString &from, const QString &to) -{ - QStringList args; - args << QLatin1String("rename") << from << to; - QByteArray stdOut; - return executeHgFullySynchronously(workingDir, args, &stdOut); } bool MercurialClient::manifestSync(const QString &repository, const QString &relativeFilename) @@ -115,7 +59,7 @@ bool MercurialClient::manifestSync(const QString &repository, const QString &rel const QStringList args(QLatin1String("manifest")); QByteArray output; - executeHgFullySynchronously(repository, args, &output); + vcsFullySynchronousExec(repository, args, &output); const QDir repositoryDir(repository); const QFileInfo needle = QFileInfo(repositoryDir, relativeFilename); @@ -128,62 +72,62 @@ bool MercurialClient::manifestSync(const QString &repository, const QString &rel return false; } -Utils::SynchronousProcessResponse - MercurialClient::executeHgSynchronously(const QString &workingDirectory, - const QStringList &hgArgs, - unsigned flags, - QTextCodec *outputCodec) +//bool MercurialClient::clone(const QString &directory, const QString &url) +bool MercurialClient::synchronousClone(const QString &workingDir, + const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions) { - const MercurialSettings &settings = MercurialPlugin::instance()->settings(); - const QString binary = settings.binary(); - const QStringList arguments = settings.standardArguments() + hgArgs; - return VCSBase::VCSBasePlugin::runVCS(workingDirectory, binary, arguments, - settings.timeoutMilliSeconds(), - flags, outputCodec); -} + Q_UNUSED(workingDir); + Q_UNUSED(extraOptions); + QDir workingDirectory(srcLocation); + QByteArray output; + const unsigned flags = VCSBase::VCSBasePlugin::SshPasswordPrompt | + VCSBase::VCSBasePlugin::ShowStdOutInLogWindow | + VCSBase::VCSBasePlugin::ShowSuccessMessage; -bool MercurialClient::executeHgFullySynchronously(const QString &workingDir, - const QStringList &args, - QByteArray *output) const -{ - QProcess hgProcess; - if (!workingDir.isEmpty()) - hgProcess.setWorkingDirectory(workingDir); - MercurialJobRunner::setProcessEnvironment(hgProcess); + if (workingDirectory.exists()) { + // Let's make first init + QStringList arguments(QLatin1String("init")); + if (!vcsFullySynchronousExec(workingDirectory.path(), arguments, &output)) { + return false; + } - const MercurialSettings &settings = MercurialPlugin::instance()->settings(); - const QString binary = settings.binary(); - const QStringList arguments = settings.standardArguments() + args; + // Then pull remote repository + arguments.clear(); + arguments << QLatin1String("pull") << dstLocation; + const Utils::SynchronousProcessResponse resp1 = + vcsSynchronousExec(workingDirectory.path(), arguments, flags); + if (resp1.result != Utils::SynchronousProcessResponse::Finished) { + return false; + } - VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); - outputWindow->appendCommand(workingDir, binary, args); + // By now, there is no hgrc file -> create it + QFile hgrc(workingDirectory.path()+"/.hg/hgrc"); + hgrc.open(QIODevice::WriteOnly); + hgrc.write(QString("[paths]\ndefault = %1\n").arg(dstLocation).toUtf8()); + hgrc.close(); - hgProcess.start(binary, arguments); - - if (!hgProcess.waitForStarted()) { - outputWindow->appendError(MercurialJobRunner::msgStartFailed(binary, hgProcess.errorString())); - return false; + // And last update repository + arguments.clear(); + arguments << QLatin1String("update"); + const Utils::SynchronousProcessResponse resp2 = + vcsSynchronousExec(workingDirectory.path(), arguments, flags); + return resp2.result == Utils::SynchronousProcessResponse::Finished; + } else { + QStringList arguments(QLatin1String("clone")); + arguments << dstLocation << workingDirectory.dirName(); + workingDirectory.cdUp(); + const Utils::SynchronousProcessResponse resp = + vcsSynchronousExec(workingDirectory.path(), arguments, flags); + return resp.result == Utils::SynchronousProcessResponse::Finished; } - - hgProcess.closeWriteChannel(); - - QByteArray stdErr; - if (!Utils::SynchronousProcess::readDataFromProcess(hgProcess, settings.timeoutMilliSeconds(), - output, &stdErr, true)) { - Utils::SynchronousProcess::stopProcess(hgProcess); - outputWindow->appendError(MercurialJobRunner::msgTimeout(settings.timeoutSeconds())); - return false; - } - if (!stdErr.isEmpty()) - outputWindow->append(QString::fromLocal8Bit(stdErr)); - - return hgProcess.exitStatus() == QProcess::NormalExit && hgProcess.exitCode() == 0; } QString MercurialClient::branchQuerySync(const QString &repositoryRoot) { QByteArray output; - if (executeHgFullySynchronously(repositoryRoot, QStringList(QLatin1String("branch")), &output)) + if (vcsFullySynchronousExec(repositoryRoot, QStringList(QLatin1String("branch")), &output)) return QTextCodec::codecForLocale()->toUnicode(output).trimmed(); return QLatin1String("Unknown Branch"); @@ -213,7 +157,7 @@ bool MercurialClient::parentRevisionsSync(const QString &workingDirectory, if (!file.isEmpty()) args << file; QByteArray outputData; - if (!executeHgFullySynchronously(workingDirectory, args, &outputData)) + if (!vcsFullySynchronousExec(workingDirectory, args, &outputData)) return false; QString output = QString::fromLocal8Bit(outputData); output.remove(QLatin1Char('\r')); @@ -257,7 +201,7 @@ bool MercurialClient::shortDescriptionSync(const QString &workingDirectory, if (!format.isEmpty()) args << QLatin1String("--template") << format; QByteArray outputData; - if (!executeHgFullySynchronously(workingDirectory, args, &outputData)) + if (!vcsFullySynchronousExec(workingDirectory, args, &outputData)) return false; *description = QString::fromLocal8Bit(outputData); description->remove(QLatin1Char('\r')); @@ -293,260 +237,6 @@ bool MercurialClient::shortDescriptionsSync(const QString &workingDirectory, con return true; } -void MercurialClient::slotAnnotateRevisionRequested(const QString &source, QString change, int lineNumber) -{ - // This might be invoked with a verbose revision description - // "SHA1 author subject" from the annotation context menu. Strip the rest. - const int blankPos = change.indexOf(QLatin1Char(' ')); - if (blankPos != -1) - change.truncate(blankPos); - const QFileInfo fi(source); - annotate(fi.absolutePath(), fi.fileName(), change, lineNumber); -} - -void MercurialClient::annotate(const QString &workingDir, const QString &file, - const QString revision /* = QString() */, - int lineNumber /* = -1 */) -{ - Q_UNUSED(lineNumber) - QStringList args; - args << QLatin1String("annotate") << QLatin1String("-u") << QLatin1String("-c") << QLatin1String("-d"); - if (!revision.isEmpty()) - args << QLatin1String("-r") << revision; - args << file; - const QString kind = QLatin1String(Constants::ANNOTATELOG); - const QString id = VCSBase::VCSBaseEditorWidget::getSource(workingDir, QStringList(file)); - const QString title = tr("Hg Annotate %1").arg(id); - const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, file); - - VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, source, true, - "annotate", id); - - QSharedPointer job(new HgTask(workingDir, args, editor)); - enqueueJob(job); -} - -void MercurialClient::diff(const QString &workingDir, const QStringList &files) -{ - QStringList args; - args << QLatin1String("diff") << QLatin1String("-g") << QLatin1String("-p") - << QLatin1String("-U 8"); - if (!files.isEmpty()) - args.append(files); - - const QString kind = QLatin1String(Constants::DIFFLOG); - const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir,files); - const QString title = tr("Hg diff %1").arg(id); - const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files); - VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, source, true, - "diff", id); - editor->setDiffBaseDirectory(workingDir); - - QSharedPointer job(new HgTask(workingDir, args, editor)); - enqueueJob(job); -} - - -void MercurialClient::log(const QString &workingDir, const QStringList &files, - bool enableAnnotationContextMenu) -{ - QStringList args(QLatin1String("log")); - if (!files.empty()) - args.append(files); - - const QString kind = QLatin1String(Constants::FILELOG); - const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir,files); - const QString title = tr("Hg log %1").arg(id); - const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files); - - VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, workingDir, true, - "log", id); - editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu); - - QSharedPointer job(new HgTask(workingDir, args, editor)); - enqueueJob(job); -} - -void MercurialClient::revertFile(const QString &workingDir, - const QString &file, - const QString &revision) -{ - const QStringList cookieList(workingDir + QLatin1Char('/') + file); - revert(workingDir, file, revision, QVariant(cookieList)); -} - -void MercurialClient::revertRepository(const QString &workingDir, - const QString &revision) -{ - revert(workingDir, QLatin1String("--all"), revision, QVariant(workingDir)); -} - -void MercurialClient::revert(const QString &workingDir, - const QString &argument, - const QString &revision, - const QVariant &cookie) -{ - QStringList args(QLatin1String("revert")); - if (!revision.isEmpty()) - args << QLatin1String("-r") << revision; - args.append(argument); - - // Indicate repository change or file list - QSharedPointer job(new HgTask(workingDir, args, false, cookie)); - connect(job.data(), SIGNAL(succeeded(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); - enqueueJob(job); -} - -bool MercurialClient::createRepositorySync(const QString &workingDirectory) -{ - const QStringList args(QLatin1String("init")); - QByteArray outputData; - if (!executeHgFullySynchronously(workingDirectory, args, &outputData)) - return false; - QString output = QString::fromLocal8Bit(outputData); - output.remove(QLatin1Char('\r')); - VCSBase::VCSBaseOutputWindow::instance()->append(output); - return true; -} - -void MercurialClient::status(const QString &workingDir, const QString &file) -{ - QStringList args(QLatin1String("status")); - if (!file.isEmpty()) - args.append(file); - VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance(); - outwin->setRepository(workingDir); - QSharedPointer job(new HgTask(workingDir, args, false)); - connect(job.data(), SIGNAL(succeeded(QVariant)), outwin, SLOT(clearRepository()), - Qt::QueuedConnection); - enqueueJob(job); -} - -void MercurialClient::statusWithSignal(const QString &repositoryRoot) -{ - const QStringList args(QLatin1String("status")); - QSharedPointer job(new HgTask(repositoryRoot, args, true)); - connect(job.data(), SIGNAL(rawData(QByteArray)), - this, SLOT(statusParser(QByteArray))); - enqueueJob(job); -} - -void MercurialClient::statusParser(const QByteArray &data) -{ - QList > statusList; - - QStringList rawStatusList = QTextCodec::codecForLocale()->toUnicode(data).split(QLatin1Char('\n')); - - foreach (const QString &string, rawStatusList) { - QPair status; - - if (string.startsWith(QLatin1Char('M'))) - status.first = QLatin1String("Modified"); - else if (string.startsWith(QLatin1Char('A'))) - status.first = QLatin1String("Added"); - else if (string.startsWith(QLatin1Char('R'))) - status.first = QLatin1String("Removed"); - else if (string.startsWith(QLatin1Char('!'))) - status.first = QLatin1String("Deleted"); - else if (string.startsWith(QLatin1Char('?'))) - status.first = QLatin1String("Untracked"); - else - continue; - - //the status string should be similar to "M file_with_Changes" - //so just should take the file name part and store it - status.second = string.mid(2); - statusList.append(status); - } - - emit parsedStatus(statusList); -} - -void MercurialClient::import(const QString &repositoryRoot, const QStringList &files) -{ - QStringList args; - args << QLatin1String("import") << QLatin1String("--no-commit"); - args += files; - - QSharedPointer job(new HgTask(repositoryRoot, args, false)); - enqueueJob(job); -} - -bool MercurialClient::pullSync(const QString &repositoryRoot, const QString &repository) -{ - QStringList args(QLatin1String("pull")); - if (!repository.isEmpty()) - args.append(repository); - // Disable UNIX terminals to suppress SSH prompting. - const unsigned flags = VCSBase::VCSBasePlugin::SshPasswordPrompt|VCSBase::VCSBasePlugin::ShowStdOutInLogWindow - |VCSBase::VCSBasePlugin::ShowSuccessMessage; - const Utils::SynchronousProcessResponse resp = - executeHgSynchronously(repositoryRoot, args, flags); - const bool ok = resp.result == Utils::SynchronousProcessResponse::Finished; - if (ok) - emit changed(QVariant(repositoryRoot)); - return ok; -} - -bool MercurialClient::pushSync(const QString &repositoryRoot, const QString &repository) -{ - QStringList args(QLatin1String("push")); - if (!repository.isEmpty()) - args.append(repository); - // Disable UNIX terminals to suppress SSH prompting. - const unsigned flags = VCSBase::VCSBasePlugin::SshPasswordPrompt|VCSBase::VCSBasePlugin::ShowStdOutInLogWindow - |VCSBase::VCSBasePlugin::ShowSuccessMessage; - const Utils::SynchronousProcessResponse resp = - executeHgSynchronously(repositoryRoot, args, flags); - return resp.result == Utils::SynchronousProcessResponse::Finished; -} - -bool MercurialClient::clone(const QString &directory, const QString &url) -{ - QDir workingDirectory(directory); - QByteArray output; - const unsigned flags = VCSBase::VCSBasePlugin::SshPasswordPrompt | - VCSBase::VCSBasePlugin::ShowStdOutInLogWindow | - VCSBase::VCSBasePlugin::ShowSuccessMessage; - - if (workingDirectory.exists()) { - // Let's make first init - QStringList arguments(QLatin1String("init")); - if (!executeHgFullySynchronously(workingDirectory.path(), arguments, &output)) { - return false; - } - - // Then pull remote repository - arguments.clear(); - arguments << QLatin1String("pull") << url; - const Utils::SynchronousProcessResponse resp1 = - executeHgSynchronously(workingDirectory.path(), arguments, flags); - if (resp1.result != Utils::SynchronousProcessResponse::Finished) { - return false; - } - - // By now, there is no hgrc file -> create it - QFile hgrc(workingDirectory.path()+"/.hg/hgrc"); - hgrc.open(QIODevice::WriteOnly); - hgrc.write(QString("[paths]\ndefault = %1\n").arg(QString(url)).toUtf8()); - hgrc.close(); - - // And last update repository - arguments.clear(); - arguments << QLatin1String("update"); - const Utils::SynchronousProcessResponse resp2 = - executeHgSynchronously(workingDirectory.path(), arguments, flags); - return resp2.result == Utils::SynchronousProcessResponse::Finished; - } else { - QStringList arguments(QLatin1String("clone")); - arguments << url << workingDirectory.dirName(); - workingDirectory.cdUp(); - const Utils::SynchronousProcessResponse resp = - executeHgSynchronously(workingDirectory.path(), arguments, flags); - return resp.result == Utils::SynchronousProcessResponse::Finished; - } -} - QString MercurialClient::vcsGetRepositoryURL(const QString &directory) { QByteArray output; @@ -554,8 +244,8 @@ QString MercurialClient::vcsGetRepositoryURL(const QString &directory) QStringList arguments(QLatin1String("showconfig")); arguments << QLatin1String("paths.default"); - if (executeHgFullySynchronously(directory, arguments, &output)) - return QString::fromLocal8Bit(output);; + if (vcsFullySynchronousExec(directory, arguments, &output)) + return QString::fromLocal8Bit(output); return QString(); } @@ -578,10 +268,10 @@ void MercurialClient::incoming(const QString &repositoryRoot, const QString &rep VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, repositoryRoot, true, "incoming", id); - QSharedPointer job(new HgTask(repositoryRoot, args, editor)); + QSharedPointer job(new VCSBase::VCSJob(repositoryRoot, args, editor)); // Suppress SSH prompting. if (!repository.isEmpty() && VCSBase::VCSBasePlugin::isSshPromptConfigured()) - job->setUnixTerminalDisabled(true); + job->setUnixTerminalDisabled(true); enqueueJob(job); } @@ -592,116 +282,209 @@ void MercurialClient::outgoing(const QString &repositoryRoot) const QString kind = QLatin1String(Constants::DIFFLOG); const QString title = tr("Hg outgoing %1"). - arg(QDir::toNativeSeparators(repositoryRoot)); + arg(QDir::toNativeSeparators(repositoryRoot)); VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, repositoryRoot, true, "outgoing", repositoryRoot); - QSharedPointer job(new HgTask(repositoryRoot, args, editor)); + QSharedPointer job(new VCSBase::VCSJob(repositoryRoot, args, editor)); // Suppress SSH prompting job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); enqueueJob(job); } -void MercurialClient::view(const QString &source, const QString &id) -{ - QStringList args; - args << QLatin1String("log") << QLatin1String("-p") << QLatin1String("-g") - << QLatin1String("-r") << id; - - const QString kind = QLatin1String(Constants::DIFFLOG); - const QString title = tr("Hg log %1").arg(id); - - VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, source, - true, "view", id); - - QSharedPointer job(new HgTask(source, args, editor)); - enqueueJob(job); -} - -void MercurialClient::update(const QString &repositoryRoot, const QString &revision) -{ - QStringList args(QLatin1String("update")); - if (!revision.isEmpty()) - args << revision; - - QSharedPointer job(new HgTask(repositoryRoot, args, false, QVariant(repositoryRoot))); - // Suppress SSH prompting - job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); - connect(job.data(), SIGNAL(succeeded(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); - enqueueJob(job); -} - -void MercurialClient::commit(const QString &repositoryRoot, const QStringList &files, - const QString &committerInfo, const QString &commitMessageFile, - bool autoAddRemove) -{ - // refuse to do "autoadd" on a commit with working directory only, as this will - // add all the untracked stuff. - QTC_ASSERT(!(autoAddRemove && files.isEmpty()), return) - QStringList args = QStringList(QLatin1String(nonInteractiveOptionC)); - args.append(QLatin1String("commit")); - if (!committerInfo.isEmpty()) - args << QLatin1String("-u") << committerInfo; - args << QLatin1String("-l") << commitMessageFile; - if (autoAddRemove) - args << QLatin1String("-A"); - args << files; - QSharedPointer job(new HgTask(repositoryRoot, args, false)); - enqueueJob(job); -} - -QString MercurialClient::findTopLevelForFile(const QFileInfo &file) +QString MercurialClient::findTopLevelForFile(const QFileInfo &file) const { const QString repositoryCheckFile = QLatin1String(Constants::MECURIALREPO) + QLatin1String("/requires"); return file.isDir() ? - VCSBase::VCSBasePlugin::findRepositoryForDirectory(file.absoluteFilePath(), repositoryCheckFile) : - VCSBase::VCSBasePlugin::findRepositoryForDirectory(file.absolutePath(), repositoryCheckFile); + VCSBase::VCSBasePlugin::findRepositoryForDirectory(file.absoluteFilePath(), repositoryCheckFile) : + VCSBase::VCSBasePlugin::findRepositoryForDirectory(file.absolutePath(), repositoryCheckFile); } -void MercurialClient::settingsChanged() +QString MercurialClient::vcsEditorKind(VCSCommand cmd) const { - if (jobManager) - jobManager->restart(); -} - -VCSBase::VCSBaseEditorWidget *MercurialClient::createVCSEditor(const QString &kind, QString title, - const QString &source, bool setSourceCodec, - const char *registerDynamicProperty, - const QString &dynamicPropertyValue) const -{ - VCSBase::VCSBaseEditorWidget *baseEditor = 0; - Core::IEditor* outputEditor = locateEditor(core, registerDynamicProperty, dynamicPropertyValue); - const QString progressMsg = tr("Working..."); - if (outputEditor) { - // Exists already - outputEditor->createNew(progressMsg); - baseEditor = VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(outputEditor); - QTC_ASSERT(baseEditor, return 0); - } else { - outputEditor = core->editorManager()->openEditorWithContents(kind, &title, progressMsg); - outputEditor->file()->setProperty(registerDynamicProperty, dynamicPropertyValue); - baseEditor = VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(outputEditor); - connect(baseEditor, SIGNAL(annotateRevisionRequested(QString,QString,int)), - this, SLOT(slotAnnotateRevisionRequested(QString,QString,int))); - QTC_ASSERT(baseEditor, return 0); - baseEditor->setSource(source); - if (setSourceCodec) - baseEditor->setCodec(VCSBase::VCSBaseEditorWidget::getCodec(source)); + switch(cmd) + { + case AnnotateCommand : return QLatin1String(Constants::ANNOTATELOG); + case DiffCommand : return QLatin1String(Constants::DIFFLOG); + case LogCommand : return QLatin1String(Constants::FILELOG); + default : return QLatin1String(""); } - - core->editorManager()->activateEditor(outputEditor, Core::EditorManager::ModeSwitch); - baseEditor->setForceReadOnly(true); - return baseEditor; + return QLatin1String(""); } -void MercurialClient::enqueueJob(const QSharedPointer &job) +QStringList MercurialClient::cloneArguments(const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions) const { - if (!jobManager) { - jobManager = new MercurialJobRunner(); - jobManager->start(); + Q_UNUSED(srcLocation); + Q_UNUSED(dstLocation); + Q_UNUSED(extraOptions); + QStringList args; + return args; +} + +QStringList MercurialClient::pullArguments(const QString &srcLocation, + const ExtraCommandOptions &extraOptions) const +{ + Q_UNUSED(extraOptions); + QStringList args; + // Add arguments for common options + if (!srcLocation.isEmpty()) + args << srcLocation; + return args; +} + +QStringList MercurialClient::pushArguments(const QString &dstLocation, + const ExtraCommandOptions &extraOptions) const +{ + Q_UNUSED(extraOptions); + QStringList args; + // Add arguments for common options + if (!dstLocation.isEmpty()) + args << dstLocation; + return args; +} + +QStringList MercurialClient::commitArguments(const QStringList &files, + const QString &commitMessageFile, + const ExtraCommandOptions &extraOptions) const +{ + QStringList args(QLatin1String("--noninteractive")); + // Fetch extra options + foreach (int iOption, extraOptions.keys()) + { + const QVariant iOptValue = extraOptions[iOption]; + switch (iOption) + { + case AuthorCommitOptionId : + { + Q_ASSERT(iOptValue.canConvert(QVariant::String)); + const QString committerInfo = iOptValue.toString(); + if (!committerInfo.isEmpty()) + args << QLatin1String("-u") << committerInfo; + break; + } + case AutoAddRemoveCommitOptionId : + { + Q_ASSERT(iOptValue.canConvert(QVariant::Bool)); + const bool autoAddRemove = iOptValue.toBool(); + if (autoAddRemove) + args << QLatin1String("-A"); + break; + } + default : + Q_ASSERT(false); // Invalid option ! + } + } // end foreach () + // Add arguments for common options + args << QLatin1String("-l") << commitMessageFile; + args << files; + return args; +} + +QStringList MercurialClient::importArguments(const QStringList &files) const +{ + QStringList args(QLatin1String("--no-commit")); + if (!files.isEmpty()) + args.append(files); + return args; +} + +QStringList MercurialClient::updateArguments(const QString &revision) const +{ + QStringList args; + if (!revision.isEmpty()) + args << QLatin1String("-r") << revision; + return args; +} + +QStringList MercurialClient::revertArguments(const QString &file, + const QString &revision) const +{ + QStringList args; + if (!revision.isEmpty()) + args << QLatin1String("-r") << revision; + if (!file.isEmpty()) + args << file; + return args; +} + +QStringList MercurialClient::revertAllArguments(const QString &revision) const +{ + QStringList args; + if (!revision.isEmpty()) + args << QLatin1String("-r") << revision; + return args << QLatin1String("--all"); +} + +QStringList MercurialClient::annotateArguments(const QString &file, + const QString &revision, + int /*lineNumber*/) const +{ + QStringList args; + args << QLatin1String("-u") << QLatin1String("-c") << QLatin1String("-d"); + if (!revision.isEmpty()) + args << QLatin1String("-r") << revision; + return args << file; +} + +QStringList MercurialClient::diffArguments(const QStringList &files) const +{ + QStringList args; + args << QLatin1String("-g") << QLatin1String("-p") << QLatin1String("-U 8"); + if (!files.isEmpty()) + args.append(files); + return args; +} + +QStringList MercurialClient::logArguments(const QStringList &files) const +{ + QStringList args; + if (!files.empty()) + args.append(files); + return args; +} + +QStringList MercurialClient::statusArguments(const QString &file) const +{ + QStringList args; + if (!file.isEmpty()) + args.append(file); + return args; +} + +QStringList MercurialClient::viewArguments(const QString &revision) const +{ + QStringList args; + args << QLatin1String("log") << QLatin1String("-p") << QLatin1String("-g") + << QLatin1String("-r") << revision; + return args; +} + +QPair MercurialClient::parseStatusLine(const QString &line) const +{ + QPair status; + if (!line.isEmpty()) + { + if (line.startsWith(QLatin1Char('M'))) + status.first = QLatin1String("Modified"); + else if (line.startsWith(QLatin1Char('A'))) + status.first = QLatin1String("Added"); + else if (line.startsWith(QLatin1Char('R'))) + status.first = QLatin1String("Removed"); + else if (line.startsWith(QLatin1Char('!'))) + status.first = QLatin1String("Deleted"); + else if (line.startsWith(QLatin1Char('?'))) + status.first = QLatin1String("Untracked"); + else + return status; + + //the status line should be similar to "M file_with_changes" + //so just should take the file name part and store it + status.second = line.mid(2); } - jobManager->enqueueJob(job); + return status; } } // namespace Internal diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h index ca5a58b45c4..b36679ca781 100644 --- a/src/plugins/mercurial/mercurialclient.h +++ b/src/plugins/mercurial/mercurialclient.h @@ -34,43 +34,27 @@ #ifndef MERCURIALCLIENT_H #define MERCURIALCLIENT_H -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QFileInfo; -class QVariant; -QT_END_NAMESPACE - -namespace Core { -class ICore; -} - -namespace VCSBase{ -class VCSBaseEditorWidget; -} - -namespace Utils { - struct SynchronousProcessResponse; -} +#include namespace Mercurial { namespace Internal { -class MercurialJobRunner; -class HgTask; - -class MercurialClient : public QObject +class MercurialClient : public VCSBase::VCSBaseClient { Q_OBJECT public: - MercurialClient(); - ~MercurialClient(); - bool add(const QString &workingDir, const QString &fileName); - bool remove(const QString &workingDir, const QString &fileName); - bool move(const QString &workingDir, const QString &from, const QString &to); + enum ExtraOptionId + { + // Commit + AuthorCommitOptionId, + AutoAddRemoveCommitOptionId + }; + + MercurialClient(const VCSBase::VCSBaseClientSettings &settings); + virtual bool synchronousClone(const QString &workingDir, + const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions = ExtraCommandOptions()); bool manifestSync(const QString &repository, const QString &filename); QString branchQuerySync(const QString &repositoryRoot); bool parentRevisionsSync(const QString &workingDirectory, @@ -83,67 +67,38 @@ public: QString *description); bool shortDescriptionsSync(const QString &workingDirectory, const QStringList &revisions, QStringList *descriptions); - void annotate(const QString &workingDir, const QString &files, - const QString revision = QString(), int lineNumber = -1); - void diff(const QString &workingDir, const QStringList &files = QStringList()); - void log(const QString &workingDir, const QStringList &files = QStringList(), - bool enableAnnotationContextMenu = false); - void import(const QString &repositoryRoot, const QStringList &files); - bool pullSync(const QString &repositoryRoot, const QString &repository = QString()); - bool pushSync(const QString &repositoryRoot, const QString &repository = QString()); void incoming(const QString &repositoryRoot, const QString &repository = QString()); void outgoing(const QString &repositoryRoot); - void status(const QString &workingDir, const QString &file = QString()); - void statusWithSignal(const QString &repository); - void revertFile(const QString &workingDir, const QString &file, const QString &revision = QString()); - void revertRepository(const QString &workingDir, const QString &revision = QString()); - bool createRepositorySync(const QString &workingDir); - void update(const QString &repositoryRoot, const QString &revision = QString()); - void commit(const QString &repositoryRoot, - const QStringList &files, - const QString &commiterInfo, - const QString &commitMessageFile, - bool autoAddRemove = false); - bool clone(const QString &directory, const QString &url); QString vcsGetRepositoryURL(const QString &directory); - static QString findTopLevelForFile(const QFileInfo &file); +public: + virtual QString findTopLevelForFile(const QFileInfo &file) const; -signals: - void parsedStatus(const QList > &statusList); - // Passes on changed signals from HgTask to Control. - void changed(const QVariant &v); +protected: + virtual QString vcsEditorKind(VCSCommand cmd) const; -public slots: - void view(const QString &source, const QString &id); - void settingsChanged(); + virtual QStringList cloneArguments(const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions) const; + virtual QStringList pullArguments(const QString &srcLocation, + const ExtraCommandOptions &extraOptions) const; + virtual QStringList pushArguments(const QString &dstLocation, + const ExtraCommandOptions &extraOptions) const; + virtual QStringList commitArguments(const QStringList &files, + const QString &commitMessageFile, + const ExtraCommandOptions &extraOptions) const; + virtual QStringList importArguments(const QStringList &files) const; + virtual QStringList updateArguments(const QString &revision) const; + virtual QStringList revertArguments(const QString &file, const QString &revision) const; + virtual QStringList revertAllArguments(const QString &revision) const; + virtual QStringList annotateArguments(const QString &file, + const QString &revision, int lineNumber) const; + virtual QStringList diffArguments(const QStringList &files) const; + virtual QStringList logArguments(const QStringList &files) const; + virtual QStringList statusArguments(const QString &file) const; + virtual QStringList viewArguments(const QString &revision) const; -private slots: - void statusParser(const QByteArray &data); - void slotAnnotateRevisionRequested(const QString &source, QString change, int lineNumber); - -private: - // Fully synchronous git execution (QProcess-based). - bool executeHgFullySynchronously(const QString &workingDir, - const QStringList &args, - QByteArray *output) const; - // Synchronous hg execution using Utils::SynchronousProcess, with - // log windows updating (using VCSBasePlugin::runVCS with flags). - inline Utils::SynchronousProcessResponse - executeHgSynchronously(const QString &workingDir, const QStringList &args, - unsigned flags = 0, QTextCodec *outputCodec = 0); - - void enqueueJob(const QSharedPointer &); - void revert(const QString &workingDir, const QString &argument, - const QString &revision, const QVariant &cookie); - - MercurialJobRunner *jobManager; - Core::ICore *core; - - VCSBase::VCSBaseEditorWidget *createVCSEditor(const QString &kind, QString title, - const QString &source, bool setSourceCodec, - const char *registerDynamicProperty, - const QString &dynamicPropertyValue) const; + virtual QPair parseStatusLine(const QString &line) const; }; } //namespace Internal diff --git a/src/plugins/mercurial/mercurialcontrol.cpp b/src/plugins/mercurial/mercurialcontrol.cpp index 66cbf0a3fe2..3d4ee801964 100644 --- a/src/plugins/mercurial/mercurialcontrol.cpp +++ b/src/plugins/mercurial/mercurialcontrol.cpp @@ -89,25 +89,27 @@ bool MercurialControl::vcsOpen(const QString &filename) bool MercurialControl::vcsAdd(const QString &filename) { const QFileInfo fi(filename); - return mercurialClient->add(fi.absolutePath(), fi.fileName()); + return mercurialClient->synchronousAdd(fi.absolutePath(), fi.fileName()); } bool MercurialControl::vcsDelete(const QString &filename) { const QFileInfo fi(filename); - return mercurialClient->remove(fi.absolutePath(), fi.fileName()); + return mercurialClient->synchronousRemove(fi.absolutePath(), fi.fileName()); } bool MercurialControl::vcsMove(const QString &from, const QString &to) { const QFileInfo fromInfo(from); const QFileInfo toInfo(to); - return mercurialClient->move(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath()); + return mercurialClient->synchronousMove(fromInfo.absolutePath(), + fromInfo.absoluteFilePath(), + toInfo.absoluteFilePath()); } bool MercurialControl::vcsCreateRepository(const QString &directory) { - return mercurialClient->createRepositorySync(directory); + return mercurialClient->synchronousCreateRepository(directory); } QString MercurialControl::vcsCreateSnapshot(const QString &) @@ -150,7 +152,7 @@ bool MercurialControl::sccManaged(const QString &filename) bool MercurialControl::vcsCheckout(const QString &directory, const QByteArray &url) { - return mercurialClient->clone(directory,url); + return mercurialClient->synchronousClone(QString(), directory, url); } QString MercurialControl::vcsGetRepositoryURL(const QString &directory) diff --git a/src/plugins/mercurial/mercurialjobrunner.cpp b/src/plugins/mercurial/mercurialjobrunner.cpp deleted file mode 100644 index 9dd572c56fa..00000000000 --- a/src/plugins/mercurial/mercurialjobrunner.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2009 Brian McGillion -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** No Commercial Usage -** -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -**************************************************************************/ - -#include "mercurialjobrunner.h" -#include "mercurialplugin.h" -#include "constants.h" -#include "mercurialsettings.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace Mercurial::Internal; -using namespace Mercurial; - -HgTask::HgTask(const QString &repositoryRoot, - const QStringList &arguments, - bool emitRaw, - const QVariant &cookie) : - m_repositoryRoot(repositoryRoot), - arguments(arguments), - emitRaw(emitRaw), - m_cookie(cookie), - editor(0), - m_unixTerminalDisabled(false) -{ -} - -HgTask::HgTask(const QString &repositoryRoot, - const QStringList &arguments, - VCSBase::VCSBaseEditorWidget *editor, - const QVariant &cookie) : - m_repositoryRoot(repositoryRoot), - arguments(arguments), - emitRaw(false), - m_cookie(cookie), - editor(editor), - m_unixTerminalDisabled(false) -{ -} - -VCSBase::VCSBaseEditorWidget* HgTask::displayEditor() const -{ - return editor; -} - -void HgTask::emitSucceeded() -{ - emit succeeded(m_cookie); -} - -MercurialJobRunner::MercurialJobRunner() : - plugin(MercurialPlugin::instance()), - keepRunning(true) -{ - VCSBase::VCSBaseOutputWindow *ow = VCSBase::VCSBaseOutputWindow::instance(); - connect(this, SIGNAL(error(QString)), ow, SLOT(appendError(QString)), Qt::QueuedConnection); - connect(this, SIGNAL(commandStarted(QString)), ow, SLOT(appendCommand(QString)), Qt::QueuedConnection); -} - -MercurialJobRunner::~MercurialJobRunner() -{ - stop(); -} - -void MercurialJobRunner::stop() -{ - mutex.lock(); - keepRunning = false; - //Create a dummy task to break the cycle - QSharedPointer job(0); - jobs.enqueue(job); - waiter.wakeAll(); - mutex.unlock(); - - wait(); -} - -void MercurialJobRunner::restart() -{ - stop(); - mutex.lock(); - keepRunning = true; - mutex.unlock(); - start(); -} - -void MercurialJobRunner::getSettings() -{ - const MercurialSettings &settings = MercurialPlugin::instance()->settings(); - binary = settings.binary(); - m_timeoutMS = settings.timeoutMilliSeconds(); - standardArguments = settings.standardArguments(); -} - -void MercurialJobRunner::enqueueJob(const QSharedPointer &job) -{ - mutex.lock(); - jobs.enqueue(job); - waiter.wakeAll(); - mutex.unlock(); -} - -void MercurialJobRunner::run() -{ - getSettings(); - forever { - mutex.lock(); - while (jobs.count() == 0) - waiter.wait(&mutex); - - if (!keepRunning) { - jobs.clear(); - mutex.unlock(); - return; - } - - QSharedPointer job = jobs.dequeue(); - mutex.unlock(); - - task(job); - } -} - -QString MercurialJobRunner::msgStartFailed(const QString &binary, const QString &why) -{ - return tr("Unable to start mercurial process '%1': %2"). - arg(QDir::toNativeSeparators(binary), why); -} - -QString MercurialJobRunner::msgTimeout(int timeoutSeconds) -{ - return tr("Timed out after %1s waiting for mercurial process to finish.").arg(timeoutSeconds); -} - -// Set environment for a hg process to run in locale "C". Note that there appears -// to be a bug in hg that causes special characters to be garbled when running -// in a different language, which seems to be independent from the encoding. -void MercurialJobRunner::setProcessEnvironment(QProcess &p) -{ - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - VCSBase::VCSBasePlugin::setProcessEnvironment(&env, false); - p.setProcessEnvironment(env); -} - -void MercurialJobRunner::task(const QSharedPointer &job) -{ - HgTask *taskData = job.data(); - - VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); - - if (taskData->shouldEmit()) { - //Call the job's signal so the Initator of the job can process the data - //Because the QSharedPointer that holds the HgTask will go out of scope and hence be deleted - //we have to block and wait until the signal is delivered - connect(this, SIGNAL(output(QByteArray)), taskData, SIGNAL(rawData(QByteArray)), - Qt::BlockingQueuedConnection); - } else if (taskData->displayEditor()) { - //An editor has been created to display the data so send it there - connect(this, SIGNAL(output(QByteArray)), - taskData->displayEditor(), SLOT(setPlainTextData(QByteArray)), - Qt::QueuedConnection); - } else { - //Just output the data to the Mercurial output window - connect(this, SIGNAL(output(QByteArray)), outputWindow, SLOT(appendData(QByteArray)), - Qt::QueuedConnection); - } - - const QStringList args = standardArguments + taskData->args(); - emit commandStarted(VCSBase::VCSBaseOutputWindow::msgExecutionLogEntry(taskData->repositoryRoot(), binary, args)); - //infom the user of what we are going to try and perform - - if (Constants::debug) - qDebug() << Q_FUNC_INFO << "Repository root is " - << taskData->repositoryRoot() << " terminal_disabled" - << taskData->unixTerminalDisabled(); - - const unsigned processFlags = taskData->unixTerminalDisabled() ? - unsigned(Utils::SynchronousProcess::UnixTerminalDisabled) : - unsigned(0); - - QSharedPointer hgProcess = Utils::SynchronousProcess::createProcess(processFlags); - hgProcess->setWorkingDirectory(taskData->repositoryRoot()); - MercurialJobRunner::setProcessEnvironment(*hgProcess); - - hgProcess->start(binary, args); - - if (!hgProcess->waitForStarted()) { - emit error(msgStartFailed(binary, hgProcess->errorString())); - return; - } - - hgProcess->closeWriteChannel(); - - QByteArray stdOutput; - QByteArray stdErr; - - if (!Utils::SynchronousProcess::readDataFromProcess(*hgProcess, m_timeoutMS, &stdOutput, &stdErr, false)) { - Utils::SynchronousProcess::stopProcess(*hgProcess); - emit error(msgTimeout(m_timeoutMS / 1000)); - return; - } - - if (hgProcess->exitStatus() == QProcess::NormalExit && hgProcess->exitCode() == 0) { - /* - * sometimes success means output is actually on error channel (stderr) - * e.g. "hg revert" outputs "no changes needed to 'file'" on stderr if file has not changed - * from revision specified - */ - if (stdOutput.isEmpty()) - stdOutput = stdErr; - emit output(stdOutput); // This will clear the diff "Working..." text. - taskData->emitSucceeded(); - } else { - emit error(QString::fromLocal8Bit(stdErr)); - } - - hgProcess->close(); - //the signal connection is to last only for the duration of a job/task. next time a new - //output signal connection must be made - disconnect(this, SIGNAL(output(QByteArray)), 0, 0); -} diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index 7d733d1e888..062241d5878 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -157,7 +157,7 @@ bool MercurialPlugin::initialize(const QStringList & /* arguments */, QString * { typedef VCSBase::VCSEditorFactory MercurialEditorFactory; - m_client = new MercurialClient(); + m_client = new MercurialClient(mercurialSettings); VCSBase::VCSBasePlugin::initialize(new MercurialControl(m_client)); core = Core::ICore::instance(); @@ -165,7 +165,7 @@ bool MercurialPlugin::initialize(const QStringList & /* arguments */, QString * optionsPage = new OptionsPage(); addAutoReleasedObject(optionsPage); - mercurialSettings.readSettings(core->settings()); + mercurialSettings.readSettings(core->settings(), QLatin1String("Mercurial")); connect(optionsPage, SIGNAL(settingsChanged()), m_client, SLOT(settingsChanged())); @@ -296,7 +296,7 @@ void MercurialPlugin::addCurrentFile() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return) - m_client->add(state.currentFileTopLevel(), state.relativeCurrentFile()); + m_client->synchronousAdd(state.currentFileTopLevel(), state.relativeCurrentFile()); } void MercurialPlugin::annotateCurrentFile() @@ -395,7 +395,7 @@ void MercurialPlugin::revertMulti() RevertDialog reverter; if (reverter.exec() != QDialog::Accepted) return; - m_client->revertRepository(state.topLevel(), reverter.revision()); + m_client->revertAll(state.topLevel(), reverter.revision()); } void MercurialPlugin::statusMulti() @@ -473,7 +473,7 @@ void MercurialPlugin::pull() dialog.setWindowTitle(tr("Pull Source")); if (dialog.exec() != QDialog::Accepted) return; - m_client->pullSync(state.topLevel(), dialog.getRepositoryString()); + m_client->synchronousPull(state.topLevel(), dialog.getRepositoryString()); } void MercurialPlugin::push() @@ -485,7 +485,7 @@ void MercurialPlugin::push() dialog.setWindowTitle(tr("Push Destination")); if (dialog.exec() != QDialog::Accepted) return; - m_client->pushSync(state.topLevel(), dialog.getRepositoryString()); + m_client->synchronousPush(state.topLevel(), dialog.getRepositoryString()); } void MercurialPlugin::update() @@ -672,11 +672,14 @@ bool MercurialPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *sub editorFile->save(); core->fileManager()->unblockFileChange(editorFile); - m_client->commit(commitEditor->repoRoot(), files, commitEditor->committerInfo(), - editorFile->fileName(), true); + QHash extraOptions; + extraOptions[MercurialClient::AuthorCommitOptionId] = commitEditor->committerInfo(); + m_client->commit(m_submitRepository, files, editorFile->fileName(), + extraOptions); } return true; } + void MercurialPlugin::deleteCommitLog() { if (changeLog) { diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp index 03343ed3326..e35b9daa410 100644 --- a/src/plugins/mercurial/mercurialsettings.cpp +++ b/src/plugins/mercurial/mercurialsettings.cpp @@ -34,120 +34,15 @@ #include "mercurialsettings.h" #include "constants.h" -#include - using namespace Mercurial::Internal; -enum { timeOutDefaultSeconds = 30 }; - -MercurialSettings::MercurialSettings() : - m_binary(QLatin1String(Constants::MERCURIALDEFAULT)), - m_logCount(0), - m_timeoutSeconds(timeOutDefaultSeconds), - m_prompt(true) +MercurialSettings::MercurialSettings() { + setBinary(QLatin1String(Constants::MERCURIALDEFAULT)); } -QString MercurialSettings::binary() const +MercurialSettings& MercurialSettings::operator=(const MercurialSettings& other) { - return m_binary; -} - -void MercurialSettings::setBinary(const QString &b) -{ - m_binary = b; -} - -QStringList MercurialSettings::standardArguments() const -{ - return m_standardArguments; -} - -QString MercurialSettings::userName() const -{ - return m_user; -} - -void MercurialSettings::setUserName(const QString &u) -{ - m_user = u; -} - -QString MercurialSettings::email() const -{ - return m_mail; -} - -void MercurialSettings::setEmail(const QString &m) -{ - m_mail = m; -} - -int MercurialSettings::logCount() const -{ - return m_logCount; -} - -void MercurialSettings::setLogCount(int l) -{ - m_logCount = l; -} - -int MercurialSettings::timeoutMilliSeconds() const -{ - //return timeout is in Ms - return m_timeoutSeconds * 1000; -} - -int MercurialSettings::timeoutSeconds() const -{ - //return timeout in seconds (as the user specifies on the options page - return m_timeoutSeconds; -} - -void MercurialSettings::setTimeoutSeconds(int s) -{ - m_timeoutSeconds = s; -} - -bool MercurialSettings::prompt() const -{ - return m_prompt; -} - -void MercurialSettings::setPrompt(bool b) -{ - m_prompt = b; -} - -void MercurialSettings::writeSettings(QSettings *settings) const -{ - settings->beginGroup(QLatin1String("Mercurial")); - settings->setValue(QLatin1String(Constants::MERCURIALPATH), m_binary); - settings->setValue(QLatin1String(Constants::MERCURIALUSERNAME), m_user); - settings->setValue(QLatin1String(Constants::MERCURIALEMAIL), m_mail); - settings->setValue(QLatin1String(Constants::MERCURIALLOGCOUNT), m_logCount); - settings->setValue(QLatin1String(Constants::MERCURIALTIMEOUT), m_timeoutSeconds); - settings->setValue(QLatin1String(Constants::MERCURIALPROMPTSUBMIT), m_prompt); - settings->endGroup(); -} - -void MercurialSettings::readSettings(const QSettings *settings) -{ - const QString keyRoot = QLatin1String("Mercurial/"); - m_binary = settings->value(keyRoot + QLatin1String(Constants::MERCURIALPATH), - QLatin1String(Constants::MERCURIALDEFAULT)).toString(); - m_user = settings->value(keyRoot + QLatin1String(Constants::MERCURIALUSERNAME), QString()).toString(); - m_mail = settings->value(keyRoot + QLatin1String(Constants::MERCURIALEMAIL), QString()).toString(); - m_logCount = settings->value(keyRoot + QLatin1String(Constants::MERCURIALLOGCOUNT), 0).toInt(); - m_timeoutSeconds = settings->value(keyRoot + QLatin1String(Constants::MERCURIALTIMEOUT), timeOutDefaultSeconds).toInt(); - m_prompt = settings->value(keyRoot + QLatin1String(Constants::MERCURIALPROMPTSUBMIT), true).toBool(); -} - -bool MercurialSettings::equals(const MercurialSettings &rhs) const -{ - return m_binary == rhs.m_binary && m_standardArguments == rhs.m_standardArguments - && m_user == rhs.m_user && m_mail == rhs.m_mail - && m_logCount == rhs.m_logCount && m_timeoutSeconds == rhs.m_timeoutSeconds - && m_prompt == rhs.m_prompt; + VCSBase::VCSBaseClientSettings::operator=(other); + return *this; } diff --git a/src/plugins/mercurial/mercurialsettings.h b/src/plugins/mercurial/mercurialsettings.h index 68c899ccf01..d298d47a764 100644 --- a/src/plugins/mercurial/mercurialsettings.h +++ b/src/plugins/mercurial/mercurialsettings.h @@ -34,65 +34,18 @@ #ifndef MERCURIALSETTINGS_H #define MERCURIALSETTINGS_H -#include -#include - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include namespace Mercurial { namespace Internal { -class MercurialSettings +class MercurialSettings : public VCSBase::VCSBaseClientSettings { public: MercurialSettings(); - - QString binary() const; - void setBinary(const QString &); - - // Calculated. - QStringList standardArguments() const; - - QString userName() const; - void setUserName(const QString &); - - QString email() const; - void setEmail(const QString &); - - int logCount() const; - void setLogCount(int l); - - int timeoutMilliSeconds() const; - int timeoutSeconds() const; - void setTimeoutSeconds(int s); - - bool prompt() const; - void setPrompt(bool b); - - void writeSettings(QSettings *settings) const; - void readSettings(const QSettings *settings); - - bool equals(const MercurialSettings &rhs) const; - -private: - void readSettings(); - - QString m_binary; - QStringList m_standardArguments; - QString m_user; - QString m_mail; - int m_logCount; - int m_timeoutSeconds; - bool m_prompt; + MercurialSettings& operator=(const MercurialSettings& other); }; -inline bool operator==(const MercurialSettings &s1, const MercurialSettings &s2) -{ return s1.equals(s2); } -inline bool operator!=(const MercurialSettings &s1, const MercurialSettings &s2) -{ return !s1.equals(s2); } - } // namespace Internal } // namespace Mercurial diff --git a/src/plugins/mercurial/optionspage.cpp b/src/plugins/mercurial/optionspage.cpp index 298ed0bd86d..a52c100b5ee 100644 --- a/src/plugins/mercurial/optionspage.cpp +++ b/src/plugins/mercurial/optionspage.cpp @@ -126,7 +126,8 @@ void OptionsPage::apply() if (newSettings != plugin->settings()) { //assume success and emit signal that settings are changed; plugin->setSettings(newSettings); - newSettings.writeSettings(Core::ICore::instance()->settings()); + newSettings.writeSettings(Core::ICore::instance()->settings(), + QLatin1String("Mercurial")); emit settingsChanged(); } } diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro index 066deab63d8..d8341917f9b 100644 --- a/src/plugins/vcsbase/vcsbase.pro +++ b/src/plugins/vcsbase/vcsbase.pro @@ -27,7 +27,10 @@ HEADERS += vcsbase_global.h \ basecheckoutwizardpage.h \ vcsbaseoutputwindow.h \ cleandialog.h \ - vcsbaseoptionspage.h + vcsbaseoptionspage.h \ + vcsjobrunner.h \ + vcsbaseclient.h \ + vcsbaseclientsettings.h SOURCES += vcsplugin.cpp \ vcsbaseplugin.cpp \ @@ -51,7 +54,10 @@ SOURCES += vcsplugin.cpp \ basecheckoutwizardpage.cpp \ vcsbaseoutputwindow.cpp \ cleandialog.cpp \ - vcsbaseoptionspage.cpp + vcsbaseoptionspage.cpp \ + vcsjobrunner.cpp \ + vcsbaseclient.cpp \ + vcsbaseclientsettings.cpp RESOURCES += vcsbase.qrc diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp new file mode 100644 index 00000000000..6f7eb0e7ae7 --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -0,0 +1,474 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Brian McGillion & Hugues Delorme +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "vcsbaseclient.h" +#include "vcsjobrunner.h" +#include "vcsbaseclientsettings.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QVariant) + +inline Core::IEditor *locateEditor(const Core::ICore *core, const char *property, const QString &entry) +{ + foreach (Core::IEditor *ed, core->editorManager()->openedEditors()) + if (ed->file()->property(property).toString() == entry) + return ed; + return 0; +} + +namespace VCSBase { + +VCSBaseClient::VCSBaseClient(const VCSBaseClientSettings &settings) : + m_jobManager(0), + m_core(Core::ICore::instance()), + m_clientSettings(settings) +{ + qRegisterMetaType(); +} + +VCSBaseClient::~VCSBaseClient() +{ + if (m_jobManager) { + delete m_jobManager; + m_jobManager = 0; + } +} + +bool VCSBaseClient::synchronousCreateRepository(const QString &workingDirectory) +{ + const QStringList args(vcsCommandString(CreateRepositoryCommand)); + QByteArray outputData; + if (!vcsFullySynchronousExec(workingDirectory, args, &outputData)) + return false; + QString output = QString::fromLocal8Bit(outputData); + output.remove(QLatin1Char('\r')); + VCSBase::VCSBaseOutputWindow::instance()->append(output); + return true; +} + +bool VCSBaseClient::synchronousClone(const QString &workingDir, + const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions) +{ + QStringList args; + args << vcsCommandString(CloneCommand) + << cloneArguments(srcLocation, dstLocation, extraOptions); + QByteArray stdOut; + return vcsFullySynchronousExec(workingDir, args, &stdOut); +} + +bool VCSBaseClient::synchronousAdd(const QString &workingDir, const QString &filename) +{ + QStringList args; + args << vcsCommandString(AddCommand) << filename; + QByteArray stdOut; + return vcsFullySynchronousExec(workingDir, args, &stdOut); +} + +bool VCSBaseClient::synchronousRemove(const QString &workingDir, const QString &filename) +{ + QStringList args; + args << vcsCommandString(RemoveCommand) << filename; + QByteArray stdOut; + return vcsFullySynchronousExec(workingDir, args, &stdOut); +} + +bool VCSBaseClient::synchronousMove(const QString &workingDir, + const QString &from, const QString &to) +{ + QStringList args; + args << vcsCommandString(MoveCommand) << from << to; + QByteArray stdOut; + return vcsFullySynchronousExec(workingDir, args, &stdOut); +} + +bool VCSBaseClient::synchronousPull(const QString &workingDir, + const QString &srcLocation, + const ExtraCommandOptions &extraOptions) +{ + QStringList args; + args << vcsCommandString(PullCommand) << pullArguments(srcLocation, extraOptions); + // Disable UNIX terminals to suppress SSH prompting + const unsigned flags = + VCSBase::VCSBasePlugin::SshPasswordPrompt + | VCSBase::VCSBasePlugin::ShowStdOutInLogWindow + | VCSBase::VCSBasePlugin::ShowSuccessMessage; + const Utils::SynchronousProcessResponse resp = vcsSynchronousExec(workingDir, args, flags); + const bool ok = resp.result == Utils::SynchronousProcessResponse::Finished; + if (ok) + emit changed(QVariant(workingDir)); + return ok; +} + +bool VCSBaseClient::synchronousPush(const QString &workingDir, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions) +{ + QStringList args; + args << vcsCommandString(PushCommand) << pushArguments(dstLocation, extraOptions); + // Disable UNIX terminals to suppress SSH prompting + const unsigned flags = + VCSBase::VCSBasePlugin::SshPasswordPrompt + | VCSBase::VCSBasePlugin::ShowStdOutInLogWindow + | VCSBase::VCSBasePlugin::ShowSuccessMessage; + const Utils::SynchronousProcessResponse resp = vcsSynchronousExec(workingDir, args, flags); + return resp.result == Utils::SynchronousProcessResponse::Finished; +} + +bool VCSBaseClient::vcsFullySynchronousExec(const QString &workingDir, + const QStringList &args, + QByteArray *output) +{ + QProcess vcsProcess; + if (!workingDir.isEmpty()) + vcsProcess.setWorkingDirectory(workingDir); + VCSJobRunner::setProcessEnvironment(&vcsProcess); + + const QString binary = m_clientSettings.binary(); + const QStringList arguments = m_clientSettings.standardArguments() + args; + + VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); + outputWindow->appendCommand(workingDir, binary, args); + + vcsProcess.start(binary, arguments); + + if (!vcsProcess.waitForStarted()) { + outputWindow->appendError(VCSJobRunner::msgStartFailed(binary, vcsProcess.errorString())); + return false; + } + + vcsProcess.closeWriteChannel(); + + QByteArray stdErr; + if (!Utils::SynchronousProcess::readDataFromProcess(vcsProcess, m_clientSettings.timeoutMilliSeconds(), + output, &stdErr, true)) { + Utils::SynchronousProcess::stopProcess(vcsProcess); + outputWindow->appendError(VCSJobRunner::msgTimeout(m_clientSettings.timeoutSeconds())); + return false; + } + if (!stdErr.isEmpty()) + outputWindow->append(QString::fromLocal8Bit(stdErr)); + + return vcsProcess.exitStatus() == QProcess::NormalExit && vcsProcess.exitCode() == 0; +} + +Utils::SynchronousProcessResponse VCSBaseClient::vcsSynchronousExec( + const QString &workingDirectory, + const QStringList &args, + unsigned flags, + QTextCodec *outputCodec) +{ + const QString binary = m_clientSettings.binary(); + const QStringList arguments = m_clientSettings.standardArguments() + args; + return VCSBase::VCSBasePlugin::runVCS(workingDirectory, binary, arguments, + m_clientSettings.timeoutMilliSeconds(), + flags, outputCodec); +} + +void VCSBaseClient::slotAnnotateRevisionRequested(const QString &source, + QString change, + int lineNumber) +{ + // This might be invoked with a verbose revision description + // "SHA1 author subject" from the annotation context menu. Strip the rest. + const int blankPos = change.indexOf(QLatin1Char(' ')); + if (blankPos != -1) + change.truncate(blankPos); + const QFileInfo fi(source); + annotate(fi.absolutePath(), fi.fileName(), change, lineNumber); +} + +void VCSBaseClient::annotate(const QString &workingDir, const QString &file, + const QString revision /* = QString() */, + int lineNumber /* = -1 */) +{ + Q_UNUSED(lineNumber) + const QString vcsCmdString = vcsCommandString(AnnotateCommand); + QStringList args; + args << vcsCmdString << annotateArguments(file, revision, lineNumber); + const QString kind = vcsEditorKind(AnnotateCommand); + const QString id = VCSBase::VCSBaseEditorWidget::getSource(workingDir, QStringList(file)); + const QString title = vcsEditorTitle(vcsCmdString, id); + const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, file); + + VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, source, true, + vcsCmdString.toLatin1().constData(), id); + + QSharedPointer job(new VCSJob(workingDir, args, editor)); + enqueueJob(job); +} + +void VCSBaseClient::diff(const QString &workingDir, const QStringList &files) +{ + const QString vcsCmdString = vcsCommandString(DiffCommand); + QStringList args; + args << vcsCmdString << diffArguments(files); + const QString kind = vcsEditorKind(DiffCommand); + const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files); + const QString title = vcsEditorTitle(vcsCmdString, id); + const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files); + VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, source, true, + vcsCmdString.toLatin1().constData(), id); + editor->setDiffBaseDirectory(workingDir); + + QSharedPointer job(new VCSJob(workingDir, args, editor)); + enqueueJob(job); +} + +void VCSBaseClient::log(const QString &workingDir, const QStringList &files, + bool enableAnnotationContextMenu) +{ + const QString vcsCmdString = vcsCommandString(LogCommand); + QStringList args; + args << vcsCmdString << logArguments(files); + const QString kind = vcsEditorKind(LogCommand); + const QString id = VCSBase::VCSBaseEditorWidget::getTitleId(workingDir, files); + const QString title = vcsEditorTitle(vcsCmdString, id); + const QString source = VCSBase::VCSBaseEditorWidget::getSource(workingDir, files); + + VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, workingDir, true, + vcsCmdString.toLatin1().constData(), id); + editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu); + + QSharedPointer job(new VCSJob(workingDir, args, editor)); + enqueueJob(job); +} + +void VCSBaseClient::revertFile(const QString &workingDir, + const QString &file, + const QString &revision) +{ + QStringList args(vcsCommandString(RevertCommand)); + args << revertArguments(file, revision); + // Indicate repository change or file list + QSharedPointer job(new VCSJob(workingDir, args)); + job->setCookie(QStringList(workingDir + QLatin1Char('/') + file)); + connect(job.data(), SIGNAL(succeeded(QVariant)), + this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); + enqueueJob(job); +} + +void VCSBaseClient::revertAll(const QString &workingDir, const QString &revision) +{ + QStringList args(vcsCommandString(RevertCommand)); + args << revertAllArguments(revision); + // Indicate repository change or file list + QSharedPointer job(new VCSJob(workingDir, args)); + connect(job.data(), SIGNAL(succeeded(QVariant)), + this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); + enqueueJob(job); +} + +void VCSBaseClient::status(const QString &workingDir, const QString &file) +{ + QStringList args(vcsCommandString(StatusCommand)); + args << statusArguments(file); + VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance(); + outwin->setRepository(workingDir); + QSharedPointer job(new VCSJob(workingDir, args)); + connect(job.data(), SIGNAL(succeeded(QVariant)), + outwin, SLOT(clearRepository()), Qt::QueuedConnection); + enqueueJob(job); +} + +void VCSBaseClient::statusWithSignal(const QString &repositoryRoot) +{ + QStringList args(vcsCommandString(StatusCommand)); + args << statusArguments(QString()); + QSharedPointer job(new VCSJob(repositoryRoot, args, VCSJob::RawDataEmitMode)); + connect(job.data(), SIGNAL(rawData(QByteArray)), + this, SLOT(statusParser(QByteArray))); + enqueueJob(job); +} + +QString VCSBaseClient::vcsCommandString(VCSCommand cmd) const +{ + switch(cmd) { + case CreateRepositoryCommand : return QLatin1String("init"); + case CloneCommand : return QLatin1String("clone"); + case AddCommand : return QLatin1String("add"); + case RemoveCommand : return QLatin1String("remove"); + case MoveCommand : return QLatin1String("rename"); + case PullCommand : return QLatin1String("pull"); + case PushCommand : return QLatin1String("push"); + case CommitCommand : return QLatin1String("commit"); + case ImportCommand : return QLatin1String("import"); + case UpdateCommand : return QLatin1String("update"); + case RevertCommand : return QLatin1String("revert"); + case AnnotateCommand : return QLatin1String("annotate"); + case DiffCommand : return QLatin1String("diff"); + case LogCommand : return QLatin1String("log"); + case StatusCommand : return QLatin1String("status"); + } + return QLatin1String(""); +} + +void VCSBaseClient::statusParser(const QByteArray &data) +{ + QList > statusList; + + QStringList rawStatusList = QTextCodec::codecForLocale()->toUnicode(data).split(QLatin1Char('\n')); + + foreach (const QString &string, rawStatusList) { + QPair status = parseStatusLine(string); + if (!status.first.isEmpty() && !status.second.isEmpty()) + statusList.append(status); + } + + emit parsedStatus(statusList); +} + +void VCSBaseClient::import(const QString &repositoryRoot, const QStringList &files) +{ + QStringList args(vcsCommandString(ImportCommand)); + args << importArguments(files); + QSharedPointer job(new VCSJob(repositoryRoot, args)); + enqueueJob(job); +} + +void VCSBaseClient::view(const QString &source, const QString &id) +{ + QStringList args(viewArguments(id)); + const QString kind = vcsEditorKind(DiffCommand); + const QString title = vcsEditorTitle(vcsCommandString(LogCommand), id); + + VCSBase::VCSBaseEditorWidget *editor = createVCSEditor(kind, title, source, + true, "view", id); + + QSharedPointer job(new VCSJob(source, args, editor)); + enqueueJob(job); +} + +void VCSBaseClient::update(const QString &repositoryRoot, const QString &revision) +{ + QStringList args(vcsCommandString(UpdateCommand)); + args.append(updateArguments(revision)); + + QSharedPointer job(new VCSJob(repositoryRoot, args)); + job->setCookie(repositoryRoot); + // Suppress SSH prompting + job->setUnixTerminalDisabled(VCSBase::VCSBasePlugin::isSshPromptConfigured()); + connect(job.data(), SIGNAL(succeeded(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection); + enqueueJob(job); +} + +void VCSBaseClient::commit(const QString &repositoryRoot, + const QStringList &files, + const QString &commitMessageFile, + const ExtraCommandOptions &extraOptions) +{ + QStringList args(vcsCommandString(CommitCommand)); + args.append(commitArguments(files, commitMessageFile, extraOptions)); + QSharedPointer job(new VCSJob(repositoryRoot, args)); + enqueueJob(job); +} + +void VCSBaseClient::settingsChanged() +{ + if (m_jobManager) { + m_jobManager->setSettings(m_clientSettings.binary(), + m_clientSettings.standardArguments(), + m_clientSettings.timeoutMilliSeconds()); + m_jobManager->restart(); + } +} + +QString VCSBaseClient::vcsEditorTitle(const QString &vcsCmd, const QString &sourceId) const +{ + return m_clientSettings.binary() + QLatin1Char(' ') + vcsCmd + QLatin1Char(' ') + sourceId; +} + +VCSBase::VCSBaseEditorWidget *VCSBaseClient::createVCSEditor(const QString &kind, QString title, + const QString &source, bool setSourceCodec, + const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const +{ + VCSBase::VCSBaseEditorWidget *baseEditor = 0; + Core::IEditor *outputEditor = locateEditor(m_core, registerDynamicProperty, dynamicPropertyValue); + const QString progressMsg = tr("Working..."); + if (outputEditor) { + // Exists already + outputEditor->createNew(progressMsg); + baseEditor = VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(outputEditor); + QTC_ASSERT(baseEditor, return 0); + } else { + outputEditor = m_core->editorManager()->openEditorWithContents(kind, &title, progressMsg); + outputEditor->file()->setProperty(registerDynamicProperty, dynamicPropertyValue); + baseEditor = VCSBase::VCSBaseEditorWidget::getVcsBaseEditor(outputEditor); + connect(baseEditor, SIGNAL(annotateRevisionRequested(QString,QString,int)), + this, SLOT(slotAnnotateRevisionRequested(QString,QString,int))); + QTC_ASSERT(baseEditor, return 0); + baseEditor->setSource(source); + if (setSourceCodec) + baseEditor->setCodec(VCSBase::VCSBaseEditorWidget::getCodec(source)); + } + + m_core->editorManager()->activateEditor(outputEditor, Core::EditorManager::ModeSwitch); + baseEditor->setForceReadOnly(true); + return baseEditor; +} + +void VCSBaseClient::enqueueJob(const QSharedPointer &job) +{ + if (!m_jobManager) { + m_jobManager = new VCSJobRunner(); + m_jobManager->setSettings(m_clientSettings.binary(), + m_clientSettings.standardArguments(), + m_clientSettings.timeoutMilliSeconds()); + m_jobManager->start(); + } + m_jobManager->enqueueJob(job); +} + +} // namespace VCSBase diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h new file mode 100644 index 00000000000..207e53f4442 --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseclient.h @@ -0,0 +1,190 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Brian McGillion & Hugues Delorme +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef VCSBASECLIENT_H +#define VCSBASECLIENT_H + +#include "vcsbase_global.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QFileInfo; +class QVariant; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace Utils { +struct SynchronousProcessResponse; +} + +namespace VCSBase { + +class VCSBaseEditorWidget; +class VCSBaseClientSettings; +class VCSJobRunner; +class VCSJob; + +class VCSBASE_EXPORT VCSBaseClient : public QObject +{ + Q_OBJECT +public: + typedef QHash ExtraCommandOptions; + + VCSBaseClient(const VCSBaseClientSettings &settings); + ~VCSBaseClient(); + virtual bool synchronousCreateRepository(const QString &workingDir); + virtual bool synchronousClone(const QString &workingDir, + const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions = ExtraCommandOptions()); + virtual bool synchronousAdd(const QString &workingDir, const QString &fileName); + virtual bool synchronousRemove(const QString &workingDir, const QString &fileName); + virtual bool synchronousMove(const QString &workingDir, + const QString &from, const QString &to); + virtual bool synchronousPull(const QString &workingDir, + const QString &srcLocation, + const ExtraCommandOptions &extraOptions = ExtraCommandOptions()); + virtual bool synchronousPush(const QString &workingDir, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions = ExtraCommandOptions()); + void annotate(const QString &workingDir, const QString &file, + const QString revision = QString(), int lineNumber = -1); + void diff(const QString &workingDir, const QStringList &files = QStringList()); + void log(const QString &workingDir, const QStringList &files = QStringList(), + bool enableAnnotationContextMenu = false); + void status(const QString &workingDir, const QString &file = QString()); + void statusWithSignal(const QString &repository); + void revertFile(const QString &workingDir, const QString &file, const QString &revision = QString()); + void revertAll(const QString &workingDir, const QString &revision = QString()); + void import(const QString &repositoryRoot, const QStringList &files); + void update(const QString &repositoryRoot, const QString &revision = QString()); + void commit(const QString &repositoryRoot, + const QStringList &files, + const QString &commitMessageFile, + const ExtraCommandOptions &extraOptions = ExtraCommandOptions()); + + virtual QString findTopLevelForFile(const QFileInfo &file) const = 0; + +signals: + void parsedStatus(const QList > &statusList); + // Passes on changed signals from VCSJob to Control + void changed(const QVariant &v); + +public slots: + void view(const QString &source, const QString &id); + void settingsChanged(); + +protected: + enum VCSCommand + { + CreateRepositoryCommand, + CloneCommand, + AddCommand, + RemoveCommand, + MoveCommand, + PullCommand, + PushCommand, + CommitCommand, + ImportCommand, + UpdateCommand, + RevertCommand, + AnnotateCommand, + DiffCommand, + LogCommand, + StatusCommand + }; + virtual QString vcsCommandString(VCSCommand cmd) const; + virtual QString vcsEditorKind(VCSCommand cmd) const = 0; + + virtual QStringList cloneArguments(const QString &srcLocation, + const QString &dstLocation, + const ExtraCommandOptions &extraOptions) const = 0; + virtual QStringList pullArguments(const QString &srcLocation, + const ExtraCommandOptions &extraOptions) const = 0; + virtual QStringList pushArguments(const QString &dstLocation, + const ExtraCommandOptions &extraOptions) const = 0; + virtual QStringList commitArguments(const QStringList &files, + const QString &commitMessageFile, + const ExtraCommandOptions &extraOptions) const = 0; + virtual QStringList importArguments(const QStringList &files) const = 0; + virtual QStringList updateArguments(const QString &revision) const = 0; + virtual QStringList revertArguments(const QString &file, const QString &revision) const = 0; + virtual QStringList revertAllArguments(const QString &revision) const = 0; + virtual QStringList annotateArguments(const QString &file, + const QString &revision, int lineNumber) const = 0; + virtual QStringList diffArguments(const QStringList &files) const = 0; + virtual QStringList logArguments(const QStringList &files) const = 0; + virtual QStringList statusArguments(const QString &file) const = 0; + virtual QStringList viewArguments(const QString &revision) const = 0; + + virtual QPair parseStatusLine(const QString &line) const = 0; + + QString vcsEditorTitle(const QString &vcsCmd, const QString &sourceId) const; + void enqueueJob(const QSharedPointer &); + // Fully synchronous VCS execution (QProcess-based) + bool vcsFullySynchronousExec(const QString &workingDir, + const QStringList &args, + QByteArray *output); + // Synchronous VCS execution using Utils::SynchronousProcess, with + // log windows updating (using VCSBasePlugin::runVCS with flags) + Utils::SynchronousProcessResponse vcsSynchronousExec(const QString &workingDir, + const QStringList &args, + unsigned flags = 0, + QTextCodec *outputCodec = 0); + VCSBase::VCSBaseEditorWidget *createVCSEditor(const QString &kind, QString title, + const QString &source, bool setSourceCodec, + const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const; + +private slots: + void statusParser(const QByteArray &data); + void slotAnnotateRevisionRequested(const QString &source, QString change, int lineNumber); + +private: + VCSJobRunner *m_jobManager; + Core::ICore *m_core; + const VCSBaseClientSettings& m_clientSettings; +}; + +} //namespace VCSBase + +#endif // VCSBASECLIENT_H diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.cpp b/src/plugins/vcsbase/vcsbaseclientsettings.cpp new file mode 100644 index 00000000000..bed1ba6fc84 --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseclientsettings.cpp @@ -0,0 +1,151 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Brian McGillion & Hugues Delorme +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "vcsbaseclientsettings.h" + +#include + +using namespace VCSBase; + +enum { timeOutDefaultSeconds = 30 }; + +VCSBaseClientSettings::VCSBaseClientSettings() : + m_binary(), + m_logCount(0), + m_prompt(true), + m_timeoutSeconds(timeOutDefaultSeconds) +{ +} + +QString VCSBaseClientSettings::binary() const +{ + return m_binary; +} + +void VCSBaseClientSettings::setBinary(const QString &b) +{ + m_binary = b; +} + +QStringList VCSBaseClientSettings::standardArguments() const +{ + return m_standardArguments; +} + +QString VCSBaseClientSettings::userName() const +{ + return m_user; +} + +void VCSBaseClientSettings::setUserName(const QString &u) +{ + m_user = u; +} + +QString VCSBaseClientSettings::email() const +{ + return m_mail; +} + +void VCSBaseClientSettings::setEmail(const QString &m) +{ + m_mail = m; +} + +int VCSBaseClientSettings::logCount() const +{ + return m_logCount; +} + +void VCSBaseClientSettings::setLogCount(int l) +{ + m_logCount = l; +} + +bool VCSBaseClientSettings::prompt() const +{ + return m_prompt; +} + +void VCSBaseClientSettings::setPrompt(bool b) +{ + m_prompt = b; +} + +int VCSBaseClientSettings::timeoutMilliSeconds() const +{ + //return timeout is in Ms + return m_timeoutSeconds * 1000; +} + +int VCSBaseClientSettings::timeoutSeconds() const +{ + //return timeout in seconds (as the user specifies on the options page + return m_timeoutSeconds; +} + +void VCSBaseClientSettings::setTimeoutSeconds(int s) +{ + m_timeoutSeconds = s; +} + +void VCSBaseClientSettings::writeSettings(QSettings *settings, const QString& group) const +{ + settings->beginGroup(group); + settings->setValue(QLatin1String("VCS_Path"), m_binary); + settings->setValue(QLatin1String("VCS_Username"), m_user); + settings->setValue(QLatin1String("VCS_Email"), m_mail); + settings->setValue(QLatin1String("VCS_LogCount"), m_logCount); + settings->setValue(QLatin1String("VCS_PromptOnSubmit"), m_prompt); + settings->setValue(QLatin1String("VCS_Timeout"), m_timeoutSeconds); + settings->endGroup(); +} + +void VCSBaseClientSettings::readSettings(const QSettings *settings, const QString& group) +{ + const QString keyRoot = group + QLatin1Char('/'); + m_binary = settings->value(keyRoot + QLatin1String("VCS_Path"), QString()).toString(); + m_user = settings->value(keyRoot + QLatin1String("VCS_Username"), QString()).toString(); + m_mail = settings->value(keyRoot + QLatin1String("VCS_Email"), QString()).toString(); + m_logCount = settings->value(keyRoot + QLatin1String("VCS_LogCount"), QString()).toInt(); + m_prompt = settings->value(keyRoot + QLatin1String("VCS_PromptOnSubmit"), QString()).toBool(); + m_timeoutSeconds = settings->value(keyRoot + QLatin1String("VCS_Timeout"), timeOutDefaultSeconds).toInt(); +} + +bool VCSBaseClientSettings::equals(const VCSBaseClientSettings &rhs) const +{ + return m_binary == rhs.m_binary && m_standardArguments == rhs.m_standardArguments + && m_user == rhs.m_user && m_mail == rhs.m_mail + && m_logCount == rhs.m_logCount && m_prompt == rhs.m_prompt + && m_timeoutSeconds == rhs.m_timeoutSeconds; +} diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.h b/src/plugins/vcsbase/vcsbaseclientsettings.h new file mode 100644 index 00000000000..6c05d9148ca --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseclientsettings.h @@ -0,0 +1,96 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Brian McGillion & Hugues Delorme +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef VCSBASECLIENTSETTINGS_H +#define VCSBASECLIENTSETTINGS_H + +#include "vcsbase_global.h" +#include +#include + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace VCSBase { + +class VCSBASE_EXPORT VCSBaseClientSettings +{ +public: + VCSBaseClientSettings(); + + QString binary() const; + void setBinary(const QString &); + + // Calculated + QStringList standardArguments() const; + + QString userName() const; + void setUserName(const QString &); + + QString email() const; + void setEmail(const QString &); + + int logCount() const; + void setLogCount(int l); + + bool prompt() const; + void setPrompt(bool b); + + int timeoutMilliSeconds() const; + int timeoutSeconds() const; + void setTimeoutSeconds(int s); + + virtual void writeSettings(QSettings *settings, const QString& group) const; + virtual void readSettings(const QSettings *settings, const QString& group); + + virtual bool equals(const VCSBaseClientSettings &rhs) const; + +private: + QString m_binary; + QStringList m_standardArguments; + QString m_user; + QString m_mail; + int m_logCount; + bool m_prompt; + int m_timeoutSeconds; +}; + +inline bool operator==(const VCSBaseClientSettings &s1, const VCSBaseClientSettings &s2) +{ return s1.equals(s2); } +inline bool operator!=(const VCSBaseClientSettings &s1, const VCSBaseClientSettings &s2) +{ return !s1.equals(s2); } + +} // namespace VCSBase + +#endif // VCSBASECLIENTSETTINGS_H diff --git a/src/plugins/vcsbase/vcsjobrunner.cpp b/src/plugins/vcsbase/vcsjobrunner.cpp new file mode 100644 index 00000000000..35c031c2222 --- /dev/null +++ b/src/plugins/vcsbase/vcsjobrunner.cpp @@ -0,0 +1,307 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Brian McGillion & Hugues Delorme +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "vcsjobrunner.h" +#include "vcsbaseconstants.h" +#include "vcsbaseoutputwindow.h" +#include "vcsbaseeditor.h" +#include "vcsbaseplugin.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace VCSBase; + +VCSJob::VCSJob(const QString &workingDir, + const QStringList &args, + DataEmitMode emitMode) : + m_workingDir(workingDir), + m_arguments(args), + m_emitRaw(emitMode == RawDataEmitMode), + m_cookie(), + m_editor(0), + m_unixTerminalDisabled(false) +{ +} + +VCSJob::VCSJob(const QString &workingDir, + const QStringList &args, + VCSBase::VCSBaseEditorWidget *editor) : + m_workingDir(workingDir), + m_arguments(args), + m_emitRaw(false), + m_cookie(), + m_editor(editor), + m_unixTerminalDisabled(false) +{ +} + + +VCSJob::DataEmitMode VCSJob::dataEmitMode() const +{ + if (m_emitRaw) + return RawDataEmitMode; + else if (displayEditor() != 0) + return EditorDataEmitMode; + else + return NoDataEmitMode; +} + +VCSBase::VCSBaseEditorWidget *VCSJob::displayEditor() const +{ + return m_editor; +} + +QStringList VCSJob::arguments() const +{ + return m_arguments; +} + +QString VCSJob::workingDirectory() const +{ + return m_workingDir; +} + +const QVariant &VCSJob::cookie() const +{ + return m_cookie; +} + +bool VCSJob::unixTerminalDisabled() const +{ + return m_unixTerminalDisabled; +} + +void VCSJob::setDisplayEditor(VCSBase::VCSBaseEditorWidget *editor) +{ + m_editor = editor; + m_emitRaw = false; +} + +void VCSJob::setCookie(const QVariant &cookie) +{ + m_cookie = cookie; +} + +void VCSJob::setUnixTerminalDisabled(bool v) +{ + m_unixTerminalDisabled = v; +} + + + +VCSJobRunner::VCSJobRunner() : + m_keepRunning(true) +{ + VCSBase::VCSBaseOutputWindow *ow = VCSBase::VCSBaseOutputWindow::instance(); + connect(this, SIGNAL(error(QString)), + ow, SLOT(appendError(QString)), Qt::QueuedConnection); + connect(this, SIGNAL(commandStarted(QString)), + ow, SLOT(appendCommand(QString)), Qt::QueuedConnection); +} + +VCSJobRunner::~VCSJobRunner() +{ + stop(); +} + +void VCSJobRunner::stop() +{ + { + QMutexLocker mutexLocker(&m_mutex); Q_UNUSED(mutexLocker); + m_keepRunning = false; + //Create a dummy task to break the cycle + QSharedPointer job(0); + m_jobs.enqueue(job); + m_waiter.wakeAll(); + } + + wait(); +} + +void VCSJobRunner::restart() +{ + stop(); + m_mutex.lock(); + m_keepRunning = true; + m_mutex.unlock(); + start(); +} + +void VCSJobRunner::enqueueJob(const QSharedPointer &job) +{ + QMutexLocker mutexLocker(&m_mutex); Q_UNUSED(mutexLocker); + m_jobs.enqueue(job); + m_waiter.wakeAll(); +} + +void VCSJobRunner::run() +{ + forever { + m_mutex.lock(); + while (m_jobs.count() == 0) + m_waiter.wait(&m_mutex); + + if (!m_keepRunning) { + m_jobs.clear(); + m_mutex.unlock(); + return; + } + + QSharedPointer job = m_jobs.dequeue(); + m_mutex.unlock(); + + task(job); + } +} + +QString VCSJobRunner::msgStartFailed(const QString &binary, const QString &why) +{ + return tr("Unable to start process '%1': %2"). + arg(QDir::toNativeSeparators(binary), why); +} + +QString VCSJobRunner::msgTimeout(int timeoutSeconds) +{ + return tr("Timed out after %1s waiting for mercurial process to finish.").arg(timeoutSeconds); +} + +// Set environment for a VCS process to run in locale "C". Note that there appears +// to be a bug in some VCSs (like hg) that causes special characters to be garbled +// when running in a different language, which seems to be independent from the encoding. +void VCSJobRunner::setProcessEnvironment(QProcess *p) +{ + if (p == 0) + return; + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + VCSBase::VCSBasePlugin::setProcessEnvironment(&env, false); + p->setProcessEnvironment(env); +} + +void VCSJobRunner::setSettings(const QString &bin, + const QStringList &stdArgs, + int timeoutMsec) +{ + m_binary = bin; + m_standardArguments = stdArgs; + m_timeoutMS = timeoutMsec; +} + +void VCSJobRunner::task(const QSharedPointer &job) +{ + VCSJob *taskData = job.data(); + + VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); + + switch (taskData->dataEmitMode()) { + case VCSJob::NoDataEmitMode : + //Just output the data to the "Version control" output window + connect(this, SIGNAL(output(QByteArray)), outputWindow, SLOT(appendData(QByteArray)), + Qt::QueuedConnection); + break; + case VCSJob::RawDataEmitMode : + //Call the job's signal so the Initator of the job can process the data + //Because the QSharedPointer that holds the VCSJob will go out of scope and hence be deleted + //we have to block and wait until the signal is delivered + connect(this, SIGNAL(output(QByteArray)), taskData, SIGNAL(rawData(QByteArray)), + Qt::BlockingQueuedConnection); + break; + case VCSJob::EditorDataEmitMode : + //An editor has been created to display the data so send it there + connect(this, SIGNAL(output(QByteArray)), + taskData->displayEditor(), SLOT(setPlainTextData(QByteArray)), + Qt::QueuedConnection); + break; + } + + const QStringList args = m_standardArguments + taskData->arguments(); + emit commandStarted(VCSBase::VCSBaseOutputWindow::msgExecutionLogEntry(taskData->workingDirectory(), m_binary, args)); + //infom the user of what we are going to try and perform + + if (Constants::Internal::debug) + qDebug() << Q_FUNC_INFO << "Repository root is " + << taskData->workingDirectory() << " terminal_disabled" + << taskData->unixTerminalDisabled(); + + const unsigned processFlags = taskData->unixTerminalDisabled() ? + unsigned(Utils::SynchronousProcess::UnixTerminalDisabled) : + unsigned(0); + + QSharedPointer vcsProcess = Utils::SynchronousProcess::createProcess(processFlags); + vcsProcess->setWorkingDirectory(taskData->workingDirectory()); + VCSJobRunner::setProcessEnvironment(vcsProcess.data()); + + vcsProcess->start(m_binary, args); + + if (!vcsProcess->waitForStarted()) { + emit error(msgStartFailed(m_binary, vcsProcess->errorString())); + return; + } + + vcsProcess->closeWriteChannel(); + + QByteArray stdOutput; + QByteArray stdErr; + + if (!Utils::SynchronousProcess::readDataFromProcess(*vcsProcess, m_timeoutMS, &stdOutput, &stdErr, false)) { + Utils::SynchronousProcess::stopProcess(*vcsProcess); + emit error(msgTimeout(m_timeoutMS / 1000)); + return; + } + + if (vcsProcess->exitStatus() == QProcess::NormalExit) { + /* + * sometimes success means output is actually on error channel (stderr) + * e.g. "hg revert" outputs "no changes needed to 'file'" on stderr if file has not changed + * from revision specified + */ + if (stdOutput.isEmpty()) + stdOutput = stdErr; + emit output(stdOutput); // This will clear the diff "Working..." text. + if (vcsProcess->exitCode() == 0) + emit taskData->succeeded(taskData->cookie()); + else + emit error(QString::fromLocal8Bit(stdErr)); + } + + vcsProcess->close(); + //the signal connection is to last only for the duration of a job/task. next time a new + //output signal connection must be made + disconnect(this, SIGNAL(output(QByteArray)), 0, 0); +} diff --git a/src/plugins/mercurial/mercurialjobrunner.h b/src/plugins/vcsbase/vcsjobrunner.h similarity index 56% rename from src/plugins/mercurial/mercurialjobrunner.h rename to src/plugins/vcsbase/vcsjobrunner.h index f3b36a6f1ba..36c174369be 100644 --- a/src/plugins/mercurial/mercurialjobrunner.h +++ b/src/plugins/vcsbase/vcsjobrunner.h @@ -2,7 +2,7 @@ ** ** This file is part of Qt Creator ** -** Copyright (c) 2009 Brian McGillion +** Copyright (c) 2010 Brian McGillion & Hugues Delorme ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** @@ -31,8 +31,10 @@ ** **************************************************************************/ -#ifndef MERCURIALJOBRUNNER_H -#define MERCURIALJOBRUNNER_H +#ifndef VCSJOBRUNNER_H +#define VCSJOBRUNNER_H + +#include "vcsbase_global.h" #include #include @@ -50,66 +52,68 @@ QT_END_NAMESPACE namespace VCSBase { class VCSBaseEditorWidget; -} -namespace Mercurial { -namespace Internal { - -class MercurialPlugin; - -class HgTask : public QObject +class VCSBASE_EXPORT VCSJob : public QObject { Q_OBJECT public: - explicit HgTask(const QString &workingDir, - const QStringList &arguments, - bool emitRaw=false, - const QVariant &cookie = QVariant()); - explicit HgTask(const QString &workingDir, const QStringList &arguments, - VCSBase::VCSBaseEditorWidget *editor, - const QVariant &cookie = QVariant()); + enum DataEmitMode { + NoDataEmitMode, + RawDataEmitMode, + EditorDataEmitMode + }; - bool shouldEmit() const { return emitRaw; } - VCSBase::VCSBaseEditorWidget* displayEditor() const; - QStringList args() const { return arguments; } - QString repositoryRoot() const { return m_repositoryRoot; } + VCSJob(const QString &workingDir, + const QStringList &args, + DataEmitMode emitMode = NoDataEmitMode); + VCSJob(const QString &workingDir, + const QStringList &args, + VCSBase::VCSBaseEditorWidget *editor); - // Disable terminal to suppress SSH prompting. - bool unixTerminalDisabled() const { return m_unixTerminalDisabled; } - void setUnixTerminalDisabled(bool v) { m_unixTerminalDisabled = v; } + DataEmitMode dataEmitMode() const; + VCSBase::VCSBaseEditorWidget *displayEditor() const; + QStringList arguments() const; + QString workingDirectory() const; + const QVariant &cookie() const; + bool unixTerminalDisabled() const; + + void setDisplayEditor(VCSBase::VCSBaseEditorWidget *editor); + void setCookie(const QVariant &cookie); + // Disable terminal to suppress SSH prompting + void setUnixTerminalDisabled(bool v); signals: void succeeded(const QVariant &cookie); // Use a queued connection void rawData(const QByteArray &data); -public slots: - void emitSucceeded(); - private: - const QString m_repositoryRoot; - const QStringList arguments; - const bool emitRaw; - const QVariant m_cookie; - QPointer editor; // User might close it. + friend class VCSJobRunner; + const QString m_workingDir; + const QStringList m_arguments; + bool m_emitRaw; + QVariant m_cookie; + QPointer m_editor; // User might close it bool m_unixTerminalDisabled; }; /* A job queue running in a separate thread, executing commands * and emitting status/log signals. */ -class MercurialJobRunner : public QThread +class VCSBASE_EXPORT VCSJobRunner : public QThread { Q_OBJECT public: - MercurialJobRunner(); - ~MercurialJobRunner(); - void enqueueJob(const QSharedPointer &job); + VCSJobRunner(); + ~VCSJobRunner(); + void enqueueJob(const QSharedPointer &job); void restart(); static QString msgStartFailed(const QString &binary, const QString &why); static QString msgTimeout(int timeoutSeconds); - // Set environment for a hg process to run in locale "C" - static void setProcessEnvironment(QProcess &p); + // Set environment for a VCS process to run in locale "C" + static void setProcessEnvironment(QProcess *p); + + void setSettings(const QString &bin, const QStringList &stdArgs, int timeoutMsec); protected: void run(); @@ -120,20 +124,18 @@ signals: void output(const QByteArray &output); private: - void task(const QSharedPointer &job); + void task(const QSharedPointer &job); void stop(); - void getSettings(); - QQueue > jobs; - QMutex mutex; - QWaitCondition waiter; - MercurialPlugin *plugin; - bool keepRunning; - QString binary; - QStringList standardArguments; + QQueue > m_jobs; + QMutex m_mutex; + QWaitCondition m_waiter; + bool m_keepRunning; + QString m_binary; + QStringList m_standardArguments; int m_timeoutMS; }; -} //namespace Internal -} //namespace Mercurial -#endif // MERCURIALJOBRUNNER_H +} //namespace VCSBase + +#endif // VCSJOBRUNNER_H