Commit 1bc84f45 authored by Kåre Särs's avatar Kåre Särs
Browse files

Add replacing single item in MatchModel

(still needs updating the match-marks in the document
parent 01476459
......@@ -14,6 +14,8 @@
#include <QDir>
#include <algorithm> // std::count_if
#include <ktexteditor/movinginterface.h>
#include <ktexteditor/movingrange.h>
static const quintptr InfoItemId = 0xFFFFFFFF;
static const quintptr FileItemId = 0x7FFFFFFF;
......@@ -199,22 +201,155 @@ void MatchModel::setMatchColors(const QColor &foreground, const QColor &backgrou
m_replaceHighlightColor = replaseBackground;
}
MatchModel::Match *MatchModel::matchFromIndex(const QModelIndex &matchIndex)
{
if (!isMatch(matchIndex)) {
qDebug() << "Not a valid match index";
return nullptr;
}
// /** This function is used to modify a match */
// void MatchModel::replaceMatch(const QModelIndex &matchIndex, const QRegularExpression &regexp, const QString &replaceText)
// {
// if (!matchIndex.isValid()) {
// qDebug() << "This should not be possible";
// return;
// }
//
// if (matchIndex.internalId() == InfoItemId || matchIndex.internalId() == FileItemId) {
// qDebug() << "You cannot replace a file or the info item";
// return;
// }
//
//
// }
int fileRow = matchIndex.internalId();
int matchRow = matchIndex.row();
return &m_matchFiles[fileRow].matches[matchRow];
}
KTextEditor::Range MatchModel::matchRange(const QModelIndex &matchIndex) const
{
if (!isMatch(matchIndex)) {
qDebug() << "Not a valid match index";
return KTextEditor::Range();
}
int fileRow = matchIndex.internalId();
int matchRow = matchIndex.row();
return m_matchFiles[fileRow].matches[matchRow].range;
}
/** This function is used to replace a match */
bool MatchModel::replaceMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString)
{
if (!doc) {
qDebug() << "No doc";
return false;
}
Match *matchItem = matchFromIndex(matchIndex);
if (!matchItem) {
qDebug() << "Not a valid index";
return false;
}
// don't replace an already replaced item
if (!matchItem->replaceText.isEmpty()) {
// qDebug() << "not replacing already replaced item";
return false;
}
// Check that the text has not been modified and still matches + get captures for the replace
QString matchLines = doc->text(matchItem->range);
QRegularExpressionMatch match = regExp.match(matchLines);
if (match.capturedStart() != 0) {
qDebug() << matchLines << "Does not match" << regExp.pattern();
return false;
}
// Modify the replace string according to this match
QString replaceText = replaceString;
replaceText.replace(QLatin1String("\\\\"), QLatin1String("¤Search&Replace¤"));
// allow captures \0 .. \9
for (int j = qMin(9, match.lastCapturedIndex()); j >= 0; --j) {
QString captureLX = QStringLiteral("\\L\\%1").arg(j);
QString captureUX = QStringLiteral("\\U\\%1").arg(j);
QString captureX = QStringLiteral("\\%1").arg(j);
replaceText.replace(captureLX, match.captured(j).toLower());
replaceText.replace(captureUX, match.captured(j).toUpper());
replaceText.replace(captureX, match.captured(j));
}
// allow captures \{0} .. \{9999999}...
for (int j = match.lastCapturedIndex(); j >= 0; --j) {
QString captureLX = QStringLiteral("\\L\\{%1}").arg(j);
QString captureUX = QStringLiteral("\\U\\{%1}").arg(j);
QString captureX = QStringLiteral("\\{%1}").arg(j);
replaceText.replace(captureLX, match.captured(j).toLower());
replaceText.replace(captureUX, match.captured(j).toUpper());
replaceText.replace(captureX, match.captured(j));
}
replaceText.replace(QLatin1String("\\n"), QLatin1String("\n"));
replaceText.replace(QLatin1String("\\t"), QLatin1String("\t"));
replaceText.replace(QLatin1String("¤Search&Replace¤"), QLatin1String("\\"));
// Replace the string
doc->replaceText(matchItem->range, replaceText);
// update the range
int newEndLine = matchItem->range.start().line() + replaceText.count(QLatin1Char('\n'));
int lastNL = replaceText.lastIndexOf(QLatin1Char('\n'));
int newEndColumn = lastNL == -1 ? matchItem->range.start().column() + replaceText.length() : replaceText.length() - lastNL - 1;
matchItem->range.setEnd(KTextEditor::Cursor{newEndLine, newEndColumn});
// Convert replace text back to "html"
replaceText.replace(QLatin1Char('\n'), QStringLiteral("\\n"));
replaceText.replace(QLatin1Char('\t'), QStringLiteral("\\t"));
matchItem->replaceText = replaceText;
return true;
}
/** This function is used to replace a match */
bool MatchModel::replaceSingleMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString)
{
if (!doc) {
qDebug() << "No doc";
return false;
}
if (!isMatch(matchIndex)) {
qDebug() << "This should not be possible";
return false;
}
if (matchIndex.internalId() == InfoItemId || matchIndex.internalId() == FileItemId) {
qDebug() << "You cannot replace a file or the info item";
return false;
}
// Create a vector of moving ranges for updating the tree-view after replace
QVector<KTextEditor::MovingRange *> matchRanges;
KTextEditor::MovingInterface *miface = qobject_cast<KTextEditor::MovingInterface *>(doc);
// Only add items after "matchIndex"
int fileRow = matchIndex.internalId();
int matchRow = matchIndex.row();
QVector<Match> &matches = m_matchFiles[fileRow].matches;
for (int i = matchRow+1; i < matches.size(); ++i) {
KTextEditor::MovingRange *mr = miface->newMovingRange(matches[i].range);
matchRanges.append(mr);
}
// The first range in the vector is for this match
if (!replaceMatch(doc, matchIndex, regExp, replaceString)) {
return false;
}
// Update the items after the matchIndex
for (int i = matchRow+1; i < matches.size(); ++i) {
Q_ASSERT(!matchRanges.isEmpty());
KTextEditor::MovingRange *mr = matchRanges.takeFirst();
matches[i].range = mr->toRange();
delete mr;
}
Q_ASSERT(matchRanges.isEmpty());
dataChanged(createIndex(matchRow, 0, fileRow), createIndex(matches.size()-1, 0, fileRow));
return true;
}
// /** Replace all matches that have been checked */
// void MatchModel::replaceChecked(const QRegularExpression &regexp, const QString &replace)
......@@ -224,11 +359,21 @@ void MatchModel::setMatchColors(const QColor &foreground, const QColor &backgrou
QString MatchModel::matchToHtmlString(const Match &match) const
{
QString pre =match.preMatchStr.toHtmlEscaped();
QString matchStr = match.matchStr;
matchStr.replace(QLatin1Char('\n'), QStringLiteral("\\n"));
matchStr = matchStr.toHtmlEscaped();
QString replaceStr = match.replaceText.toHtmlEscaped();
if (!replaceStr.isEmpty()) {
matchStr = QLatin1String("<i><s>") + matchStr + QLatin1String("</s></i> ");
}
matchStr = QStringLiteral("<span style=\"background-color:%1; color:%2;\">%3</span>")
.arg(m_searchBackgroundColor.name(), m_foregroundColor.name(), matchStr);
if (!replaceStr.isEmpty()) {
matchStr += QStringLiteral("<span style=\"background-color:%1; color:%2;\">%3</span>")
.arg(m_replaceHighlightColor.name(), m_foregroundColor.name(), replaceStr);
}
QString post = match.postMatchStr.toHtmlEscaped();
// (line:col)[space][space] ...Line text pre [highlighted match] Line text post....
......
......@@ -95,6 +95,8 @@ public:
/** This function clears all matches in all files */
void clear();
KTextEditor::Range matchRange(const QModelIndex &matchIndex) const;
public Q_SLOTS:
/** This function returns the row index of the specified file.
......@@ -104,13 +106,14 @@ public Q_SLOTS:
/** This function is used to add a new file */
void addMatches(const QUrl &fileUrl, const QVector<KateSearchMatch> &searchMatches);
// /** This function is used to replace a match */
// void replaceMatch(const QModelIndex &matchIndex, const QRegularExpression &regexp, const QString &replaceText);
/** This function is used to replace a single match */
bool replaceSingleMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceStringText);
// /** Replace all matches that have been checked */
// void replaceChecked(const QRegularExpression &regexp, const QString &replace);
Q_SIGNALS:
void replaceDone();
public:
bool isMatch(const QModelIndex &itemIndex) const;
......@@ -133,6 +136,9 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
private:
bool replaceMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString);
QString matchToHtmlString(const Match &match) const;
QString fileItemToHtmlString(const MatchFile &matchFile) const;
......@@ -140,6 +146,8 @@ private:
QString infoHtmlString() const;
Match *matchFromIndex(const QModelIndex &matchIndex);
QVector<MatchFile> m_matchFiles;
QHash<QUrl, int> m_matchFileIndexHash;
QColor m_searchBackgroundColor;
......
......@@ -1569,50 +1569,26 @@ void KatePluginSearchView::replaceSingleMatch()
goToNextMatch2();
}
QTreeWidgetItem *item = res->tree->currentItem();
if (!item || !item->parent()) {
// Nothing was selected
goToNextMatch();
return;
}
if (!m_mainWindow->activeView() || !m_mainWindow->activeView()->cursorPosition().isValid()) {
itemSelected(item); // Correct any bad cursor positions
itemSelected2(itemIndex); // Correct any bad cursor positions
return;
}
int cursorLine = m_mainWindow->activeView()->cursorPosition().line();
int cursorColumn = m_mainWindow->activeView()->cursorPosition().column();
KTextEditor::Range matchRange = res->matchModel.matchRange(itemIndex);
int startLine = item->data(0, ReplaceMatches::StartLineRole).toInt();
int startColumn = item->data(0, ReplaceMatches::StartColumnRole).toInt();
if ((cursorLine != startLine) || (cursorColumn != startColumn)) {
itemSelected(item);
if (m_mainWindow->activeView()->cursorPosition() != matchRange.start()) {
itemSelected2(itemIndex);
return;
}
KTextEditor::Document *doc = m_mainWindow->activeView()->document();
// Find the corresponding range
int i;
for (i = 0; i < m_matchRanges.size(); i++) {
if (m_matchRanges[i]->document() != doc)
continue;
if (m_matchRanges[i]->start().line() != startLine)
continue;
if (m_matchRanges[i]->start().column() != startColumn)
continue;
break;
}
if (i >= m_matchRanges.size()) {
goToNextMatch();
return;
}
// FIXME why did we go through the m_matchRanges?
m_replacer.replaceSingleMatch(doc, item, res->regExp, m_ui.replaceCombo->currentText());
res->matchModel.replaceSingleMatch(doc, itemIndex, res->regExp, m_ui.replaceCombo->currentText());
goToNextMatch();
goToNextMatch2();
}
void KatePluginSearchView::replaceChecked()
......@@ -2094,9 +2070,7 @@ void KatePluginSearchView::goToNextMatch2()
// we had an active item go to next
currentIndex = res->matchModel.nextMatch(currentIndex);
itemSelected2(currentIndex);
qDebug() << currentIndex << res->matchModel.firstMatch();
if (currentIndex == res->matchModel.firstMatch()) {
qDebug() << "ontinuing from first match";
delete m_infoMessage;
const QString msg = i18n("Continuing from first match");
m_infoMessage = new KTextEditor::Message(msg, KTextEditor::Message::Information);
......
......@@ -320,6 +320,7 @@ void ReplaceMatches::updateTreeViewItems(QTreeWidgetItem *fileItem, const QVecto
// if we have a non-empty matches, we need to update stuff
// we always pass only matching sized vectors if non-empty!
if (fileItem && !matches.isEmpty()) {
qDebug() << fileItem->childCount() << replaced.size() << matches.size();
Q_ASSERT(fileItem->childCount() == replaced.size());
Q_ASSERT(matches.size() == replaced.size());
for (int j = 0; j < fileItem->childCount(); ++j) {
......
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