FindInFiles: make possible to have more than one extension

This change adds a possibility to add more than
one external tool which can be used instead of internal finder.
Currently there is one extension: 'gitgrep'.
By default we use internal finder.

Change-Id: If644358552f3cea9ebda8308539322a1b6d3ab77
Reviewed-by: Orgad Shaneh's avatarOrgad Shaneh <orgads@gmail.com>
parent 4ae07377
......@@ -42,7 +42,6 @@
#include <utils/synchronousprocess.h>
#include <utils/textfileformat.h>
#include <QCheckBox>
#include <QFuture>
#include <QFutureWatcher>
#include <QHBoxLayout>
......@@ -58,7 +57,6 @@ class GitGrepParameters
{
public:
QString ref;
bool isEnabled = false;
};
using namespace Core;
......@@ -67,7 +65,6 @@ using VcsBase::VcsCommand;
namespace {
const char EnableGitGrep[] = "EnableGitGrep";
const char GitGrepRef[] = "GitGrepRef";
class GitGrepRunner : public QObject
......@@ -155,7 +152,7 @@ public:
else
arguments << "-F";
arguments << m_parameters.text;
GitGrepParameters params = m_parameters.extensionParameters.value<GitGrepParameters>();
GitGrepParameters params = m_parameters.searchEngineParameters.value<GitGrepParameters>();
if (!params.ref.isEmpty()) {
arguments << params.ref;
m_ref = params.ref + ':';
......@@ -213,10 +210,6 @@ GitGrep::GitGrep()
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 (optional)"));
m_treeLineEdit->setToolTip(tr("Can be HEAD, tag, local or remote branch, or a commit hash.\n"
......@@ -230,7 +223,7 @@ GitGrep::GitGrep()
m_widget, [this](const QString &path) {
m_widget->setEnabled(validateDirectory(path));
});
findInFiles->setFindExtension(this);
findInFiles->addSearchEngine(this);
}
GitGrep::~GitGrep()
......@@ -258,36 +251,28 @@ QWidget *GitGrep::widget() const
bool GitGrep::isEnabled() const
{
return m_widget->isEnabled() && m_enabledCheckBox->isChecked();
}
bool GitGrep::isEnabled(const TextEditor::FileFindParameters &parameters) const
{
return parameters.extensionParameters.value<GitGrepParameters>().isEnabled;
return m_widget->isEnabled();
}
QVariant GitGrep::parameters() const
{
GitGrepParameters params;
params.isEnabled = isEnabled();
params.ref = m_treeLineEdit->text();
return qVariantFromValue(params);
}
void GitGrep::readSettings(QSettings *settings)
{
m_enabledCheckBox->setChecked(settings->value(EnableGitGrep, false).toBool());
m_treeLineEdit->setText(settings->value(GitGrepRef).toString());
}
void GitGrep::writeSettings(QSettings *settings) const
{
settings->setValue(EnableGitGrep, m_enabledCheckBox->isChecked());
settings->setValue(GitGrepRef, m_treeLineEdit->text());
}
QFuture<FileSearchResultList> GitGrep::executeSearch(
const TextEditor::FileFindParameters &parameters)
QFuture<FileSearchResultList> GitGrep::executeSearch(const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind * /*baseFileFind*/)
{
return Utils::runAsync(GitGrepRunner::run, parameters);
}
......@@ -295,8 +280,8 @@ QFuture<FileSearchResultList> GitGrep::executeSearch(
IEditor *GitGrep::openEditor(const SearchResultItem &item,
const TextEditor::FileFindParameters &parameters)
{
GitGrepParameters params = parameters.extensionParameters.value<GitGrepParameters>();
if (!params.isEnabled || params.ref.isEmpty() || item.path.isEmpty())
GitGrepParameters params = parameters.searchEngineParameters.value<GitGrepParameters>();
if (params.ref.isEmpty() || item.path.isEmpty())
return nullptr;
const QString path = QDir::fromNativeSeparators(item.path.first());
QByteArray content;
......
......@@ -29,14 +29,12 @@
#include <QCoreApplication>
QT_FORWARD_DECLARE_CLASS(QCheckBox)
namespace Utils { class FancyLineEdit; }
namespace Git {
namespace Internal {
class GitGrep : public TextEditor::FileFindExtension
class GitGrep : public TextEditor::SearchEngine
{
Q_DECLARE_TR_FUNCTIONS(GitGrep)
......@@ -47,18 +45,17 @@ public:
QString toolTip() 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;
const TextEditor::FileFindParameters &parameters,
TextEditor::BaseFileFind *baseFileFind) override;
Core::IEditor *openEditor(const Core::SearchResultItem &item,
const TextEditor::FileFindParameters &parameters) override;
private:
QWidget *m_widget;
QCheckBox *m_enabledCheckBox;
Utils::FancyLineEdit *m_treeLineEdit;
};
......
......@@ -50,7 +50,6 @@
#include <QPointer>
#include <QComboBox>
#include <QLabel>
#include <QLabel>
using namespace Utils;
using namespace Core;
......@@ -58,6 +57,45 @@ using namespace Core;
namespace TextEditor {
namespace Internal {
namespace {
class InternalEngine : public TextEditor::SearchEngine
{
public:
InternalEngine() : m_widget(new QWidget) {}
~InternalEngine() override { delete m_widget;}
QString title() const override { return tr("Internal"); }
QString toolTip() const override { return QString(); }
QWidget *widget() const override { return m_widget; }
bool isEnabled() const override { return true; }
QVariant parameters() const override { return QVariant(); }
void readSettings(QSettings */*settings*/) override {}
void writeSettings(QSettings */*settings*/) const override {}
QFuture<Utils::FileSearchResultList> executeSearch(
const TextEditor::FileFindParameters &parameters,
BaseFileFind *baseFileFind) override
{
auto func = parameters.flags & FindRegularExpression
? Utils::findInFilesRegExp
: Utils::findInFiles;
return func(parameters.text,
baseFileFind->files(parameters.nameFilters, parameters.additionalParameters),
textDocumentFlagsForFindFlags(parameters.flags),
TextDocument::openedTextDocumentContents());
}
Core::IEditor *openEditor(const Core::SearchResultItem &/*item*/,
const TextEditor::FileFindParameters &/*parameters*/) override
{
return nullptr;
}
private:
QWidget *m_widget;
};
}
class CountingLabel : public QLabel
{
public:
......@@ -68,6 +106,7 @@ public:
class BaseFileFindPrivate
{
public:
~BaseFileFindPrivate() { delete m_internalSearchEngine; }
QMap<QFutureWatcher<FileSearchResultList> *, QPointer<SearchResult> > m_watchers;
QPointer<IFindSupport> m_currentFindSupport;
......@@ -75,7 +114,9 @@ public:
QStringListModel m_filterStrings;
QString m_filterSetting;
QPointer<QComboBox> m_filterCombo;
QPointer<FileFindExtension> m_extension;
QVector<SearchEngine *> m_searchEngines;
SearchEngine *m_internalSearchEngine;
int m_currentSearchEngineIndex = 0;
};
} // namespace Internal
......@@ -84,6 +125,8 @@ using namespace Internal;
BaseFileFind::BaseFileFind() : d(new BaseFileFindPrivate)
{
d->m_internalSearchEngine = new InternalEngine;
addSearchEngine(d->m_internalSearchEngine);
}
BaseFileFind::~BaseFileFind()
......@@ -119,7 +162,7 @@ QStringList BaseFileFind::fileNameFilters() const
{
QStringList filters;
if (d->m_filterCombo && !d->m_filterCombo->currentText().isEmpty()) {
const QStringList parts = d->m_filterCombo->currentText().split(QLatin1Char(','));
const QStringList parts = d->m_filterCombo->currentText().split(',');
foreach (const QString &part, parts) {
const QString filter = part.trimmed();
if (!filter.isEmpty())
......@@ -129,9 +172,21 @@ QStringList BaseFileFind::fileNameFilters() const
return filters;
}
FileFindExtension *BaseFileFind::extension() const
SearchEngine *BaseFileFind::currentSearchEngine() const
{
return d->m_extension.data();
if (d->m_searchEngines.isEmpty() || d->m_currentSearchEngineIndex == -1)
return nullptr;
return d->m_searchEngines[d->m_currentSearchEngineIndex];
}
QVector<SearchEngine *> BaseFileFind::searchEngines() const
{
return d->m_searchEngines;
}
void BaseFileFind::setCurrentSearchEngine(int index)
{
d->m_currentSearchEngineIndex = index;
}
void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
......@@ -141,12 +196,12 @@ void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
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)),
txt, searchMode, SearchResultWindow::PreserveCaseEnabled,
QString::fromLatin1("TextEditor"));
SearchResult *search = SearchResultWindow::instance()->startNewSearch(
label(),
tooltip.arg(IFindFilter::descriptionForFindFlags(findFlags)),
txt, searchMode, SearchResultWindow::PreserveCaseEnabled,
QString::fromLatin1("TextEditor"));
search->setTextToReplace(txt);
search->setSearchAgainSupported(true);
FileFindParameters parameters;
......@@ -154,8 +209,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();
parameters.searchEngineParameters = currentSearchEngine()->parameters();
parameters.searchEngineIndex = d->m_currentSearchEngineIndex;
search->setUserData(qVariantFromValue(parameters));
connect(search, &SearchResult::activated, this, &BaseFileFind::openEditor);
if (searchMode == SearchResultWindow::SearchAndReplace)
......@@ -201,10 +256,9 @@ void BaseFileFind::replaceAll(const QString &txt, FindFlags findFlags)
runNewSearch(txt, findFlags, SearchResultWindow::SearchAndReplace);
}
void BaseFileFind::setFindExtension(FileFindExtension *extension)
void BaseFileFind::addSearchEngine(SearchEngine *searchEngine)
{
QTC_ASSERT(!d->m_extension, return);
d->m_extension = extension;
d->m_searchEngines.push_back(searchEngine);
}
void BaseFileFind::doReplace(const QString &text,
......@@ -275,17 +329,19 @@ QWidget *BaseFileFind::createPatternWidget()
void BaseFileFind::writeCommonSettings(QSettings *settings)
{
settings->setValue(QLatin1String("filters"), d->m_filterStrings.stringList());
settings->setValue("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);
settings->setValue("currentFilter", d->m_filterCombo->currentText());
foreach (SearchEngine *searchEngine, d->m_searchEngines)
searchEngine->writeSettings(settings);
settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex);
}
void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaultFilter)
{
QStringList filters = settings->value(QLatin1String("filters")).toStringList();
const QVariant currentFilter = settings->value(QLatin1String("currentFilter"));
QStringList filters = settings->value("filters").toStringList();
const QVariant currentFilter = settings->value("currentFilter");
d->m_filterSetting = currentFilter.toString();
if (filters.isEmpty())
filters << defaultFilter;
......@@ -294,8 +350,11 @@ 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);
foreach (SearchEngine* searchEngine, d->m_searchEngines)
searchEngine->readSettings(settings);
const int currentSearchEngineIndex = settings->value("currentSearchEngineIndex", 0).toInt();
syncSearchEngineCombo(currentSearchEngineIndex);
}
void BaseFileFind::syncComboWithSettings(QComboBox *combo, const QString &setting)
......@@ -325,9 +384,8 @@ void BaseFileFind::openEditor(const SearchResultItem &item)
{
SearchResult *result = qobject_cast<SearchResult *>(sender());
FileFindParameters parameters = result->userData().value<FileFindParameters>();
IEditor *openedEditor = 0;
if (d->m_extension)
openedEditor = d->m_extension->openEditor(item, parameters);
IEditor *openedEditor =
d->m_searchEngines[parameters.searchEngineIndex]->openEditor(item, parameters);
if (!openedEditor) {
if (item.path.size() > 0) {
openedEditor = EditorManager::openEditorAt(QDir::fromNativeSeparators(item.path.first()),
......@@ -447,17 +505,7 @@ 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;
return func(parameters.text,
files(parameters.nameFilters, parameters.additionalParameters),
textDocumentFlagsForFindFlags(parameters.flags),
TextDocument::openedTextDocumentContents());
return d->m_searchEngines[parameters.searchEngineIndex]->executeSearch(parameters,this);
}
namespace Internal {
......
......@@ -57,25 +57,28 @@ public:
Core::FindFlags flags;
QStringList nameFilters;
QVariant additionalParameters;
QVariant extensionParameters;
int searchEngineIndex;
QVariant searchEngineParameters;
};
class TEXTEDITOR_EXPORT FileFindExtension : public QObject
class BaseFileFind;
class TEXTEDITOR_EXPORT SearchEngine : public QObject
{
public:
virtual ~FileFindExtension() {}
virtual ~SearchEngine() {}
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 &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;
const FileFindParameters &parameters, BaseFileFind *baseFileFind) = 0;
virtual Core::IEditor *openEditor(const Core::SearchResultItem &item,
const FileFindParameters &parameters) = 0;
};
class TEXTEDITOR_EXPORT BaseFileFind : public Core::IFindFilter
......@@ -90,16 +93,16 @@ 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);
void addSearchEngine(SearchEngine *searchEngine);
/* returns the list of unique files that were passed in items */
static QStringList replaceAll(const QString &txt,
const QList<Core::SearchResultItem> &items,
bool preserveCase = false);
protected:
virtual Utils::FileIterator *files(const QStringList &nameFilters,
const QVariant &additionalParameters) const = 0;
protected:
virtual QVariant additionalParameters() const = 0;
QVariant getAdditionalParameters(Core::SearchResult *search);
virtual QString label() const = 0; // see Core::SearchResultWindow::startNewSearch
......@@ -113,7 +116,11 @@ protected:
void syncComboWithSettings(QComboBox *combo, const QString &setting);
void updateComboEntries(QComboBox *combo, bool onTop);
QStringList fileNameFilters() const;
FileFindExtension *extension() const;
SearchEngine *currentSearchEngine() const;
QVector<SearchEngine *> searchEngines() const;
void setCurrentSearchEngine(int index);
virtual void syncSearchEngineCombo(int /*selectedSearchEngineIndex*/) {}
private:
void displayResult(int index);
......
......@@ -41,6 +41,8 @@
#include <QFileDialog>
#include <QLabel>
#include <QHBoxLayout>
#include <QStackedWidget>
#include <QComboBox>
using namespace Core;
using namespace TextEditor;
......@@ -64,7 +66,7 @@ FindInFiles::~FindInFiles()
bool FindInFiles::isValid() const
{
return m_directory->isValid();
return m_directory->isValid() && currentSearchEngine()->isEnabled();
}
QString FindInFiles::id() const
......@@ -92,11 +94,8 @@ QVariant FindInFiles::additionalParameters() const
QString FindInFiles::label() const
{
QString title = tr("Directory");
if (FileFindExtension *ext = extension()) {
if (ext->isEnabled())
title = ext->title();
}
QString title = currentSearchEngine()->title();
const QChar slash = QLatin1Char('/');
const QStringList &nonEmptyComponents = path().toFileInfo().absoluteFilePath()
.split(slash, QString::SkipEmptyParts);
......@@ -107,10 +106,31 @@ QString FindInFiles::label() const
QString FindInFiles::toolTip() const
{
//: %3 is filled by BaseFileFind::runNewSearch
return tr("Path: %1\nFilter: %2\n%3")
//: the last arg is filled by BaseFileFind::runNewSearch
QString tooltip = tr("Path: %1\nFilter: %2\n%3")
.arg(path().toUserOutput())
.arg(fileNameFilters().join(QLatin1Char(',')));
const QString searchEngineToolTip = currentSearchEngine()->toolTip();
if (!searchEngineToolTip.isEmpty())
tooltip = tooltip.arg(searchEngineToolTip);
return tooltip;
}
void FindInFiles::syncSearchEngineCombo(int selectedSearchEngineIndex)
{
QTC_ASSERT(m_searchEngineCombo && selectedSearchEngineIndex >= 0
&& selectedSearchEngineIndex < searchEngines().size(), return);
m_searchEngineCombo->setCurrentIndex(selectedSearchEngineIndex);
searchEnginesSelectionChanged(selectedSearchEngineIndex);
}
void FindInFiles::searchEnginesSelectionChanged(int index)
{
setCurrentSearchEngine(index);
m_searchEngineWidget->setCurrentIndex(index);
}
QWidget *FindInFiles::createConfigWidget()
......@@ -122,8 +142,22 @@ QWidget *FindInFiles::createConfigWidget()
m_configWidget->setLayout(gridLayout);
int row = 0;
if (FileFindExtension *ext = extension())
gridLayout->addWidget(ext->widget(), row++, 1, 1, 2);
auto searchEngineLabel = new QLabel(tr("Search engine:"));
gridLayout->addWidget(searchEngineLabel, row, 0, Qt::AlignRight);
m_searchEngineCombo = new QComboBox;
auto cc = static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged);
connect(m_searchEngineCombo, cc, this, &FindInFiles::searchEnginesSelectionChanged);
connect(m_searchEngineCombo, cc, this, &FindInFiles::enabledChanged);
searchEngineLabel->setBuddy(m_searchEngineCombo);
gridLayout->addWidget(m_searchEngineCombo, row, 1);
m_searchEngineWidget = new QStackedWidget(m_configWidget);
foreach (SearchEngine *searchEngine, searchEngines()) {
m_searchEngineWidget->addWidget(searchEngine->widget());
m_searchEngineCombo->addItem(searchEngine->title());
}
gridLayout->addWidget(m_searchEngineWidget, row++, 2);
QLabel *dirLabel = new QLabel(tr("Director&y:"));
gridLayout->addWidget(dirLabel, row, 0, Qt::AlignRight);
......@@ -131,9 +165,10 @@ QWidget *FindInFiles::createConfigWidget()
m_directory->setExpectedKind(PathChooser::ExistingDirectory);
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);
this, [this](const QString &path) {
emit FindInFiles::pathChanged(path);
emit FindInFiles::enabledChanged(isEnabled());
});
m_directory->setHistoryCompleter(QLatin1String(HistoryKey),
/*restoreLastItemFromHistory=*/ true);
if (!HistoryCompleter::historyExistsFor(QLatin1String(HistoryKey))) {
......
......@@ -32,7 +32,10 @@
#include <QPointer>
#include <QStringListModel>
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_BEGIN_NAMESPACE
class QLabel;
class QStackedWidget;
QT_END_NAMESPACE
namespace Utils { class PathChooser; }
......@@ -67,12 +70,16 @@ protected:
QVariant additionalParameters() const;
QString label() const;
QString toolTip() const;
void syncSearchEngineCombo(int selectedSearchEngineIndex) override;
private:
void searchEnginesSelectionChanged(int index);
Utils::FileName path() const;
QPointer<QWidget> m_configWidget;
QPointer<Utils::PathChooser> m_directory;
QStackedWidget *m_searchEngineWidget = nullptr;
QComboBox *m_searchEngineCombo = nullptr;
};
} // namespace TextEditor
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment