diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index 57ce4afb22d2f1ed08b9bc74a9cfb7dc49e560e2..390a546f64ad200c4f303df0c4057fb7701ebbce 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -27,21 +27,26 @@ #include "gitclient.h" #include "gitplugin.h" +#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/vcsmanager.h> #include <texteditor/findinfiles.h> #include <vcsbase/vcscommand.h> #include <vcsbase/vcsbaseconstants.h> +#include <utils/fancylineedit.h> #include <utils/filesearch.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> #include <utils/runextensions.h> #include <utils/synchronousprocess.h> +#include <utils/textfileformat.h> #include <QCheckBox> #include <QFuture> #include <QFutureWatcher> +#include <QHBoxLayout> +#include <QRegularExpressionValidator> #include <QScopedPointer> #include <QSettings> #include <QTextStream> @@ -49,6 +54,13 @@ namespace Git { namespace Internal { +class GitGrepParameters +{ +public: + QString ref; + bool isEnabled = false; +}; + using namespace Core; using namespace Utils; using VcsBase::VcsCommand; @@ -56,6 +68,7 @@ using VcsBase::VcsCommand; namespace { const char EnableGitGrep[] = "EnableGitGrep"; +const char GitGrepRef[] = "GitGrepRef"; class GitGrepRunner : public QObject { @@ -78,8 +91,10 @@ public: static const QLatin1String resetColor("\x1b[m"); FileSearchResult single; const int lineSeparator = line.indexOf(QChar::Null); - single.fileName = m_directory + QLatin1Char('/') - + line.left(lineSeparator); + QString filePath = line.left(lineSeparator); + if (!m_ref.isEmpty() && filePath.startsWith(m_ref)) + filePath.remove(0, m_ref.length()); + single.fileName = m_directory + QLatin1Char('/') + filePath; const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1); single.lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt(); QString text = line.mid(textSeparator + 1); @@ -130,6 +145,11 @@ public: else arguments << QLatin1String("-F"); arguments << m_parameters.text; + GitGrepParameters params = m_parameters.extensionParameters.value<GitGrepParameters>(); + if (!params.ref.isEmpty()) { + arguments << params.ref; + m_ref = params.ref + QLatin1Char(':'); + } arguments << QLatin1String("--") << m_parameters.nameFilters; GitClient *client = GitPlugin::instance()->client(); QScopedPointer<VcsCommand> command(client->createCommand(m_directory)); @@ -166,6 +186,7 @@ public: private: FutureInterfaceType m_fi; QString m_directory; + QString m_ref; const TextEditor::FileFindParameters &m_parameters; }; @@ -180,9 +201,21 @@ static bool validateDirectory(const QString &path) GitGrep::GitGrep() { - m_widget = new QCheckBox(tr("&Use Git Grep")); - m_widget->setToolTip(tr("Use Git Grep for searching. This includes only files " - "that are managed by source control.")); + m_widget = new QWidget; + auto layout = new QHBoxLayout(m_widget); + layout->setMargin(0); + m_enabledCheckBox = new QCheckBox(tr("&Use Git Grep")); + m_enabledCheckBox->setToolTip(tr("Use Git Grep for searching. This includes only files " + "that are managed by Git.")); + layout->addWidget(m_enabledCheckBox); + m_treeLineEdit = new FancyLineEdit; + m_treeLineEdit->setPlaceholderText( + tr("Tree: add reference here or leave empty to search through the file system)")); + m_treeLineEdit->setToolTip( + tr("Reference can be HEAD, tag, local or remote branch, or a commit hash.")); + const QRegularExpression refExpression(QLatin1String("[\\w/]*")); + m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this)); + layout->addWidget(m_treeLineEdit); TextEditor::FindInFiles *findInFiles = TextEditor::FindInFiles::instance(); QTC_ASSERT(findInFiles, return); connect(findInFiles, &TextEditor::FindInFiles::pathChanged, @@ -202,6 +235,14 @@ QString GitGrep::title() const return tr("Git Grep"); } +QString GitGrep::toolTip() const +{ + const QString ref = m_treeLineEdit->text(); + if (!ref.isEmpty()) + return tr("Ref: %1\n%2").arg(ref); + return QLatin1String("%1"); +} + QWidget *GitGrep::widget() const { return m_widget; @@ -209,27 +250,32 @@ QWidget *GitGrep::widget() const bool GitGrep::isEnabled() const { - return m_widget->isEnabled() && m_widget->isChecked(); + return m_widget->isEnabled() && m_enabledCheckBox->isChecked(); } bool GitGrep::isEnabled(const TextEditor::FileFindParameters ¶meters) const { - return parameters.extensionParameters.toBool(); + return parameters.extensionParameters.value<GitGrepParameters>().isEnabled; } QVariant GitGrep::parameters() const { - return isEnabled(); + GitGrepParameters params; + params.isEnabled = isEnabled(); + params.ref = m_treeLineEdit->text(); + return qVariantFromValue(params); } void GitGrep::readSettings(QSettings *settings) { - m_widget->setChecked(settings->value(QLatin1String(EnableGitGrep), false).toBool()); + m_enabledCheckBox->setChecked(settings->value(QLatin1String(EnableGitGrep), false).toBool()); + m_treeLineEdit->setText(settings->value(QLatin1String(GitGrepRef)).toString()); } void GitGrep::writeSettings(QSettings *settings) const { - settings->setValue(QLatin1String(EnableGitGrep), m_widget->isChecked()); + settings->setValue(QLatin1String(EnableGitGrep), m_enabledCheckBox->isChecked()); + settings->setValue(QLatin1String(GitGrepRef), m_treeLineEdit->text()); } QFuture<FileSearchResultList> GitGrep::executeSearch( @@ -238,5 +284,37 @@ QFuture<FileSearchResultList> GitGrep::executeSearch( return Utils::runAsync(GitGrepRunner::run, parameters); } +IEditor *GitGrep::openEditor(const SearchResultItem &item, + const TextEditor::FileFindParameters ¶meters) +{ + GitGrepParameters params = parameters.extensionParameters.value<GitGrepParameters>(); + if (!params.isEnabled || params.ref.isEmpty() || item.path.isEmpty()) + return nullptr; + const QString path = QDir::fromNativeSeparators(item.path.first()); + QByteArray content; + GitClient *client = GitPlugin::instance()->client(); + const QString topLevel = parameters.additionalParameters.toString(); + const QString relativePath = QDir(topLevel).relativeFilePath(path); + if (!client->synchronousShow(topLevel, params.ref + QLatin1String(":./") + relativePath, + &content, nullptr)) { + return nullptr; + } + if (content.isEmpty()) + return nullptr; + QByteArray fileContent; + if (TextFileFormat::readFileUTF8(path, 0, &fileContent, 0) == TextFileFormat::ReadSuccess) { + if (fileContent == content) + return nullptr; // open the file for read/write + } + QString title = tr("Git Show %1:%2").arg(params.ref).arg(relativePath); + IEditor *editor = EditorManager::openEditorWithContents(Id(), &title, content, title, + EditorManager::DoNotSwitchToDesignMode); + editor->gotoLine(item.lineNumber, item.textMarkPos); + editor->document()->setTemporary(true); + return editor; +} + } // Internal } // Git + +Q_DECLARE_METATYPE(Git::Internal::GitGrepParameters) diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h index e0a5ea60f1cf3ae3859ec4e62863f02f57c8a1b3..a18f8ae7fc9c9bce42210e6a774435f094265b77 100644 --- a/src/plugins/git/gitgrep.h +++ b/src/plugins/git/gitgrep.h @@ -28,10 +28,12 @@ #include <texteditor/basefilefind.h> -#include <utils/fileutils.h> +#include <QCoreApplication> QT_FORWARD_DECLARE_CLASS(QCheckBox) +namespace Utils { class FancyLineEdit; } + namespace Git { namespace Internal { @@ -43,6 +45,7 @@ public: GitGrep(); ~GitGrep() override; QString title() const override; + QString toolTip() const override; QWidget *widget() const override; bool isEnabled() const override; bool isEnabled(const TextEditor::FileFindParameters ¶meters) const override; @@ -51,9 +54,13 @@ public: void writeSettings(QSettings *settings) const override; QFuture<Utils::FileSearchResultList> executeSearch( const TextEditor::FileFindParameters ¶meters) override; + Core::IEditor *openEditor(const Core::SearchResultItem &item, + const TextEditor::FileFindParameters ¶meters) override; private: - QCheckBox *m_widget; + QWidget *m_widget; + QCheckBox *m_enabledCheckBox; + Utils::FancyLineEdit *m_treeLineEdit; }; } // namespace Internal diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index bb8c9f2f1cc8091cbe889129ffe067136d9a2f0c..facac8a9c05a79c7c843828381dafacf9b352e1e 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -140,8 +140,11 @@ void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags, d->m_currentFindSupport = 0; if (d->m_filterCombo) updateComboEntries(d->m_filterCombo, true); + QString tooltip = toolTip(); + if (d->m_extension) + tooltip = tooltip.arg(d->m_extension->toolTip()); SearchResult *search = SearchResultWindow::instance()->startNewSearch(label(), - toolTip().arg(IFindFilter::descriptionForFindFlags(findFlags)), + tooltip.arg(IFindFilter::descriptionForFindFlags(findFlags)), txt, searchMode, SearchResultWindow::PreserveCaseEnabled, QString::fromLatin1("TextEditor")); search->setTextToReplace(txt); @@ -320,14 +323,19 @@ void BaseFileFind::updateComboEntries(QComboBox *combo, bool onTop) void BaseFileFind::openEditor(const SearchResultItem &item) { SearchResult *result = qobject_cast<SearchResult *>(sender()); + FileFindParameters parameters = result->userData().value<FileFindParameters>(); IEditor *openedEditor = 0; - if (item.path.size() > 0) { - openedEditor = EditorManager::openEditorAt(QDir::fromNativeSeparators(item.path.first()), - item.lineNumber, - item.textMarkPos, Id(), - EditorManager::DoNotSwitchToDesignMode); - } else { - openedEditor = EditorManager::openEditor(QDir::fromNativeSeparators(item.text)); + if (d->m_extension) + openedEditor = d->m_extension->openEditor(item, parameters); + if (!openedEditor) { + if (item.path.size() > 0) { + openedEditor = EditorManager::openEditorAt(QDir::fromNativeSeparators(item.path.first()), + item.lineNumber, + item.textMarkPos, Id(), + EditorManager::DoNotSwitchToDesignMode); + } else { + openedEditor = EditorManager::openEditor(QDir::fromNativeSeparators(item.text)); + } } if (d->m_currentFindSupport) d->m_currentFindSupport->clearHighlights(); @@ -336,11 +344,8 @@ void BaseFileFind::openEditor(const SearchResultItem &item) return; // highlight results if (IFindSupport *findSupport = Aggregation::query<IFindSupport>(openedEditor->widget())) { - if (result) { - FileFindParameters parameters = result->userData().value<FileFindParameters>(); - d->m_currentFindSupport = findSupport; - d->m_currentFindSupport->highlightAll(parameters.text, parameters.flags); - } + d->m_currentFindSupport = findSupport; + d->m_currentFindSupport->highlightAll(parameters.text, parameters.flags); } } diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index f21f946b91c77465a8177a843cbcbd69594b0261..a39d1f3643fec17a72dbde3fc2a51313a4edbf4e 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -41,9 +41,10 @@ QT_END_NAMESPACE namespace Utils { class FileIterator; } namespace Core { +class IEditor; +class IFindSupport; class SearchResult; class SearchResultItem; -class IFindSupport; } // namespace Core namespace TextEditor { @@ -65,6 +66,7 @@ class TEXTEDITOR_EXPORT FileFindExtension : public QObject public: virtual ~FileFindExtension() {} virtual QString title() const = 0; + virtual QString toolTip() const = 0; // add %1 placeholder where the find flags should be put virtual QWidget *widget() const = 0; virtual bool isEnabled() const = 0; virtual bool isEnabled(const FileFindParameters ¶meters) const = 0; @@ -73,6 +75,8 @@ public: virtual void writeSettings(QSettings *settings) const = 0; virtual QFuture<Utils::FileSearchResultList> executeSearch( const FileFindParameters ¶meters) = 0; + virtual Core::IEditor *openEditor(const Core::SearchResultItem &item, + const FileFindParameters ¶meters) = 0; }; class TEXTEDITOR_EXPORT BaseFileFind : public Core::IFindFilter