2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2008-12-02 16:19:05 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "basefilefind.h"
|
2023-01-17 18:02:43 +01:00
|
|
|
|
|
|
|
|
#include "refactoringchanges.h"
|
2015-02-26 13:22:35 +01:00
|
|
|
#include "textdocument.h"
|
2023-01-17 18:02:43 +01:00
|
|
|
#include "texteditortr.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-08-09 11:08:01 +02:00
|
|
|
#include <aggregation/aggregate.h>
|
2023-01-17 18:02:43 +01:00
|
|
|
|
2012-10-15 11:53:22 +02:00
|
|
|
#include <coreplugin/dialogs/readonlyfilesdialog.h>
|
2012-02-14 16:43:51 +01:00
|
|
|
#include <coreplugin/documentmanager.h>
|
2015-02-26 13:22:35 +01:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2014-01-13 16:17:34 +01:00
|
|
|
#include <coreplugin/find/ifindsupport.h>
|
2021-06-07 13:17:25 +02:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/progressmanager/futureprogress.h>
|
|
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
2023-01-17 18:02:43 +01:00
|
|
|
|
2016-12-09 13:33:12 +01:00
|
|
|
#include <utils/algorithm.h>
|
2015-03-10 11:35:09 +01:00
|
|
|
#include <utils/fadingindicator.h>
|
2013-03-26 12:32:52 +01:00
|
|
|
#include <utils/filesearch.h>
|
2021-06-07 13:17:25 +02:00
|
|
|
#include <utils/futuresynchronizer.h>
|
2023-06-01 20:43:37 +02:00
|
|
|
#include <utils/process.h>
|
2015-03-10 11:35:09 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/stylehelper.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QSettings>
|
|
|
|
|
#include <QHash>
|
|
|
|
|
#include <QPair>
|
2023-06-05 09:39:32 +02:00
|
|
|
#include <QPromise>
|
2013-03-26 12:32:52 +01:00
|
|
|
#include <QStringListModel>
|
|
|
|
|
#include <QFutureWatcher>
|
|
|
|
|
#include <QPointer>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QComboBox>
|
2013-03-26 12:32:52 +01:00
|
|
|
#include <QLabel>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
using namespace Core;
|
|
|
|
|
|
2013-03-26 12:32:52 +01:00
|
|
|
namespace TextEditor {
|
2023-06-01 20:43:37 +02:00
|
|
|
|
|
|
|
|
static std::optional<QRegularExpression> regExpFromParameters(const FileFindParameters ¶meters)
|
|
|
|
|
{
|
|
|
|
|
if (!(parameters.flags & FindRegularExpression))
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
const QRegularExpression::PatternOptions options = parameters.flags & FindCaseSensitively
|
|
|
|
|
? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption;
|
|
|
|
|
QRegularExpression regExp;
|
|
|
|
|
regExp.setPattern(parameters.text);
|
|
|
|
|
regExp.setPatternOptions(options);
|
|
|
|
|
return regExp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void searchInProcessOutput(QPromise<SearchResultItems> &promise,
|
|
|
|
|
const FileFindParameters ¶meters,
|
|
|
|
|
const ProcessSetupHandler &processSetupHandler,
|
|
|
|
|
const ProcessOutputParser &processOutputParser)
|
|
|
|
|
{
|
|
|
|
|
if (promise.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QEventLoop loop;
|
|
|
|
|
|
|
|
|
|
Process process;
|
|
|
|
|
processSetupHandler(process);
|
|
|
|
|
|
|
|
|
|
const std::optional<QRegularExpression> regExp = regExpFromParameters(parameters);
|
|
|
|
|
QStringList outputBuffer;
|
|
|
|
|
// The states transition exactly in this order:
|
|
|
|
|
enum State { BelowLimit, AboveLimit, Paused, Resumed };
|
|
|
|
|
State state = BelowLimit;
|
|
|
|
|
int reportedItemsCount = 0;
|
|
|
|
|
QFuture<void> future(promise.future());
|
|
|
|
|
process.setStdOutCallback([&](const QString &output) {
|
|
|
|
|
if (promise.isCanceled()) {
|
|
|
|
|
process.close();
|
|
|
|
|
loop.quit();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// The SearchResultWidget is going to pause the search anyway, so start buffering
|
|
|
|
|
// the output.
|
|
|
|
|
if (state == AboveLimit || state == Paused) {
|
|
|
|
|
outputBuffer.append(output);
|
|
|
|
|
} else {
|
|
|
|
|
const SearchResultItems items = processOutputParser(future, output, regExp);
|
|
|
|
|
if (!items.isEmpty())
|
|
|
|
|
promise.addResult(items);
|
|
|
|
|
reportedItemsCount += items.size();
|
|
|
|
|
if (state == BelowLimit && reportedItemsCount > 200000)
|
|
|
|
|
state = AboveLimit;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(&process, &Process::done, &loop, [&loop, &promise, &state] {
|
|
|
|
|
if (state == BelowLimit || state == Resumed || promise.isCanceled())
|
|
|
|
|
loop.quit();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (promise.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
process.start();
|
|
|
|
|
if (process.state() == QProcess::NotRunning)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QFutureWatcher<void> watcher;
|
|
|
|
|
QObject::connect(&watcher, &QFutureWatcherBase::canceled, &loop, [&process, &loop] {
|
|
|
|
|
process.close();
|
|
|
|
|
loop.quit();
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(&watcher, &QFutureWatcherBase::paused, &loop, [&state] { state = Paused; });
|
|
|
|
|
QObject::connect(&watcher, &QFutureWatcherBase::resumed, &loop, [&] {
|
|
|
|
|
state = Resumed;
|
|
|
|
|
for (const QString &output : outputBuffer) {
|
|
|
|
|
if (promise.isCanceled()) {
|
|
|
|
|
process.close();
|
|
|
|
|
loop.quit();
|
|
|
|
|
}
|
|
|
|
|
const SearchResultItems items = processOutputParser(future, output, regExp);
|
|
|
|
|
if (!items.isEmpty())
|
|
|
|
|
promise.addResult(items);
|
|
|
|
|
}
|
|
|
|
|
outputBuffer.clear();
|
|
|
|
|
if (process.state() == QProcess::NotRunning)
|
|
|
|
|
loop.quit();
|
|
|
|
|
});
|
|
|
|
|
watcher.setFuture(future);
|
|
|
|
|
if (promise.isCanceled())
|
|
|
|
|
return;
|
|
|
|
|
loop.exec(QEventLoop::ExcludeUserInputEvents);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-26 12:32:52 +01:00
|
|
|
namespace Internal {
|
2014-01-13 16:17:34 +01:00
|
|
|
|
2016-03-16 22:37:24 +01:00
|
|
|
class InternalEngine : public TextEditor::SearchEngine
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
InternalEngine() : m_widget(new QWidget) {}
|
|
|
|
|
~InternalEngine() override { delete m_widget;}
|
2023-01-17 18:02:43 +01:00
|
|
|
QString title() const override { return Tr::tr("Internal"); }
|
2019-02-18 15:05:13 +01:00
|
|
|
QString toolTip() const override { return {}; }
|
2016-03-16 22:37:24 +01:00
|
|
|
QWidget *widget() const override { return m_widget; }
|
2016-11-24 01:15:57 +01:00
|
|
|
void readSettings(QSettings * /*settings*/) override {}
|
|
|
|
|
void writeSettings(QSettings * /*settings*/) const override {}
|
2023-06-28 19:14:50 +02:00
|
|
|
SearchExecutor searchExecutor() const override
|
2016-03-16 22:37:24 +01:00
|
|
|
{
|
2023-06-28 19:14:50 +02:00
|
|
|
return [](const FileFindParameters ¶meters) {
|
|
|
|
|
return Utils::findInFiles(parameters.text, parameters.fileContainerProvider(),
|
|
|
|
|
parameters.flags, TextDocument::openedTextDocumentContents());
|
|
|
|
|
};
|
2016-03-16 22:37:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QWidget *m_widget;
|
|
|
|
|
};
|
|
|
|
|
|
2016-11-21 10:58:19 +01:00
|
|
|
class SearchEnginePrivate
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bool isEnabled = true;
|
|
|
|
|
};
|
2016-03-16 22:37:24 +01:00
|
|
|
|
2015-11-30 23:14:41 +02:00
|
|
|
class CountingLabel : public QLabel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CountingLabel();
|
|
|
|
|
void updateCount(int count);
|
|
|
|
|
};
|
|
|
|
|
|
2014-01-13 16:17:34 +01:00
|
|
|
class BaseFileFindPrivate
|
|
|
|
|
{
|
2013-03-26 12:32:52 +01:00
|
|
|
public:
|
2015-02-03 23:46:35 +02:00
|
|
|
QPointer<IFindSupport> m_currentFindSupport;
|
2013-03-26 12:32:52 +01:00
|
|
|
|
2021-06-07 13:17:25 +02:00
|
|
|
Utils::FutureSynchronizer m_futureSynchronizer;
|
2018-09-20 01:16:01 +03:00
|
|
|
QLabel *m_resultLabel = nullptr;
|
2016-12-09 13:33:12 +01:00
|
|
|
// models in native path format
|
2013-03-26 12:32:52 +01:00
|
|
|
QStringListModel m_filterStrings;
|
2016-12-09 13:33:12 +01:00
|
|
|
QStringListModel m_exclusionStrings;
|
|
|
|
|
// current filter in portable path format
|
2013-03-26 12:32:52 +01:00
|
|
|
QString m_filterSetting;
|
2016-12-09 13:33:12 +01:00
|
|
|
QString m_exclusionSetting;
|
2013-03-26 12:32:52 +01:00
|
|
|
QPointer<QComboBox> m_filterCombo;
|
2016-12-09 13:33:12 +01:00
|
|
|
QPointer<QComboBox> m_exclusionCombo;
|
2016-03-16 22:37:24 +01:00
|
|
|
QVector<SearchEngine *> m_searchEngines;
|
2019-02-18 15:05:13 +01:00
|
|
|
InternalEngine m_internalSearchEngine;
|
2016-11-21 10:58:19 +01:00
|
|
|
int m_currentSearchEngineIndex = -1;
|
2013-03-26 12:32:52 +01:00
|
|
|
};
|
2014-01-13 16:17:34 +01:00
|
|
|
|
2013-03-26 12:32:52 +01:00
|
|
|
} // namespace Internal
|
|
|
|
|
|
2016-11-29 12:03:46 +01:00
|
|
|
static void syncComboWithSettings(QComboBox *combo, const QString &setting)
|
|
|
|
|
{
|
|
|
|
|
if (!combo)
|
|
|
|
|
return;
|
2016-12-09 13:33:12 +01:00
|
|
|
const QString &nativeSettings = QDir::toNativeSeparators(setting);
|
|
|
|
|
int index = combo->findText(nativeSettings);
|
2016-11-29 12:03:46 +01:00
|
|
|
if (index < 0)
|
2016-12-09 13:33:12 +01:00
|
|
|
combo->setEditText(nativeSettings);
|
2016-11-29 12:03:46 +01:00
|
|
|
else
|
|
|
|
|
combo->setCurrentIndex(index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void updateComboEntries(QComboBox *combo, bool onTop)
|
|
|
|
|
{
|
|
|
|
|
int index = combo->findText(combo->currentText());
|
|
|
|
|
if (index < 0) {
|
|
|
|
|
if (onTop)
|
|
|
|
|
combo->insertItem(0, combo->currentText());
|
|
|
|
|
else
|
|
|
|
|
combo->addItem(combo->currentText());
|
|
|
|
|
combo->setCurrentIndex(combo->findText(combo->currentText()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-13 16:17:34 +01:00
|
|
|
using namespace Internal;
|
2013-03-26 12:32:52 +01:00
|
|
|
|
2018-02-02 10:08:21 +01:00
|
|
|
SearchEngine::SearchEngine(QObject *parent)
|
|
|
|
|
: QObject(parent), d(new SearchEnginePrivate)
|
2016-11-21 10:58:19 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SearchEngine::~SearchEngine()
|
|
|
|
|
{
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SearchEngine::isEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return d->isEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SearchEngine::setEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
if (enabled == d->isEnabled)
|
|
|
|
|
return;
|
|
|
|
|
d->isEnabled = enabled;
|
|
|
|
|
emit enabledChanged(d->isEnabled);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-26 12:32:52 +01:00
|
|
|
BaseFileFind::BaseFileFind() : d(new BaseFileFindPrivate)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2019-02-18 15:05:13 +01:00
|
|
|
addSearchEngine(&d->m_internalSearchEngine);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-08-18 14:23:06 +02:00
|
|
|
BaseFileFind::~BaseFileFind()
|
|
|
|
|
{
|
2013-03-26 12:32:52 +01:00
|
|
|
delete d;
|
2011-08-18 14:23:06 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
bool BaseFileFind::isEnabled() const
|
|
|
|
|
{
|
2011-12-12 15:16:49 +01:00
|
|
|
return true;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList BaseFileFind::fileNameFilters() const
|
|
|
|
|
{
|
2016-12-09 13:33:12 +01:00
|
|
|
if (d->m_filterCombo)
|
|
|
|
|
return splitFilterUiText(d->m_filterCombo->currentText());
|
2019-02-18 15:05:13 +01:00
|
|
|
return {};
|
2016-12-09 13:33:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList BaseFileFind::fileExclusionFilters() const
|
|
|
|
|
{
|
|
|
|
|
if (d->m_exclusionCombo)
|
|
|
|
|
return splitFilterUiText(d->m_exclusionCombo->currentText());
|
2019-02-18 15:05:13 +01:00
|
|
|
return {};
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-16 22:37:24 +01:00
|
|
|
SearchEngine *BaseFileFind::currentSearchEngine() const
|
2016-01-28 23:37:10 +02:00
|
|
|
{
|
2016-03-16 22:37:24 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2016-11-21 10:58:19 +01:00
|
|
|
if (d->m_currentSearchEngineIndex == index)
|
|
|
|
|
return;
|
2016-03-16 22:37:24 +01:00
|
|
|
d->m_currentSearchEngineIndex = index;
|
2016-11-21 10:58:19 +01:00
|
|
|
emit currentSearchEngineChanged();
|
2016-01-28 23:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
|
2011-08-09 11:08:01 +02:00
|
|
|
SearchResultWindow::SearchMode searchMode)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2018-09-20 01:16:01 +03:00
|
|
|
d->m_currentFindSupport = nullptr;
|
2013-03-26 12:32:52 +01:00
|
|
|
if (d->m_filterCombo)
|
|
|
|
|
updateComboEntries(d->m_filterCombo, true);
|
2016-12-09 13:33:12 +01:00
|
|
|
if (d->m_exclusionCombo)
|
|
|
|
|
updateComboEntries(d->m_exclusionCombo, true);
|
2019-02-18 15:05:13 +01:00
|
|
|
const QString tooltip = toolTip();
|
2016-03-16 22:37:24 +01:00
|
|
|
|
|
|
|
|
SearchResult *search = SearchResultWindow::instance()->startNewSearch(
|
|
|
|
|
label(),
|
|
|
|
|
tooltip.arg(IFindFilter::descriptionForFindFlags(findFlags)),
|
|
|
|
|
txt, searchMode, SearchResultWindow::PreserveCaseEnabled,
|
|
|
|
|
QString::fromLatin1("TextEditor"));
|
2023-06-28 09:55:53 +02:00
|
|
|
setupSearch(search);
|
2011-12-12 15:16:49 +01:00
|
|
|
search->setTextToReplace(txt);
|
2011-12-13 10:50:57 +01:00
|
|
|
search->setSearchAgainSupported(true);
|
2023-06-28 19:14:50 +02:00
|
|
|
SearchEngine *searchEngine = currentSearchEngine();
|
2011-12-13 10:50:57 +01:00
|
|
|
FileFindParameters parameters;
|
|
|
|
|
parameters.text = txt;
|
|
|
|
|
parameters.flags = findFlags;
|
|
|
|
|
parameters.nameFilters = fileNameFilters();
|
2016-12-09 13:33:12 +01:00
|
|
|
parameters.exclusionFilters = fileExclusionFilters();
|
2011-12-13 10:50:57 +01:00
|
|
|
parameters.additionalParameters = additionalParameters();
|
2023-06-28 17:03:58 +02:00
|
|
|
parameters.fileContainerProvider = fileContainerProvider();
|
2023-06-28 19:14:50 +02:00
|
|
|
parameters.editorOpener = searchEngine->editorOpener();
|
|
|
|
|
parameters.searchExecutor = searchEngine->searchExecutor();
|
2023-06-28 17:03:58 +02:00
|
|
|
|
2019-05-27 13:32:20 +02:00
|
|
|
search->setUserData(QVariant::fromValue(parameters));
|
2019-02-18 15:05:13 +01:00
|
|
|
connect(search, &SearchResult::activated, this, [this, search](const SearchResultItem &item) {
|
|
|
|
|
openEditor(search, item);
|
|
|
|
|
});
|
2015-12-13 01:18:33 +02:00
|
|
|
if (searchMode == SearchResultWindow::SearchAndReplace)
|
|
|
|
|
connect(search, &SearchResult::replaceButtonClicked, this, &BaseFileFind::doReplace);
|
|
|
|
|
connect(search, &SearchResult::visibilityChanged, this, &BaseFileFind::hideHighlightAll);
|
2019-02-18 15:05:13 +01:00
|
|
|
connect(search, &SearchResult::searchAgainRequested, this, [this, search] {
|
|
|
|
|
searchAgain(search);
|
|
|
|
|
});
|
2013-02-12 18:37:14 +01:00
|
|
|
|
2011-12-13 10:50:57 +01:00
|
|
|
runSearch(search);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
void BaseFileFind::runSearch(SearchResult *search)
|
2011-12-13 10:50:57 +01:00
|
|
|
{
|
2019-02-18 15:05:13 +01:00
|
|
|
const FileFindParameters parameters = search->userData().value<FileFindParameters>();
|
2015-02-03 23:46:35 +02:00
|
|
|
SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch|IOutputPane::WithFocus));
|
2023-05-05 14:05:10 +02:00
|
|
|
auto watcher = new QFutureWatcher<SearchResultItems>;
|
2011-12-13 10:50:57 +01:00
|
|
|
watcher->setPendingResultsLimit(1);
|
2016-11-29 16:17:34 +01:00
|
|
|
// search is deleted if it is removed from search panel
|
|
|
|
|
connect(search, &QObject::destroyed, watcher, &QFutureWatcherBase::cancel);
|
2022-07-20 12:46:13 +02:00
|
|
|
connect(search, &SearchResult::canceled, watcher, &QFutureWatcherBase::cancel);
|
2016-11-29 16:17:34 +01:00
|
|
|
connect(search, &SearchResult::paused, watcher, [watcher](bool paused) {
|
|
|
|
|
if (!paused || watcher->isRunning()) // guard against pausing when the search is finished
|
|
|
|
|
watcher->setPaused(paused);
|
|
|
|
|
});
|
|
|
|
|
connect(watcher, &QFutureWatcherBase::resultReadyAt, search, [watcher, search](int index) {
|
2023-05-05 14:05:10 +02:00
|
|
|
search->addResults(watcher->resultAt(index), SearchResult::AddOrdered);
|
2016-11-29 16:17:34 +01:00
|
|
|
});
|
|
|
|
|
// auto-delete:
|
|
|
|
|
connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
|
|
|
|
|
connect(watcher, &QFutureWatcherBase::finished, search, [watcher, search]() {
|
|
|
|
|
search->finishSearch(watcher->isCanceled());
|
|
|
|
|
});
|
2023-05-05 14:05:10 +02:00
|
|
|
QFuture<SearchResultItems> future = executeSearch(parameters);
|
2021-06-07 13:17:25 +02:00
|
|
|
watcher->setFuture(future);
|
|
|
|
|
d->m_futureSynchronizer.addFuture(future);
|
2021-06-08 10:13:09 +02:00
|
|
|
FutureProgress *progress = ProgressManager::addTask(future,
|
2023-01-17 18:02:43 +01:00
|
|
|
Tr::tr("Searching"),
|
2019-03-29 15:05:31 +01:00
|
|
|
Constants::TASK_SEARCH);
|
|
|
|
|
connect(search, &SearchResult::countChanged, progress, [progress](int c) {
|
2023-01-17 18:02:43 +01:00
|
|
|
progress->setSubtitle(Tr::tr("%n found.", nullptr, c));
|
2019-03-29 15:05:31 +01:00
|
|
|
});
|
|
|
|
|
progress->setSubtitleVisibleInStatusBar(true);
|
2015-12-13 01:18:33 +02:00
|
|
|
connect(progress, &FutureProgress::clicked, search, &SearchResult::popup);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
void BaseFileFind::findAll(const QString &txt, FindFlags findFlags)
|
2011-08-09 11:08:01 +02:00
|
|
|
{
|
|
|
|
|
runNewSearch(txt, findFlags, SearchResultWindow::SearchOnly);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
void BaseFileFind::replaceAll(const QString &txt, FindFlags findFlags)
|
2009-12-21 11:08:20 +01:00
|
|
|
{
|
2011-08-09 11:08:01 +02:00
|
|
|
runNewSearch(txt, findFlags, SearchResultWindow::SearchAndReplace);
|
2009-12-21 11:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-16 22:37:24 +01:00
|
|
|
void BaseFileFind::addSearchEngine(SearchEngine *searchEngine)
|
2016-01-28 23:37:10 +02:00
|
|
|
{
|
2016-03-16 22:37:24 +01:00
|
|
|
d->m_searchEngines.push_back(searchEngine);
|
2016-11-28 15:41:56 +01:00
|
|
|
if (d->m_searchEngines.size() == 1) // empty before, make sure we have a current engine
|
|
|
|
|
setCurrentSearchEngine(0);
|
2016-01-28 23:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-05 19:12:47 +02:00
|
|
|
void BaseFileFind::doReplace(const QString &text, const SearchResultItems &items,
|
2012-11-30 16:15:07 +01:00
|
|
|
bool preserveCase)
|
2009-12-21 11:08:20 +01:00
|
|
|
{
|
2021-06-22 08:57:36 +02:00
|
|
|
const FilePaths files = replaceAll(text, items, preserveCase);
|
2009-12-21 11:08:20 +01:00
|
|
|
if (!files.isEmpty()) {
|
2020-06-02 09:10:40 +02:00
|
|
|
Utils::FadingIndicator::showText(ICore::dialogParent(),
|
2023-01-17 18:02:43 +01:00
|
|
|
Tr::tr("%n occurrences replaced.", nullptr, items.size()),
|
2015-03-10 11:35:09 +01:00
|
|
|
Utils::FadingIndicator::SmallText);
|
2013-08-30 09:22:42 +02:00
|
|
|
DocumentManager::notifyFilesChangedInternally(files);
|
2015-02-03 23:46:35 +02:00
|
|
|
SearchResultWindow::instance()->hide();
|
2009-12-21 11:08:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-09 13:33:12 +01:00
|
|
|
static QComboBox *createCombo(QAbstractItemModel *model)
|
|
|
|
|
{
|
|
|
|
|
auto combo = new QComboBox;
|
|
|
|
|
combo->setEditable(true);
|
|
|
|
|
combo->setModel(model);
|
|
|
|
|
combo->setMaxCount(10);
|
|
|
|
|
combo->setMinimumContentsLength(10);
|
|
|
|
|
combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
|
|
|
|
|
combo->setInsertPolicy(QComboBox::InsertAtBottom);
|
|
|
|
|
combo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
|
|
return combo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QLabel *createLabel(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
auto filePatternLabel = new QLabel(text);
|
|
|
|
|
filePatternLabel->setMinimumWidth(80);
|
|
|
|
|
filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
|
|
|
|
filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
|
|
return filePatternLabel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QPair<QWidget *, QWidget *>> BaseFileFind::createPatternWidgets()
|
|
|
|
|
{
|
2017-02-23 15:42:36 +01:00
|
|
|
QLabel *filterLabel = createLabel(msgFilePatternLabel());
|
2016-12-09 13:33:12 +01:00
|
|
|
d->m_filterCombo = createCombo(&d->m_filterStrings);
|
2017-02-23 15:42:36 +01:00
|
|
|
d->m_filterCombo->setToolTip(msgFilePatternToolTip());
|
2016-12-09 13:33:12 +01:00
|
|
|
filterLabel->setBuddy(d->m_filterCombo);
|
2013-03-26 12:32:52 +01:00
|
|
|
syncComboWithSettings(d->m_filterCombo, d->m_filterSetting);
|
2017-02-23 15:42:36 +01:00
|
|
|
QLabel *exclusionLabel = createLabel(msgExclusionPatternLabel());
|
2016-12-09 13:33:12 +01:00
|
|
|
d->m_exclusionCombo = createCombo(&d->m_exclusionStrings);
|
2023-05-19 16:55:46 +08:00
|
|
|
d->m_exclusionCombo->setToolTip(msgFilePatternToolTip(Utils::InclusionType::Excluded));
|
2016-12-09 13:33:12 +01:00
|
|
|
exclusionLabel->setBuddy(d->m_exclusionCombo);
|
|
|
|
|
syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting);
|
2022-09-30 10:35:15 +02:00
|
|
|
return {{filterLabel, d->m_filterCombo}, {exclusionLabel, d->m_exclusionCombo}};
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BaseFileFind::writeCommonSettings(QSettings *settings)
|
|
|
|
|
{
|
2019-02-18 15:05:13 +01:00
|
|
|
const auto fromNativeSeparators = [](const QStringList &files) -> QStringList {
|
2016-12-09 13:33:12 +01:00
|
|
|
return Utils::transform(files, &QDir::fromNativeSeparators);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
settings->setValue("filters", fromNativeSeparators(d->m_filterStrings.stringList()));
|
2013-03-26 12:32:52 +01:00
|
|
|
if (d->m_filterCombo)
|
2016-12-09 13:33:12 +01:00
|
|
|
settings->setValue("currentFilter",
|
|
|
|
|
QDir::fromNativeSeparators(d->m_filterCombo->currentText()));
|
|
|
|
|
settings->setValue("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList()));
|
|
|
|
|
if (d->m_exclusionCombo)
|
|
|
|
|
settings->setValue("currentExclusionFilter",
|
|
|
|
|
QDir::fromNativeSeparators(d->m_exclusionCombo->currentText()));
|
2016-03-16 22:37:24 +01:00
|
|
|
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const SearchEngine *searchEngine : std::as_const(d->m_searchEngines))
|
2016-03-16 22:37:24 +01:00
|
|
|
searchEngine->writeSettings(settings);
|
|
|
|
|
settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-12-09 13:33:12 +01:00
|
|
|
void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaultFilter,
|
|
|
|
|
const QString &defaultExclusionFilter)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2019-02-18 15:05:13 +01:00
|
|
|
const auto toNativeSeparators = [](const QStringList &files) -> QStringList {
|
2016-12-09 13:33:12 +01:00
|
|
|
return Utils::transform(files, &QDir::toNativeSeparators);
|
|
|
|
|
};
|
|
|
|
|
|
2019-02-18 15:05:13 +01:00
|
|
|
const QStringList filterSetting = settings->value("filters").toStringList();
|
|
|
|
|
const QStringList filters = filterSetting.isEmpty() ? QStringList(defaultFilter)
|
|
|
|
|
: filterSetting;
|
2016-12-09 13:33:12 +01:00
|
|
|
const QVariant currentFilter = settings->value("currentFilter");
|
|
|
|
|
d->m_filterSetting = currentFilter.isValid() ? currentFilter.toString()
|
|
|
|
|
: filters.first();
|
|
|
|
|
d->m_filterStrings.setStringList(toNativeSeparators(filters));
|
2013-03-26 12:32:52 +01:00
|
|
|
if (d->m_filterCombo)
|
|
|
|
|
syncComboWithSettings(d->m_filterCombo, d->m_filterSetting);
|
2016-03-16 22:37:24 +01:00
|
|
|
|
2016-12-09 13:33:12 +01:00
|
|
|
QStringList exclusionFilters = settings->value("exclusionFilters").toStringList();
|
2018-11-23 11:36:42 +01:00
|
|
|
if (!exclusionFilters.contains(defaultExclusionFilter))
|
2016-12-09 13:33:12 +01:00
|
|
|
exclusionFilters << defaultExclusionFilter;
|
|
|
|
|
const QVariant currentExclusionFilter = settings->value("currentExclusionFilter");
|
|
|
|
|
d->m_exclusionSetting = currentExclusionFilter.isValid() ? currentExclusionFilter.toString()
|
|
|
|
|
: exclusionFilters.first();
|
|
|
|
|
d->m_exclusionStrings.setStringList(toNativeSeparators(exclusionFilters));
|
|
|
|
|
if (d->m_exclusionCombo)
|
|
|
|
|
syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting);
|
|
|
|
|
|
2022-10-07 14:46:06 +02:00
|
|
|
for (SearchEngine* searchEngine : std::as_const(d->m_searchEngines))
|
2016-03-16 22:37:24 +01:00
|
|
|
searchEngine->readSettings(settings);
|
|
|
|
|
const int currentSearchEngineIndex = settings->value("currentSearchEngineIndex", 0).toInt();
|
|
|
|
|
syncSearchEngineCombo(currentSearchEngineIndex);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-18 15:05:13 +01:00
|
|
|
void BaseFileFind::openEditor(SearchResult *result, const SearchResultItem &item)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2019-02-18 15:05:13 +01:00
|
|
|
const FileFindParameters parameters = result->userData().value<FileFindParameters>();
|
2023-06-28 18:25:42 +02:00
|
|
|
IEditor *openedEditor = parameters.editorOpener ? parameters.editorOpener(item, parameters)
|
|
|
|
|
: nullptr;
|
2017-09-22 16:36:26 +02:00
|
|
|
if (!openedEditor)
|
2020-09-29 10:24:48 +02:00
|
|
|
EditorManager::openEditorAtSearchResult(item, Id(), EditorManager::DoNotSwitchToDesignMode);
|
2013-03-26 12:32:52 +01:00
|
|
|
if (d->m_currentFindSupport)
|
2014-07-02 13:43:31 +02:00
|
|
|
d->m_currentFindSupport->clearHighlights();
|
2018-09-20 01:16:01 +03:00
|
|
|
d->m_currentFindSupport = nullptr;
|
2011-08-09 11:08:01 +02:00
|
|
|
if (!openedEditor)
|
|
|
|
|
return;
|
|
|
|
|
// highlight results
|
2018-09-20 01:16:01 +03:00
|
|
|
if (auto findSupport = Aggregation::query<IFindSupport>(openedEditor->widget())) {
|
2016-02-07 23:35:41 +02:00
|
|
|
d->m_currentFindSupport = findSupport;
|
|
|
|
|
d->m_currentFindSupport->highlightAll(parameters.text, parameters.flags);
|
2010-07-19 14:46:53 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-12-21 11:08:20 +01:00
|
|
|
|
2011-08-09 11:08:01 +02:00
|
|
|
void BaseFileFind::hideHighlightAll(bool visible)
|
|
|
|
|
{
|
2013-03-26 12:32:52 +01:00
|
|
|
if (!visible && d->m_currentFindSupport)
|
2014-07-02 13:43:31 +02:00
|
|
|
d->m_currentFindSupport->clearHighlights();
|
2011-08-09 11:08:01 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-18 15:05:13 +01:00
|
|
|
void BaseFileFind::searchAgain(SearchResult *search)
|
2011-12-13 10:50:57 +01:00
|
|
|
{
|
2012-02-07 15:44:12 +01:00
|
|
|
search->restart();
|
2011-12-13 10:50:57 +01:00
|
|
|
runSearch(search);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-28 09:55:53 +02:00
|
|
|
void BaseFileFind::setupSearch(SearchResult *search)
|
2013-02-12 18:37:14 +01:00
|
|
|
{
|
2023-06-28 09:55:53 +02:00
|
|
|
connect(this, &IFindFilter::enabledChanged, search, [this, search] {
|
|
|
|
|
search->setSearchAgainEnabled(isEnabled());
|
|
|
|
|
});
|
2013-02-12 18:37:14 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-05 19:12:47 +02:00
|
|
|
FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems &items,
|
2021-06-22 08:57:36 +02:00
|
|
|
bool preserveCase)
|
2009-12-21 11:08:20 +01:00
|
|
|
{
|
2010-12-01 13:25:08 +01:00
|
|
|
if (items.isEmpty())
|
2021-06-22 08:57:36 +02:00
|
|
|
return {};
|
2009-12-21 11:08:20 +01:00
|
|
|
|
2011-10-17 10:37:22 +02:00
|
|
|
RefactoringChanges refactoring;
|
2009-12-21 11:08:20 +01:00
|
|
|
|
2023-05-05 19:12:47 +02:00
|
|
|
QHash<FilePath, SearchResultItems> changes;
|
2019-02-18 15:05:13 +01:00
|
|
|
for (const SearchResultItem &item : items)
|
2022-12-07 19:31:39 +01:00
|
|
|
changes[FilePath::fromUserInput(item.path().constFirst())].append(item);
|
2009-12-21 11:08:20 +01:00
|
|
|
|
2012-10-15 11:53:22 +02:00
|
|
|
// Checking for files without write permissions
|
2019-05-28 16:18:05 +02:00
|
|
|
QSet<FilePath> roFiles;
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto it = changes.cbegin(), end = changes.cend(); it != end; ++it) {
|
2021-06-22 08:57:36 +02:00
|
|
|
if (!it.key().isWritableFile())
|
|
|
|
|
roFiles.insert(it.key());
|
2012-10-15 11:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Query the user for permissions
|
|
|
|
|
if (!roFiles.isEmpty()) {
|
2020-06-02 09:10:40 +02:00
|
|
|
ReadOnlyFilesDialog roDialog(Utils::toList(roFiles), ICore::dialogParent());
|
2023-01-17 18:02:43 +01:00
|
|
|
roDialog.setShowFailWarning(true, Tr::tr("Aborting replace."));
|
2014-09-20 21:52:56 +02:00
|
|
|
if (roDialog.exec() == ReadOnlyFilesDialog::RO_Cancel)
|
2021-06-22 08:57:36 +02:00
|
|
|
return {};
|
2012-10-15 11:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto it = changes.cbegin(), end = changes.cend(); it != end; ++it) {
|
2021-06-22 08:57:36 +02:00
|
|
|
const FilePath filePath = it.key();
|
2023-05-05 19:12:47 +02:00
|
|
|
const SearchResultItems changeItems = it.value();
|
2009-12-21 11:08:20 +01:00
|
|
|
|
2011-10-17 10:37:22 +02:00
|
|
|
ChangeSet changeSet;
|
2021-06-22 08:57:36 +02:00
|
|
|
RefactoringFilePtr file = refactoring.file(filePath);
|
2022-09-30 10:35:15 +02:00
|
|
|
QSet<QPair<int, int>> processed;
|
2019-02-18 15:05:13 +01:00
|
|
|
for (const SearchResultItem &item : changeItems) {
|
2022-09-30 10:35:15 +02:00
|
|
|
const QPair<int, int> p{item.mainRange().begin.line, item.mainRange().begin.column};
|
2023-06-22 14:58:11 +02:00
|
|
|
if (!Utils::insert(processed, p))
|
2011-10-17 10:37:22 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
QString replacement;
|
2021-02-11 16:22:48 +01:00
|
|
|
if (item.userData().canConvert<QStringList>() && !item.userData().toStringList().isEmpty()) {
|
|
|
|
|
replacement = Utils::expandRegExpReplacement(text, item.userData().toStringList());
|
2012-11-30 16:15:07 +01:00
|
|
|
} else if (preserveCase) {
|
2023-05-11 09:34:53 +02:00
|
|
|
Text::Range range = item.mainRange();
|
|
|
|
|
range.end.line -= range.begin.line - 1;
|
|
|
|
|
range.begin.line = 1;
|
|
|
|
|
QString originalText = item.lineText();
|
|
|
|
|
const int rangeLength = range.length(item.lineText());
|
|
|
|
|
if (rangeLength > 0)
|
|
|
|
|
originalText = originalText.mid(range.begin.column, rangeLength);
|
2012-11-30 16:15:07 +01:00
|
|
|
replacement = Utils::matchCaseReplacement(originalText, text);
|
|
|
|
|
} else {
|
2011-10-17 10:37:22 +02:00
|
|
|
replacement = text;
|
2012-11-30 16:15:07 +01:00
|
|
|
}
|
2011-10-17 10:37:22 +02:00
|
|
|
|
2021-02-11 16:22:48 +01:00
|
|
|
const int start = file->position(item.mainRange().begin.line,
|
|
|
|
|
item.mainRange().begin.column + 1);
|
|
|
|
|
const int end = file->position(item.mainRange().end.line,
|
|
|
|
|
item.mainRange().end.column + 1);
|
2011-10-17 10:37:22 +02:00
|
|
|
changeSet.replace(start, end, replacement);
|
2009-12-21 11:08:20 +01:00
|
|
|
}
|
2011-10-17 10:37:22 +02:00
|
|
|
file->setChangeSet(changeSet);
|
|
|
|
|
file->apply();
|
2009-12-21 11:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return changes.keys();
|
|
|
|
|
}
|
2011-12-12 15:16:49 +01:00
|
|
|
|
2023-05-05 14:05:10 +02:00
|
|
|
QFuture<SearchResultItems> BaseFileFind::executeSearch(const FileFindParameters ¶meters)
|
2015-11-30 23:14:41 +02:00
|
|
|
{
|
2023-06-28 19:14:50 +02:00
|
|
|
return parameters.searchExecutor(parameters);
|
2015-11-30 23:14:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
2014-01-13 16:17:34 +01:00
|
|
|
} // namespace TextEditor
|