diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp
index 20ec0507479a73e28bcf037e2405f7cd3fcdb020..3f0729e9f5496d11dd3af5bcc0dfefef023f65d4 100644
--- a/src/plugins/git/gitgrep.cpp
+++ b/src/plugins/git/gitgrep.cpp
@@ -27,24 +27,21 @@
 #include "gitclient.h"
 #include "gitplugin.h"
 
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/find/findplugin.h>
 #include <coreplugin/vcsmanager.h>
+#include <texteditor/findinfiles.h>
 #include <vcsbase/vcsbaseconstants.h>
 
 #include <utils/filesearch.h>
 #include <utils/fileutils.h>
-#include <utils/pathchooser.h>
 #include <utils/qtcassert.h>
 #include <utils/qtcprocess.h>
 #include <utils/runextensions.h>
 
-#include <QDir>
+#include <QCheckBox>
+#include <QCoreApplication>
 #include <QEventLoop>
 #include <QFuture>
 #include <QFutureWatcher>
-#include <QGridLayout>
-#include <QLabel>
 #include <QSettings>
 
 using namespace Utils;
@@ -54,18 +51,10 @@ namespace Internal {
 
 using namespace Core;
 
-QString GitGrep::id() const
-{
-    return QLatin1String("Git Grep");
-}
-
-QString GitGrep::displayName() const
-{
-    return tr("Git Grep");
-}
-
 namespace {
 
+const char EnableGitGrep[] = "EnableGitGrep";
+
 class GitGrepRunner : public QObject
 {
     using FutureInterfaceType = QFutureInterface<FileSearchResultList>;
@@ -180,116 +169,71 @@ private:
 
 } // namespace
 
-QFuture<FileSearchResultList> GitGrep::executeSearch(
-        const TextEditor::FileFindParameters &parameters)
-{
-    return Utils::runAsync<FileSearchResultList>(GitGrepRunner::run, parameters);
-}
-
-FileIterator *GitGrep::files(const QStringList &, const QVariant &) const
+static bool validateDirectory(const QString &path)
 {
-    QTC_ASSERT(false, return 0);
+    static IVersionControl *gitVc = VcsManager::versionControl(VcsBase::Constants::VCS_ID_GIT);
+    QTC_ASSERT(gitVc, return false);
+    return gitVc == VcsManager::findVersionControlForDirectory(path, 0);
 }
 
-QVariant GitGrep::additionalParameters() const
+GitGrep::GitGrep()
 {
-    return qVariantFromValue(path().toString());
+    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."));
+    TextEditor::FindInFiles *findInFiles = TextEditor::FindInFiles::instance();
+    QTC_ASSERT(findInFiles, return);
+    QObject::connect(findInFiles, &TextEditor::FindInFiles::pathChanged,
+                     m_widget.data(), [this](const QString &path) {
+        m_widget->setEnabled(validateDirectory(path));
+    });
+    findInFiles->setFindExtension(this);
 }
 
-QString GitGrep::label() const
+GitGrep::~GitGrep()
 {
-    const QChar slash = QLatin1Char('/');
-    const QStringList &nonEmptyComponents = path().toFileInfo().absoluteFilePath()
-            .split(slash, QString::SkipEmptyParts);
-    return tr("Git Grep \"%1\":").arg(nonEmptyComponents.isEmpty() ? QString(slash)
-                                                                   : nonEmptyComponents.last());
+    delete m_widget.data();
 }
 
-QString GitGrep::toolTip() const
+QString GitGrep::title() const
 {
-    //: %3 is filled by BaseFileFind::runNewSearch
-    return tr("Path: %1\nFilter: %2\n%3")
-            .arg(path().toUserOutput(), fileNameFilters().join(QLatin1Char(',')));
+    return tr("Git Grep");
 }
 
-bool GitGrep::validateDirectory(FancyLineEdit *edit, QString *errorMessage) const
+QWidget *GitGrep::widget() const
 {
-    static IVersionControl *gitVc =
-            VcsManager::versionControl(VcsBase::Constants::VCS_ID_GIT);
-    QTC_ASSERT(gitVc, return false);
-    if (!m_directory->defaultValidationFunction()(edit, errorMessage))
-        return false;
-    const QString path = m_directory->path();
-    IVersionControl *vc = VcsManager::findVersionControlForDirectory(path, 0);
-    if (vc == gitVc)
-        return true;
-    if (errorMessage)
-        *errorMessage = tr("The path \"%1\" is not managed by Git").arg(path);
-    return false;
+    return m_widget.data();
 }
 
-QWidget *GitGrep::createConfigWidget()
+bool GitGrep::isEnabled() const
 {
-    if (!m_configWidget) {
-        m_configWidget = new QWidget;
-        QGridLayout * const gridLayout = new QGridLayout(m_configWidget);
-        gridLayout->setMargin(0);
-        m_configWidget->setLayout(gridLayout);
-
-        QLabel *dirLabel = new QLabel(tr("Director&y:"));
-        gridLayout->addWidget(dirLabel, 0, 0, Qt::AlignRight);
-        m_directory = new PathChooser;
-        m_directory->setExpectedKind(PathChooser::ExistingDirectory);
-        m_directory->setHistoryCompleter(QLatin1String("Git.Grep.History"), true);
-        m_directory->setPromptDialogTitle(tr("Directory to search"));
-        m_directory->setValidationFunction([this](FancyLineEdit *edit, QString *errorMessage) {
-            return validateDirectory(edit, errorMessage);
-        });
-        connect(m_directory.data(), &PathChooser::validChanged,
-                this, &GitGrep::enabledChanged);
-        dirLabel->setBuddy(m_directory);
-        gridLayout->addWidget(m_directory, 0, 1, 1, 2);
-
-        QLabel * const filePatternLabel = new QLabel(tr("Fi&le pattern:"));
-        filePatternLabel->setMinimumWidth(80);
-        filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
-        filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
-        QWidget *patternWidget = createPatternWidget();
-        filePatternLabel->setBuddy(patternWidget);
-        gridLayout->addWidget(filePatternLabel, 1, 0);
-        gridLayout->addWidget(patternWidget, 1, 1, 1, 2);
-        m_configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
-    }
-    return m_configWidget;
+    return m_widget->isEnabled() && m_widget->isChecked();
 }
 
-FileName GitGrep::path() const
+bool GitGrep::isEnabled(const TextEditor::FileFindParameters &parameters) const
 {
-    return m_directory->fileName();
+    return parameters.extensionParameters.toBool();
 }
 
-void GitGrep::writeSettings(QSettings *settings)
+QVariant GitGrep::parameters() const
 {
-    settings->beginGroup(QLatin1String("GitGrep"));
-    writeCommonSettings(settings);
-    settings->endGroup();
+    return isEnabled();
 }
 
 void GitGrep::readSettings(QSettings *settings)
 {
-    settings->beginGroup(QLatin1String("GitGrep"));
-    readCommonSettings(settings, QLatin1String("*"));
-    settings->endGroup();
+    m_widget->setChecked(settings->value(QLatin1String(EnableGitGrep), false).toBool());
 }
 
-bool GitGrep::isValid() const
+void GitGrep::writeSettings(QSettings *settings) const
 {
-    return m_directory->isValid();
+    settings->setValue(QLatin1String(EnableGitGrep), m_widget->isChecked());
 }
 
-void GitGrep::setDirectory(const FileName &directory)
+QFuture<FileSearchResultList> GitGrep::executeSearch(
+        const TextEditor::FileFindParameters &parameters)
 {
-    m_directory->setFileName(directory);
+    return Utils::runAsync<FileSearchResultList>(GitGrepRunner::run, parameters);
 }
 
 } // Internal
diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h
index d919cfad8d60597545d1288d5558abcef0db3c62..67a239bf582b49c7f51101fcfe403e5815b128bc 100644
--- a/src/plugins/git/gitgrep.h
+++ b/src/plugins/git/gitgrep.h
@@ -33,43 +33,30 @@
 #include <QPointer>
 #include <QStringListModel>
 
-namespace Utils {
-class FancyLineEdit;
-class PathChooser;
-}
+QT_FORWARD_DECLARE_CLASS(QCheckBox)
 
 namespace Git {
 namespace Internal {
 
-class GitGrep : public TextEditor::BaseFileFind
+class GitGrep : public TextEditor::FileFindExtension
 {
-    Q_OBJECT
+    Q_DECLARE_TR_FUNCTIONS(GitGrep)
 
 public:
-    QString id() const override;
-    QString displayName() const override;
+    GitGrep();
+    ~GitGrep() override;
+    QString title() const override;
+    QWidget *widget() const override;
+    bool isEnabled() const override;
+    bool isEnabled(const TextEditor::FileFindParameters &parameters) const override;
+    QVariant parameters() const override;
+    void readSettings(QSettings *settings) override;
+    void writeSettings(QSettings *settings) const override;
     QFuture<Utils::FileSearchResultList> executeSearch(
             const TextEditor::FileFindParameters &parameters) override;
-    QWidget *createConfigWidget() override;
-    void writeSettings(QSettings *settings) override;
-    void readSettings(QSettings *settings) override;
-    bool isValid() const override;
-
-    void setDirectory(const Utils::FileName &directory);
-
-protected:
-    Utils::FileIterator *files(const QStringList &nameFilters,
-                               const QVariant &additionalParameters) const override;
-    QVariant additionalParameters() const override;
-    QString label() const override;
-    QString toolTip() const override;
 
 private:
-    Utils::FileName path() const;
-    bool validateDirectory(Utils::FancyLineEdit *edit, QString *errorMessage) const;
-
-    QPointer<QWidget> m_configWidget;
-    QPointer<Utils::PathChooser> m_directory;
+    QPointer<QCheckBox> m_widget;
 };
 
 } // namespace Internal
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index b5077262edf8d25088bba0fe065ed61912acceab..bb8c9f2f1cc8091cbe889129ffe067136d9a2f0c 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -68,15 +68,14 @@ public:
 class BaseFileFindPrivate
 {
 public:
-    BaseFileFindPrivate() : m_resultLabel(0), m_filterCombo(0) {}
-
     QMap<QFutureWatcher<FileSearchResultList> *, QPointer<SearchResult> > m_watchers;
     QPointer<IFindSupport> m_currentFindSupport;
 
-    QLabel *m_resultLabel;
+    QLabel *m_resultLabel = 0;
     QStringListModel m_filterStrings;
     QString m_filterSetting;
     QPointer<QComboBox> m_filterCombo;
+    QPointer<FileFindExtension> m_extension;
 };
 
 } // namespace Internal
@@ -130,6 +129,11 @@ QStringList BaseFileFind::fileNameFilters() const
     return filters;
 }
 
+FileFindExtension *BaseFileFind::extension() const
+{
+    return d->m_extension.data();
+}
+
 void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
                                     SearchResultWindow::SearchMode searchMode)
 {
@@ -147,6 +151,8 @@ void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
     parameters.flags = findFlags;
     parameters.nameFilters = fileNameFilters();
     parameters.additionalParameters = additionalParameters();
+    if (d->m_extension)
+        parameters.extensionParameters = d->m_extension->parameters();
     search->setUserData(qVariantFromValue(parameters));
     connect(search, &SearchResult::activated, this, &BaseFileFind::openEditor);
     if (searchMode == SearchResultWindow::SearchAndReplace)
@@ -192,6 +198,12 @@ void BaseFileFind::replaceAll(const QString &txt, FindFlags findFlags)
     runNewSearch(txt, findFlags, SearchResultWindow::SearchAndReplace);
 }
 
+void BaseFileFind::setFindExtension(FileFindExtension *extension)
+{
+    QTC_ASSERT(!d->m_extension, return);
+    d->m_extension = extension;
+}
+
 void BaseFileFind::doReplace(const QString &text,
                              const QList<SearchResultItem> &items,
                              bool preserveCase)
@@ -263,6 +275,8 @@ void BaseFileFind::writeCommonSettings(QSettings *settings)
     settings->setValue(QLatin1String("filters"), d->m_filterStrings.stringList());
     if (d->m_filterCombo)
         settings->setValue(QLatin1String("currentFilter"), d->m_filterCombo->currentText());
+    if (d->m_extension)
+        d->m_extension->writeSettings(settings);
 }
 
 void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaultFilter)
@@ -276,6 +290,8 @@ void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaul
     d->m_filterStrings.setStringList(filters);
     if (d->m_filterCombo)
         syncComboWithSettings(d->m_filterCombo, d->m_filterSetting);
+    if (d->m_extension)
+        d->m_extension->readSettings(settings);
 }
 
 void BaseFileFind::syncComboWithSettings(QComboBox *combo, const QString &setting)
@@ -425,6 +441,9 @@ QVariant BaseFileFind::getAdditionalParameters(SearchResult *search)
 
 QFuture<FileSearchResultList> BaseFileFind::executeSearch(const FileFindParameters &parameters)
 {
+    if (d->m_extension && d->m_extension->isEnabled(parameters))
+        return d->m_extension->executeSearch(parameters);
+
     auto func = parameters.flags & FindRegularExpression
             ? Utils::findInFilesRegExp
             : Utils::findInFiles;
diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h
index 31f56ed368f993d2f09db79dd89ff4a4024498a2..fd74bccbe3eb52802f6213a757c08f11028435a9 100644
--- a/src/plugins/texteditor/basefilefind.h
+++ b/src/plugins/texteditor/basefilefind.h
@@ -57,6 +57,22 @@ public:
     Core::FindFlags flags;
     QStringList nameFilters;
     QVariant additionalParameters;
+    QVariant extensionParameters;
+};
+
+class TEXTEDITOR_EXPORT FileFindExtension : public QObject
+{
+public:
+    virtual ~FileFindExtension() {}
+    virtual QString title() const = 0;
+    virtual QWidget *widget() const = 0;
+    virtual bool isEnabled() const = 0;
+    virtual bool isEnabled(const FileFindParameters &parameters) const = 0;
+    virtual QVariant parameters() const = 0;
+    virtual void readSettings(QSettings *settings) = 0;
+    virtual void writeSettings(QSettings *settings) const = 0;
+    virtual QFuture<Utils::FileSearchResultList> executeSearch(
+            const FileFindParameters &parameters) = 0;
 };
 
 class TEXTEDITOR_EXPORT BaseFileFind : public Core::IFindFilter
@@ -71,6 +87,7 @@ public:
     bool isReplaceSupported() const { return true; }
     void findAll(const QString &txt, Core::FindFlags findFlags);
     void replaceAll(const QString &txt, Core::FindFlags findFlags);
+    void setFindExtension(FileFindExtension *extension);
 
     /* returns the list of unique files that were passed in items */
     static QStringList replaceAll(const QString &txt,
@@ -94,6 +111,7 @@ protected:
     void syncComboWithSettings(QComboBox *combo, const QString &setting);
     void updateComboEntries(QComboBox *combo, bool onTop);
     QStringList fileNameFilters() const;
+    FileFindExtension *extension() const;
 
 private:
     void displayResult(int index);
diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp
index 34510fb44d20d28c5ae8d83509f3ba90591cfec0..06c21c127679cb8415438c09ef68a0a576125021 100644
--- a/src/plugins/texteditor/findinfiles.cpp
+++ b/src/plugins/texteditor/findinfiles.cpp
@@ -92,10 +92,17 @@ QVariant FindInFiles::additionalParameters() const
 
 QString FindInFiles::label() const
 {
+    QString title = tr("Directory");
+    if (FileFindExtension *ext = extension()) {
+        if (ext->isEnabled())
+            title = ext->title();
+    }
     const QChar slash = QLatin1Char('/');
     const QStringList &nonEmptyComponents = path().toFileInfo().absoluteFilePath()
             .split(slash, QString::SkipEmptyParts);
-    return tr("Directory \"%1\":").arg(nonEmptyComponents.isEmpty() ? QString(slash) : nonEmptyComponents.last());
+    return tr("%1 \"%2\":")
+            .arg(title)
+            .arg(nonEmptyComponents.isEmpty() ? QString(slash) : nonEmptyComponents.last());
 }
 
 QString FindInFiles::toolTip() const
@@ -114,15 +121,21 @@ QWidget *FindInFiles::createConfigWidget()
         gridLayout->setMargin(0);
         m_configWidget->setLayout(gridLayout);
 
+        int row = 0;
+        if (FileFindExtension *ext = extension())
+            gridLayout->addWidget(ext->widget(), row++, 1, 1, 2);
+
         QLabel *dirLabel = new QLabel(tr("Director&y:"));
-        gridLayout->addWidget(dirLabel, 0, 0, Qt::AlignRight);
+        gridLayout->addWidget(dirLabel, row, 0, Qt::AlignRight);
         m_directory = new PathChooser;
         m_directory->setExpectedKind(PathChooser::ExistingDirectory);
-        m_directory->setHistoryCompleter(QLatin1String(HistoryKey),
-                                         /*restoreLastItemFromHistory=*/ true);
         m_directory->setPromptDialogTitle(tr("Directory to Search"));
+        connect(m_directory.data(), &PathChooser::pathChanged,
+                this, &FindInFiles::pathChanged);
         connect(m_directory.data(), &PathChooser::validChanged,
                 this, &FindInFiles::enabledChanged);
+        m_directory->setHistoryCompleter(QLatin1String(HistoryKey),
+                                         /*restoreLastItemFromHistory=*/ true);
         if (!HistoryCompleter::historyExistsFor(QLatin1String(HistoryKey))) {
             auto completer = static_cast<HistoryCompleter *>(m_directory->lineEdit()->completer());
             const QStringList legacyHistory = Core::ICore::settings()->value(
@@ -131,7 +144,7 @@ QWidget *FindInFiles::createConfigWidget()
                 completer->addEntry(dir);
         }
         dirLabel->setBuddy(m_directory);
-        gridLayout->addWidget(m_directory, 0, 1, 1, 2);
+        gridLayout->addWidget(m_directory, row++, 1, 1, 2);
 
         QLabel * const filePatternLabel = new QLabel(tr("Fi&le pattern:"));
         filePatternLabel->setMinimumWidth(80);
@@ -139,8 +152,8 @@ QWidget *FindInFiles::createConfigWidget()
         filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
         QWidget *patternWidget = createPatternWidget();
         filePatternLabel->setBuddy(patternWidget);
-        gridLayout->addWidget(filePatternLabel, 1, 0);
-        gridLayout->addWidget(patternWidget, 1, 1, 1, 2);
+        gridLayout->addWidget(filePatternLabel, row, 0);
+        gridLayout->addWidget(patternWidget, row++, 1, 1, 2);
         m_configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
     }
     return m_configWidget;
@@ -170,6 +183,11 @@ void FindInFiles::setDirectory(const FileName &directory)
     m_directory->setFileName(directory);
 }
 
+FileName FindInFiles::directory() const
+{
+    return m_directory->fileName();
+}
+
 void FindInFiles::findOnFileSystem(const QString &path)
 {
     QTC_ASSERT(m_instance, return);
@@ -178,3 +196,8 @@ void FindInFiles::findOnFileSystem(const QString &path)
     m_instance->setDirectory(FileName::fromString(folder));
     FindPlugin::instance()->openFindDialog(m_instance);
 }
+
+FindInFiles *FindInFiles::instance()
+{
+    return m_instance;
+}
diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h
index e0bd247ac1f3ed2225ff1b18a9b6f88171e2ab77..c04982031158bb2fa5c9f18d479b60e9fd5cfa29 100644
--- a/src/plugins/texteditor/findinfiles.h
+++ b/src/plugins/texteditor/findinfiles.h
@@ -55,7 +55,12 @@ public:
     bool isValid() const;
 
     void setDirectory(const Utils::FileName &directory);
+    Utils::FileName directory() const;
     static void findOnFileSystem(const QString &path);
+    static FindInFiles *instance();
+
+signals:
+    void pathChanged(const QString &directory);
 
 protected:
     Utils::FileIterator *files(const QStringList &nameFilters,
diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp
index 29ee1ad42a92dae21ea648d71d11a90c9bbdcac9..8c729f9bdc041782f1c585e7e93b43a0e7380cd8 100644
--- a/src/plugins/texteditor/texteditorplugin.cpp
+++ b/src/plugins/texteditor/texteditorplugin.cpp
@@ -129,6 +129,10 @@ bool TextEditorPlugin::initialize(const QStringList &arguments, QString *errorMe
 
     m_baseTextMarkRegistry = new TextMarkRegistry(this);
 
+    addAutoReleasedObject(new FindInFiles);
+    addAutoReleasedObject(new FindInCurrentFile);
+    addAutoReleasedObject(new FindInOpenFiles);
+
     return true;
 }
 
@@ -146,10 +150,6 @@ void TextEditorPlugin::extensionsInitialized()
 
     updateSearchResultsTabWidth(m_settings->codeStyle()->currentTabSettings());
 
-    addAutoReleasedObject(new FindInFiles);
-    addAutoReleasedObject(new FindInCurrentFile);
-    addAutoReleasedObject(new FindInOpenFiles);
-
     Utils::MacroExpander *expander = Utils::globalMacroExpander();
 
     expander->registerVariable(kCurrentDocumentSelection,