Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 739a730f authored by Kåre Särs's avatar Kåre Särs

S&R: Replace matches in chunks also in files.

Files with long lines can be slow to replace -> replace in chunks.
parent 5327d8f8
......@@ -1402,7 +1402,7 @@ void KatePluginSearchView::replaceChecked()
m_curResults->replaceStr);
}
void KatePluginSearchView::replaceStatus(const QUrl &url)
void KatePluginSearchView::replaceStatus(const QUrl &url, int replacedInFile, int matchesInFile)
{
if (!m_curResults) {
qDebug() << "m_curResults == nullptr";
......@@ -1412,10 +1412,10 @@ void KatePluginSearchView::replaceStatus(const QUrl &url)
if (root) {
QString file = url.toString(QUrl::PreferLocalFile);
if (file.size() > 70) {
root->setData(0, Qt::DisplayRole, i18n("<b>Replacing in: ...%1</b>", file.right(70)));
root->setData(0, Qt::DisplayRole, i18n("<b>Processed %1 of %2 matches in: ...%3</b>", replacedInFile, matchesInFile, file.right(70)));
}
else {
root->setData(0, Qt::DisplayRole, i18n("<b>Replacing in: %1</b>", file));
root->setData(0, Qt::DisplayRole, i18n("<b>Processed %1 of %2 matches in: %3</b>", replacedInFile, matchesInFile, file));
}
}
}
......
......@@ -157,7 +157,7 @@ private Q_SLOTS:
void replaceSingleMatch();
void replaceChecked();
void replaceStatus(const QUrl &url);
void replaceStatus(const QUrl &url, int replacedInFile, int matchesInFile);
void replaceDone();
void docViewChanged();
......
......@@ -22,30 +22,23 @@
#include <QTreeWidgetItem>
#include <QTimer>
#include <ktexteditor/movinginterface.h>
#include <ktexteditor/movingrange.h>
#include <klocalizedstring.h>
ReplaceMatches::ReplaceMatches(QObject *parent) : QObject(parent),
m_manager(nullptr),
m_tree(nullptr),
m_rootIndex(-1)
{
connect(this, &ReplaceMatches::replaceNextMatch, this, &ReplaceMatches::doReplaceNextMatch, Qt::QueuedConnection);
}
ReplaceMatches::ReplaceMatches(QObject *parent) : QObject(parent) {}
void ReplaceMatches::replaceChecked(QTreeWidget *tree, const QRegularExpression &regexp, const QString &replace)
{
if (m_manager == nullptr) return;
if (m_rootIndex != -1) return;
if (m_rootIndex != -1) return; // already replacing
m_tree = tree;
m_rootIndex = 0;
m_childStartIndex = 0;
m_regExp = regexp;
m_replaceText = replace;
m_cancelReplace = false;
m_progressTime.restart();
emit replaceNextMatch();
doReplaceNextMatch();
}
void ReplaceMatches::setDocumentManager(KTextEditor::Application *manager)
......@@ -60,7 +53,7 @@ void ReplaceMatches::cancelReplace()
KTextEditor::Document *ReplaceMatches::findNamed(const QString &name)
{
QList<KTextEditor::Document*> docs = m_manager->documents();
const QList<KTextEditor::Document*> docs = m_manager->documents();
foreach (KTextEditor::Document* it, docs) {
if ( it->documentName() == name) {
......@@ -78,7 +71,7 @@ bool ReplaceMatches::replaceMatch(KTextEditor::Document *doc, QTreeWidgetItem *i
// don't replace an already replaced item
if (item->data(0, ReplaceMatches::ReplacedRole).toBool()) {
qDebug() << "not replacing";
//qDebug() << "not replacing already replaced item";
return false;
}
......@@ -202,7 +195,8 @@ bool ReplaceMatches::replaceSingleMatch(KTextEditor::Document *doc, QTreeWidgetI
void ReplaceMatches::doReplaceNextMatch()
{
if ((!m_manager) || (m_cancelReplace) || (m_tree->topLevelItemCount() != 1)) {
if (!m_manager || m_tree->topLevelItemCount() != 1) {
updateTreeViewItems(nullptr);
m_rootIndex = -1;
emit replaceDone();
return;
......@@ -212,94 +206,135 @@ void ReplaceMatches::doReplaceNextMatch()
// cancelReplace(). A closed file could lead to a crash if it is not handled.
// Open the file
QTreeWidgetItem *rootItem = m_tree->topLevelItem(0)->child(m_rootIndex);
if (!rootItem) {
QTreeWidgetItem *fileItem = m_tree->topLevelItem(0)->child(m_rootIndex);
if (!fileItem) {
updateTreeViewItems(nullptr);
m_rootIndex = -1;
emit replaceDone();
return;
}
if (!rootItem->data(0, StartColumnRole).toString().isEmpty()) {
bool isSearchAsYouType = false;
if (!fileItem->data(0, StartColumnRole).toString().isEmpty()) {
// this is a search as you type replace
rootItem = m_tree->topLevelItem(0);
m_cancelReplace = true; // only one document...
fileItem = m_tree->topLevelItem(0);
isSearchAsYouType = true;
}
if (rootItem->checkState(0) == Qt::Unchecked) {
m_rootIndex++;
emit replaceNextMatch();
if (m_cancelReplace) {
updateTreeViewItems(fileItem);
m_rootIndex = -1;
emit replaceDone();
return;
}
if (fileItem->checkState(0) == Qt::Unchecked) {
updateTreeViewItems(fileItem);
QTimer::singleShot(0, this, &ReplaceMatches::doReplaceNextMatch);
return;
}
KTextEditor::Document *doc;
QString docUrl = rootItem->data(0, FileUrlRole).toString();
QString docUrl = fileItem->data(0, FileUrlRole).toString();
if (docUrl.isEmpty()) {
doc = findNamed(rootItem->data(0, FileNameRole).toString());
doc = findNamed(fileItem->data(0, FileNameRole).toString());
}
else {
doc = m_manager->findUrl(QUrl::fromUserInput(docUrl));
if (!doc) {
doc = m_manager->openUrl(QUrl::fromUserInput(rootItem->data(0, FileUrlRole).toString()));
doc = m_manager->openUrl(QUrl::fromUserInput(fileItem->data(0, FileUrlRole).toString()));
}
}
if (!doc) {
m_rootIndex++;
emit replaceNextMatch();
updateTreeViewItems(fileItem);
QTimer::singleShot(0, this, &ReplaceMatches::doReplaceNextMatch);
return;
}
if (m_progressTime.elapsed() > 100) {
m_progressTime.restart();
emit replaceStatus(doc->url());
if (m_currentMatches.isEmpty()) {
emit replaceStatus(doc->url(), 0, 0);
}
else {
emit replaceStatus(doc->url(), m_childStartIndex, m_currentMatches.count());
}
}
if (m_childStartIndex == 0) {
// Create a vector of moving ranges for updating the tree-view after replace
QVector<bool> replaced;
KTextEditor::MovingInterface* miface = qobject_cast<KTextEditor::MovingInterface*>(doc);
for (int j = 0; j < fileItem->childCount(); ++j) {
QTreeWidgetItem *item = fileItem->child(j);
int startLine = item->data(0, ReplaceMatches::StartLineRole).toInt();
int startColumn = item->data(0, ReplaceMatches::StartColumnRole).toInt();
int endLine = item->data(0, ReplaceMatches::EndLineRole).toInt();
int endColumn = item->data(0, ReplaceMatches::EndColumnRole).toInt();
KTextEditor::Range range(startLine, startColumn, endLine, endColumn);
KTextEditor::MovingRange* mr = miface->newMovingRange(range);
m_currentMatches.append(mr);
m_currentReplaced << false;
}
}
// Make one transaction for the whole replace to speed up things
// and get all replacements in one "undo"
KTextEditor::Document::EditingTransaction transaction(doc);
// Create a vector of moving ranges for updating the tree-view after replace
QVector<KTextEditor::MovingRange*> matches;
QVector<bool> replaced;
KTextEditor::MovingInterface* miface = qobject_cast<KTextEditor::MovingInterface*>(doc);
// now do the replaces
int i = m_childStartIndex;
for (; i < fileItem->childCount(); ++i) {
if (m_progressTime.elapsed() > 100) {
break;
}
QTreeWidgetItem *item = fileItem->child(i);
for (int i=0; i<rootItem->childCount(); i++) {
QTreeWidgetItem *item = rootItem->child(i);
int startLine = item->data(0, ReplaceMatches::StartLineRole).toInt();
int startColumn = item->data(0, ReplaceMatches::StartColumnRole).toInt();
int endLine = item->data(0, ReplaceMatches::EndLineRole).toInt();
int endColumn = item->data(0, ReplaceMatches::EndColumnRole).toInt();
KTextEditor::Range range(startLine, startColumn, endLine, endColumn);
KTextEditor::MovingRange* mr = miface->newMovingRange(range);
matches.append(mr);
if (item->checkState(0) == Qt::Checked) {
m_currentReplaced[i] = replaceMatch(doc, item, m_currentMatches[i]->toRange(), m_regExp, m_replaceText);
item->setCheckState(0, Qt::PartiallyChecked);
}
}
for (int i=0; i<rootItem->childCount(); i++) {
QTreeWidgetItem *item = rootItem->child(i);
if (item->checkState(0) == Qt::Unchecked) {
replaced << false;
}
else {
replaced << replaceMatch(doc, item, matches[i]->toRange(), m_regExp, m_replaceText);
if (i == fileItem->childCount()) {
updateTreeViewItems(fileItem);
if (isSearchAsYouType) {
m_rootIndex = -1;
emit replaceDone();
return;
}
}
else {
m_childStartIndex = i;
}
QTimer::singleShot(0, this, &ReplaceMatches::doReplaceNextMatch);
}
// Update the tree-view-items
for (int i=0; i<replaced.size() && i<matches.size(); i++) {
QTreeWidgetItem *item = rootItem->child(i);
if (!replaced[i]) {
item->setData(0, ReplaceMatches::StartLineRole, matches[i]->start().line());
item->setData(0, ReplaceMatches::StartColumnRole, matches[i]->start().column());
item->setData(0, ReplaceMatches::EndLineRole, matches[i]->end().line());
item->setData(0, ReplaceMatches::EndColumnRole, matches[i]->end().column());
void ReplaceMatches::updateTreeViewItems(QTreeWidgetItem *fileItem)
{
if (fileItem &&
m_currentReplaced.size() == m_currentMatches.size() &&
m_currentReplaced.size() == fileItem->childCount())
{
for (int j=0; j<m_currentReplaced.size() && j<m_currentMatches.size(); ++j) {
QTreeWidgetItem *item = fileItem->child(j);
if (!m_currentReplaced[j] && item) {
item->setData(0, ReplaceMatches::StartLineRole, m_currentMatches[j]->start().line());
item->setData(0, ReplaceMatches::StartColumnRole, m_currentMatches[j]->start().column());
item->setData(0, ReplaceMatches::EndLineRole, m_currentMatches[j]->end().line());
item->setData(0, ReplaceMatches::EndColumnRole, m_currentMatches[j]->end().column());
}
}
}
qDeleteAll(matches);
qDeleteAll(m_currentMatches);
m_rootIndex++;
emit replaceNextMatch();
m_childStartIndex = 0;
m_currentMatches.clear();
m_currentReplaced.clear();
}
......@@ -27,6 +27,8 @@
#include <QElapsedTimer>
#include <ktexteditor/document.h>
#include <ktexteditor/application.h>
#include <ktexteditor/movinginterface.h>
#include <ktexteditor/movingrange.h>
class ReplaceMatches: public QObject
{
......@@ -64,14 +66,19 @@ private Q_SLOTS:
void doReplaceNextMatch();
Q_SIGNALS:
void replaceNextMatch();
void replaceStatus(const QUrl &url);
void replaceStatus(const QUrl &url, int replacedInFile, int matchesInFile);
void replaceDone();
private:
KTextEditor::Application *m_manager;
QTreeWidget *m_tree;
int m_rootIndex;
void updateTreeViewItems(QTreeWidgetItem *fileItem);
KTextEditor::Application *m_manager = nullptr;
QTreeWidget *m_tree = nullptr;
int m_rootIndex = -1;
int m_childStartIndex = -1;
QVector<KTextEditor::MovingRange*> m_currentMatches;
QVector<bool> m_currentReplaced;
QRegularExpression m_regExp;
QString m_replaceText;
bool m_cancelReplace;
......
......@@ -97,7 +97,7 @@ int SearchOpenFiles::searchSingleLineRegExp(KTextEditor::Document *doc, const QR
time.start();
for (int line = startLine; line < doc->lines(); line++) {
if (time.elapsed() > 100) {
qDebug() << "Search time exceeded" << time.elapsed() << line;
//qDebug() << "Search time exceeded" << time.elapsed() << line;
return line;
}
QRegularExpressionMatch match;
......
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