diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index 2f5ba8ff58f92b5ae9f8335c9f4f32d508c2781a..2e15c6a8cd5dbd5f2aac0771eee440b0db1ca8a3 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -159,8 +159,116 @@ QString MercurialClient::branchQuerySync(const QString &repositoryRoot) return QLatin1String("Unknown Branch"); } -void MercurialClient::slotAnnotateRevisionRequested(const QString &source, const QString &change, int lineNumber) +static inline QString msgParentRevisionFailed(const QString &workingDirectory, + const QString &revision, + const QString &why) { + return MercurialClient::tr("Unable to find parent revisions of %1 in %2: %3").arg(revision, workingDirectory, why); +} + +static inline QString msgParseParentsOutputFailed(const QString &output) +{ + return MercurialClient::tr("Cannot parse output: %1").arg(output); +} + +bool MercurialClient::parentRevisionsSync(const QString &workingDirectory, + const QString &file /* = QString() */, + const QString &revision, + QStringList *parents) +{ + parents->clear(); + QStringList args; + args << QLatin1String("parents") << QLatin1String("-r") <<revision; + if (!file.isEmpty()) + args << file; + QByteArray outputData; + if (!executeHgSynchronously(workingDirectory, args, &outputData)) + return false; + QString output = QString::fromLocal8Bit(outputData); + output.remove(QLatin1Char('\r')); + /* Looks like: \code +changeset: 0:031a48610fba +user: ... +\endcode */ + // Obtain first line and split by blank-delimited tokens + VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); + const QStringList lines = output.split(QLatin1Char('\n')); + if (lines.size() < 1) { + outputWindow->appendSilently(msgParentRevisionFailed(workingDirectory, revision, msgParseParentsOutputFailed(output))); + return false; + } + QStringList changeSets = lines.front().simplified().split(QLatin1Char(' ')); + if (changeSets.size() < 2) { + outputWindow->appendSilently(msgParentRevisionFailed(workingDirectory, revision, msgParseParentsOutputFailed(output))); + return false; + } + // Remove revision numbers + const QChar colon = QLatin1Char(':'); + const QStringList::iterator end = changeSets.end(); + QStringList::iterator it = changeSets.begin(); + for (++it; it != end; ++it) { + const int colonIndex = it->indexOf(colon); + if (colonIndex != -1) + parents->push_back(it->mid(colonIndex + 1)); + } + return true; +} + +// Describe a change using an optional format +bool MercurialClient::shortDescriptionSync(const QString &workingDirectory, + const QString &revision, + const QString &format, + QString *description) +{ + description->clear(); + QStringList args; + args << QLatin1String("log") << QLatin1String("-r") <<revision; + if (!format.isEmpty()) + args << QLatin1String("--template") << format; + QByteArray outputData; + if (!executeHgSynchronously(workingDirectory, args, &outputData)) + return false; + *description = QString::fromLocal8Bit(outputData); + description->remove(QLatin1Char('\r')); + if (description->endsWith(QLatin1Char('\n'))) + description->truncate(description->size() - 1); + return true; +} + +// Default format: "SHA1 (author summmary)" +static const char defaultFormatC[] = "{node} ({author|person} {desc|firstline})"; + +bool MercurialClient::shortDescriptionSync(const QString &workingDirectory, + const QString &revision, + QString *description) +{ + if (!shortDescriptionSync(workingDirectory, revision, QLatin1String(defaultFormatC), description)) + return false; + description->remove(QLatin1Char('\n')); + return true; +} + +// Convenience to format a list of changes +bool MercurialClient::shortDescriptionsSync(const QString &workingDirectory, const QStringList &revisions, + QStringList *descriptions) +{ + descriptions->clear(); + foreach(const QString &revision, revisions) { + QString description; + if (!shortDescriptionSync(workingDirectory, revision, &description)) + return false; + descriptions->push_back(description); + } + 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); } diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h index 0a59b83a1d32c8c0f749c88612f708816608faa8..53fc0e58d0c135f10567746d4ceb99c451a510dc 100644 --- a/src/plugins/mercurial/mercurialclient.h +++ b/src/plugins/mercurial/mercurialclient.h @@ -64,6 +64,16 @@ public: bool remove(const QString &workingDir, const QString &fileName); bool manifestSync(const QString &repository, const QString &filename); QString branchQuerySync(const QString &repositoryRoot); + bool parentRevisionsSync(const QString &workingDirectory, + const QString &file /* = QString() */, + const QString &revision, + QStringList *parents); + bool shortDescriptionSync(const QString &workingDirectory, const QString &revision, + const QString &format /* = QString() */, QString *description); + bool shortDescriptionSync(const QString &workingDirectory, const QString &revision, + 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()); @@ -95,7 +105,7 @@ public slots: private slots: void statusParser(const QByteArray &data); - void slotAnnotateRevisionRequested(const QString &source, const QString &change, int lineNumber); + void slotAnnotateRevisionRequested(const QString &source, QString change, int lineNumber); private: bool executeHgSynchronously(const QString &workingDir, diff --git a/src/plugins/mercurial/mercurialeditor.cpp b/src/plugins/mercurial/mercurialeditor.cpp index 9315c3dad237720fe8f663818b068b73302dd8b9..e38d30b886ebc777a625a4a85308e624d71b39f1 100644 --- a/src/plugins/mercurial/mercurialeditor.cpp +++ b/src/plugins/mercurial/mercurialeditor.cpp @@ -31,6 +31,7 @@ #include "annotationhighlighter.h" #include "constants.h" #include "mercurialplugin.h" +#include "mercurialclient.h" #include <coreplugin/editormanager/editormanager.h> #include <vcsbase/diffhighlighter.h> @@ -53,6 +54,7 @@ MercurialEditor::MercurialEditor(const VCSBase::VCSBaseEditorParameters *type, Q changesetIdentifier40(QLatin1String(Constants::CHANGESETID40)), diffIdentifier(QLatin1String(Constants::DIFFIDENTIFIER)) { + setAnnotateRevisionTextFormat(tr("Annotate %1")); } QSet<QString> MercurialEditor::annotationChanges() const @@ -110,3 +112,21 @@ QString MercurialEditor::fileNameFromDiffSpecification(const QTextBlock &diffFil } return QString(); } + +QStringList MercurialEditor::annotationPreviousVersions(const QString &revision) const +{ + MercurialClient *client = MercurialPlugin::instance()->client(); + QStringList parents; + const QFileInfo fi(source()); + const QString workingDirectory = fi.absolutePath(); + // Retrieve parent revisions + QStringList revisions; + if (!client->parentRevisionsSync(workingDirectory, fi.fileName(), revision, &revisions)) + return QStringList(); + // Format with short summary + QStringList descriptions; + if (!client->shortDescriptionsSync(workingDirectory, revisions, &descriptions)) + return QStringList(); + return descriptions; +} + diff --git a/src/plugins/mercurial/mercurialeditor.h b/src/plugins/mercurial/mercurialeditor.h index ac5c3573387164b21c28280da5f6f5ec85229906..c74e6e48c865c710727998f2c179ef6d66da25e0 100644 --- a/src/plugins/mercurial/mercurialeditor.h +++ b/src/plugins/mercurial/mercurialeditor.h @@ -48,6 +48,7 @@ private: virtual VCSBase::DiffHighlighter *createDiffHighlighter() const; virtual VCSBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const; virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileSpec) const; + virtual QStringList annotationPreviousVersions(const QString &revision) const; const QRegExp exactIdentifier12; const QRegExp exactIdentifier40; diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index c9eb4ba34454b7bb48ef55cddc64e846b25c850f..29c9f0f8d24795e58657a96da3fee36b8c57b803 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -117,7 +117,7 @@ MercurialPlugin *MercurialPlugin::m_instance = 0; MercurialPlugin::MercurialPlugin() : VCSBase::VCSBasePlugin(QLatin1String(Constants::COMMITKIND)), optionsPage(0), - client(0), + m_client(0), changeLog(0), m_menuAction(0) { @@ -126,9 +126,9 @@ MercurialPlugin::MercurialPlugin() : MercurialPlugin::~MercurialPlugin() { - if (client) { - delete client; - client = 0; + if (m_client) { + delete m_client; + m_client = 0; } deleteCommitLog(); @@ -140,7 +140,7 @@ bool MercurialPlugin::initialize(const QStringList & /* arguments */, QString * { typedef VCSBase::VCSEditorFactory<MercurialEditor> MercurialEditorFactory; - VCSBase::VCSBasePlugin::initialize(new MercurialControl(client)); + VCSBase::VCSBasePlugin::initialize(new MercurialControl(m_client)); core = Core::ICore::instance(); actionManager = core->actionManager(); @@ -149,15 +149,15 @@ bool MercurialPlugin::initialize(const QStringList & /* arguments */, QString * addAutoReleasedObject(optionsPage); mercurialSettings.readSettings(core->settings()); - client = new MercurialClient(); - connect(optionsPage, SIGNAL(settingsChanged()), client, SLOT(settingsChanged())); + m_client = new MercurialClient(); + connect(optionsPage, SIGNAL(settingsChanged()), m_client, SLOT(settingsChanged())); - connect(client, SIGNAL(changed(QVariant)), versionControl(), SLOT(changed(QVariant))); + connect(m_client, SIGNAL(changed(QVariant)), versionControl(), SLOT(changed(QVariant))); static const char *describeSlot = SLOT(view(QString,QString)); const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters); for (int i = 0; i < editorCount; i++) - addAutoReleasedObject(new MercurialEditorFactory(editorParameters + i, client, describeSlot)); + addAutoReleasedObject(new MercurialEditorFactory(editorParameters + i, m_client, describeSlot)); addAutoReleasedObject(new VCSBase::VCSSubmitEditorFactory<CommitEditor>(&submitEditorParameters)); @@ -258,21 +258,21 @@ void MercurialPlugin::annotateCurrentFile() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return) - client->annotate(state.currentFileTopLevel(), state.relativeCurrentFile()); + m_client->annotate(state.currentFileTopLevel(), state.relativeCurrentFile()); } void MercurialPlugin::diffCurrentFile() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return) - client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); + m_client->diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); } void MercurialPlugin::logCurrentFile() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return) - client->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true); + m_client->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true); } void MercurialPlugin::revertCurrentFile() @@ -283,14 +283,14 @@ void MercurialPlugin::revertCurrentFile() RevertDialog reverter; if (reverter.exec() != QDialog::Accepted) return; - client->revertFile(state.currentFileTopLevel(), state.relativeCurrentFile(), reverter.revision()); + m_client->revertFile(state.currentFileTopLevel(), state.relativeCurrentFile(), reverter.revision()); } void MercurialPlugin::statusCurrentFile() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return) - client->status(state.currentFileTopLevel(), state.relativeCurrentFile()); + m_client->status(state.currentFileTopLevel(), state.relativeCurrentFile()); } void MercurialPlugin::createDirectoryActions(const QList<int> &context) @@ -327,14 +327,14 @@ void MercurialPlugin::diffRepository() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return) - client->diff(state.topLevel()); + m_client->diff(state.topLevel()); } void MercurialPlugin::logRepository() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return) - client->log(state.topLevel()); + m_client->log(state.topLevel()); } void MercurialPlugin::revertMulti() @@ -345,7 +345,7 @@ void MercurialPlugin::revertMulti() RevertDialog reverter; if (reverter.exec() != QDialog::Accepted) return; - client->revertRepository(state.topLevel(), reverter.revision()); + m_client->revertRepository(state.topLevel(), reverter.revision()); } void MercurialPlugin::statusMulti() @@ -353,7 +353,7 @@ void MercurialPlugin::statusMulti() const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return) - client->status(state.topLevel()); + m_client->status(state.topLevel()); } void MercurialPlugin::createRepositoryActions(const QList<int> &context) @@ -411,7 +411,7 @@ void MercurialPlugin::pull() dialog.setWindowTitle(tr("Pull Source")); if (dialog.exec() != QDialog::Accepted) return; - client->pull(state.topLevel(), dialog.getRepositoryString()); + m_client->pull(state.topLevel(), dialog.getRepositoryString()); } void MercurialPlugin::push() @@ -423,7 +423,7 @@ void MercurialPlugin::push() dialog.setWindowTitle(tr("Push Destination")); if (dialog.exec() != QDialog::Accepted) return; - client->push(state.topLevel(), dialog.getRepositoryString()); + m_client->push(state.topLevel(), dialog.getRepositoryString()); } void MercurialPlugin::update() @@ -435,7 +435,7 @@ void MercurialPlugin::update() updateDialog.setWindowTitle(tr("Update")); if (updateDialog.exec() != QDialog::Accepted) return; - client->update(state.topLevel(), updateDialog.revision()); + m_client->update(state.topLevel(), updateDialog.revision()); } void MercurialPlugin::import() @@ -451,7 +451,7 @@ void MercurialPlugin::import() return; const QStringList fileNames = importDialog.selectedFiles(); - client->import(state.topLevel(), fileNames); + m_client->import(state.topLevel(), fileNames); } void MercurialPlugin::incoming() @@ -463,14 +463,14 @@ void MercurialPlugin::incoming() dialog.setWindowTitle(tr("Incoming Source")); if (dialog.exec() != QDialog::Accepted) return; - client->incoming(state.topLevel(), dialog.getRepositoryString()); + m_client->incoming(state.topLevel(), dialog.getRepositoryString()); } void MercurialPlugin::outgoing() { const VCSBase::VCSBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return) - client->outgoing(state.topLevel()); + m_client->outgoing(state.topLevel()); } void MercurialPlugin::createSubmitEditorActions() @@ -502,9 +502,9 @@ void MercurialPlugin::commit() m_submitRepository = state.topLevel(); - connect(client, SIGNAL(parsedStatus(QList<QPair<QString,QString> >)), + connect(m_client, SIGNAL(parsedStatus(QList<QPair<QString,QString> >)), this, SLOT(showCommitWidget(QList<QPair<QString,QString> >))); - client->statusWithSignal(m_submitRepository); + m_client->statusWithSignal(m_submitRepository); } void MercurialPlugin::showCommitWidget(const QList<QPair<QString, QString> > &status) @@ -512,7 +512,7 @@ void MercurialPlugin::showCommitWidget(const QList<QPair<QString, QString> > &st VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); //Once we receive our data release the connection so it can be reused elsewhere - disconnect(client, SIGNAL(parsedStatus(QList<QPair<QString,QString> >)), + disconnect(m_client, SIGNAL(parsedStatus(QList<QPair<QString,QString> >)), this, SLOT(showCommitWidget(QList<QPair<QString,QString> >))); if (status.isEmpty()) { @@ -547,7 +547,7 @@ void MercurialPlugin::showCommitWidget(const QList<QPair<QString, QString> > &st const QString msg = tr("Commit changes for \"%1\".").arg(m_submitRepository); commitEditor->setDisplayName(msg); - QString branch = client->branchQuerySync(m_submitRepository); + QString branch = m_client->branchQuerySync(m_submitRepository); commitEditor->setFields(m_submitRepository, branch, mercurialSettings.userName(), mercurialSettings.email(), status); @@ -560,7 +560,7 @@ void MercurialPlugin::showCommitWidget(const QList<QPair<QString, QString> > &st void MercurialPlugin::diffFromEditorSelected(const QStringList &files) { - client->diff(m_submitRepository, files); + m_client->diff(m_submitRepository, files); } void MercurialPlugin::commitFromEditor() @@ -604,7 +604,7 @@ bool MercurialPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *sub editorFile->save(); core->fileManager()->unblockFileChange(editorFile); - client->commit(commitEditor->repoRoot(), files, commitEditor->committerInfo(), + m_client->commit(commitEditor->repoRoot(), files, commitEditor->committerInfo(), editorFile->fileName()); } return true; diff --git a/src/plugins/mercurial/mercurialplugin.h b/src/plugins/mercurial/mercurialplugin.h index c11d5590a0796bc75a94a36df35ddd821a1d44e7..6b6e9f50717a1b61525afff367d0c58343cf1dad 100644 --- a/src/plugins/mercurial/mercurialplugin.h +++ b/src/plugins/mercurial/mercurialplugin.h @@ -79,6 +79,7 @@ public: bool initialize(const QStringList &arguments, QString *error_message); void extensionsInitialized(); static MercurialPlugin *instance() { return m_instance; } + MercurialClient *client() const { return m_client; } QStringList standardArguments() const; @@ -145,7 +146,7 @@ private: static MercurialPlugin *m_instance; MercurialSettings mercurialSettings; OptionsPage *optionsPage; - MercurialClient *client; + MercurialClient *m_client; Core::ICore *core; Core::ActionManager *actionManager;