Commit 8be8dab8 authored by Kåre Särs's avatar Kåre Särs
Browse files

Add replaceChecked() to MatchModel

parent 1bc84f45
......@@ -47,8 +47,13 @@ MatchModel::MatchModel(QObject *parent)
dataChanged(createIndex(0, 0, InfoItemId), createIndex(0, 0, InfoItemId), QVector<int>(Qt::DisplayRole));
});
}
MatchModel::~MatchModel()
MatchModel::~MatchModel() {}
void MatchModel::setDocumentManager(KTextEditor::Application *manager)
{
m_docManager = manager;
connect(m_docManager, &KTextEditor::Application::documentWillBeDeleted, this, &MatchModel::cancelReplace);
}
void MatchModel::setSearchPlace(MatchModel::SearchPlaces searchPlace)
......@@ -351,10 +356,101 @@ bool MatchModel::replaceSingleMatch(KTextEditor::Document *doc, const QModelInde
return true;
}
// /** Replace all matches that have been checked */
// void MatchModel::replaceChecked(const QRegularExpression &regexp, const QString &replace)
// {
// }
void MatchModel::doReplaceNextMatch()
{
Q_ASSERT(m_docManager);
if (m_cancelReplace || m_replaceFile >= m_matchFiles.size()) {
m_replaceFile = -1;
emit replaceDone();
return;
}
// NOTE The document managers signal documentWillBeDeleted() must be connected to
// cancelReplace(). A closed file could lead to a crash if it is not handled.
// this is now done in setDocumentManager()
MatchFile &matchFile = m_matchFiles[m_replaceFile];
if (matchFile.checkState == Qt::Unchecked) {
m_replaceFile++;
QTimer::singleShot(0, this, &MatchModel::doReplaceNextMatch);
return;
}
KTextEditor::Document *doc;
doc = m_docManager->findUrl(matchFile.fileUrl);
if (!doc) {
doc = m_docManager->openUrl(matchFile.fileUrl);
}
if (!doc) {
qDebug() << "Failed to open the document" << matchFile.fileUrl;
m_replaceFile++;
QTimer::singleShot(0, this, &MatchModel::doReplaceNextMatch);
return;
}
if (doc->url() != matchFile.fileUrl) {
qDebug() << "url differences" << matchFile.fileUrl << doc->url();
matchFile.fileUrl = doc->url();
}
auto &matches = matchFile.matches;
// Create a vector of moving ranges for updating the matches after replace
QVector<KTextEditor::MovingRange *> matchRanges;
matchRanges.reserve(matches.size());
KTextEditor::MovingInterface *miface = qobject_cast<KTextEditor::MovingInterface *>(doc);
for (const auto &match: qAsConst(matches)) {
matchRanges.append(miface->newMovingRange(match.range));
}
// Make one transaction for the whole replace to speed up things
// and get all replacements in one "undo"
KTextEditor::Document::EditingTransaction transaction(doc);
for (int i = 0; i < matches.size(); ++i) {
if (matches[i].checked) {
replaceMatch(doc, createIndex(i, 0, m_replaceFile), m_regExp, m_replaceText);
}
// The document has been modified -> make sure the next match has the correct range
if (i < matches.size()-1) {
matches[i+1].range = matchRanges[i+1]->toRange();
}
}
dataChanged(createIndex(0, 0, m_replaceFile), createIndex(matches.size()-1, 0, m_replaceFile));
// free our moving ranges
qDeleteAll(matchRanges);
m_replaceFile++;
QTimer::singleShot(0, this, &MatchModel::doReplaceNextMatch);
}
/** Initiate a replace of all matches that have been checked */
void MatchModel::replaceChecked(const QRegularExpression &regExp, const QString &replaceString)
{
Q_ASSERT(m_docManager != nullptr);
if (m_replaceFile != -1) {
Q_ASSERT(m_replaceFile != -1);
return; // already replacing
}
m_replaceFile = 0;
m_regExp = regExp;
m_replaceText = replaceString;
m_cancelReplace = false;
doReplaceNextMatch();
}
void MatchModel::cancelReplace()
{
m_replaceFile = -1;
m_cancelReplace = true;
}
QString MatchModel::matchToHtmlString(const Match &match) const
{
......
......@@ -13,7 +13,9 @@
#include <QUrl>
#include <QBrush>
#include <QTimer>
#include <QRegularExpression>
#include <ktexteditor/application.h>
#include <KTextEditor/Range>
#include <KTextEditor/Cursor>
......@@ -82,6 +84,8 @@ public:
MatchModel(QObject *parent = nullptr);
~MatchModel() override;
void setDocumentManager(KTextEditor::Application *manager);
void setMatchColors(const QColor &foreground, const QColor &background, const QColor &replaseBackground);
void setSearchPlace(MatchModel::SearchPlaces searchPlace);
......@@ -107,10 +111,14 @@ public Q_SLOTS:
void addMatches(const QUrl &fileUrl, const QVector<KateSearchMatch> &searchMatches);
/** This function is used to replace a single match */
bool replaceSingleMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceStringText);
bool replaceSingleMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString);
/** Initiate a replace of all matches that have been checked.
* The actual replacing is split up into slot calls that are added to the event loop */
void replaceChecked(const QRegularExpression &regExp, const QString &replaceString);
// /** Replace all matches that have been checked */
// void replaceChecked(const QRegularExpression &regexp, const QString &replace);
/** Cancel the replacing of checked matches. NOTE: This will only be handled when the next file is handled */
void cancelReplace();
Q_SIGNALS:
void replaceDone();
......@@ -135,6 +143,9 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
private Q_SLOTS:
void doReplaceNextMatch();
private:
bool replaceMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString);
......@@ -161,6 +172,14 @@ private:
QString m_projectName;
QUrl m_lastMatchUrl;
QTimer m_infoUpdateTimer;
// Replacing related objects
KTextEditor::Application *m_docManager = nullptr;
int m_replaceFile = -1;
QRegularExpression m_regExp;
QString m_replaceText;
bool m_cancelReplace = true;
};
#endif
......@@ -1582,10 +1582,9 @@ void KatePluginSearchView::replaceSingleMatch()
}
KTextEditor::Document *doc = m_mainWindow->activeView()->document();
// Find the corresponding range
// FIXME why did we go through the m_matchRanges?
// FIXME The document might have been edited after the search.
// Fix the ranges before attempting the replace
res->matchModel.replaceSingleMatch(doc, itemIndex, res->regExp, m_ui.replaceCombo->currentText());
goToNextMatch2();
......@@ -1626,12 +1625,7 @@ void KatePluginSearchView::replaceChecked()
m_curResults->replaceStr = m_ui.replaceCombo->currentText();
QTreeWidgetItem *root = m_curResults->tree->topLevelItem(0);
if (root) {
m_curResults->treeRootText = root->data(0, Qt::DisplayRole).toString();
}
m_replacer.replaceChecked(m_curResults->tree, m_curResults->regExp, m_curResults->replaceStr);
m_curResults->matchModel.replaceChecked(m_curResults->regExp, m_curResults->replaceStr);
}
void KatePluginSearchView::replaceStatus(const QUrl &url, int replacedInFile, int matchesInFile)
......@@ -1666,15 +1660,6 @@ void KatePluginSearchView::replaceDone()
m_ui.matchCase->setDisabled(false);
m_ui.expandResults->setDisabled(false);
m_ui.currentFolderButton->setDisabled(false);
if (!m_curResults) {
// qDebug() << "m_curResults == nullptr";
return;
}
QTreeWidgetItem *root = m_curResults->tree->topLevelItem(0);
if (root) {
root->setData(0, Qt::DisplayRole, m_curResults->treeRootText);
}
}
void KatePluginSearchView::docViewChanged()
......@@ -2350,6 +2335,8 @@ void KatePluginSearchView::addTab()
res->treeView->setRootIsDecorated(false);
connect(res->treeView, &QTreeView::doubleClicked, this, &KatePluginSearchView::itemSelected2, Qt::UniqueConnection);
connect(res->treeView, &QTreeView::customContextMenuRequested, this, &KatePluginSearchView::customResMenuRequested, Qt::UniqueConnection);
res->matchModel.setDocumentManager(m_kateApp);
connect(&res->matchModel, &MatchModel::replaceDone, this, &KatePluginSearchView::replaceDone);
res->searchPlaceIndex = m_ui.searchPlaceCombo->currentIndex();
res->useRegExp = m_ui.useRegExp->isChecked();
......
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