Commit a0abde63 authored by hjk's avatar hjk
Browse files

fakevim: Add back references, $ and ^ handling to ex-mode substitute

parent bf077800
...@@ -214,7 +214,7 @@ struct Range ...@@ -214,7 +214,7 @@ struct Range
: beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m) : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
{} {}
QString toString() const QString toString() const
{ {
return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos) return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
.arg(rangemode); .arg(rangemode);
...@@ -291,6 +291,8 @@ public: ...@@ -291,6 +291,8 @@ public:
int firstPositionInLine(int line) const; // 1 based line, 0 based pos int firstPositionInLine(int line) const; // 1 based line, 0 based pos
int lastPositionInLine(int line) const; // 1 based line, 0 based pos int lastPositionInLine(int line) const; // 1 based line, 0 based pos
int lineForPosition(int pos) const; // 1 based line, 0 based pos int lineForPosition(int pos) const; // 1 based line, 0 based pos
QString lineContents(int line) const; // 1 based line
void setLineContents(int line, const QString &contents) const; // 1 based line
// all zero-based counting // all zero-based counting
int cursorLineOnScreen() const; int cursorLineOnScreen() const;
...@@ -1893,9 +1895,50 @@ void FakeVimHandler::Private::handleCommand(const QString &cmd) ...@@ -1893,9 +1895,50 @@ void FakeVimHandler::Private::handleCommand(const QString &cmd)
EDITOR(setTextCursor(m_tc)); EDITOR(setTextCursor(m_tc));
} }
// result: (needle, replacement, opions)
static bool isSubstitution(const QString &cmd0, QStringList *result)
{
QString cmd;
if (cmd0.startsWith("substitute"))
cmd = cmd0.mid(10);
else if (cmd0.startsWith('s'))
cmd = cmd0.mid(1);
else
return false;
// we have /{pattern}/{string}/[flags] now
if (cmd.isEmpty())
return false;
const QChar separator = cmd.at(0);
int pos1 = -1;
int pos2 = -1;
int i;
for (i = 1; i < cmd.size(); ++i) {
if (cmd.at(i) == separator && cmd.at(i - 1) != '\\') {
pos1 = i;
break;
}
}
if (pos1 == -1)
return false;
for (++i; i < cmd.size(); ++i) {
if (cmd.at(i) == separator && cmd.at(i - 1) != '\\') {
pos2 = i;
break;
}
}
if (pos2 == -1)
pos2 = cmd.size();
result->append(cmd.mid(1, pos1 - 1));
result->append(cmd.mid(pos1 + 1, pos2 - pos1 - 1));
result->append(cmd.mid(pos2 + 1));
return true;
}
void FakeVimHandler::Private::handleExCommand(const QString &cmd0) void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
{ {
QString cmd = cmd0; QString cmd = cmd0;
// FIXME: that seems to be different for %w and %s
if (cmd.startsWith(QLatin1Char('%'))) if (cmd.startsWith(QLatin1Char('%')))
cmd = "1,$" + cmd.mid(1); cmd = "1,$" + cmd.mid(1);
...@@ -1921,7 +1964,9 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) ...@@ -1921,7 +1964,9 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
static QRegExp reNormal("^norm(al)?( (.*))?$"); static QRegExp reNormal("^norm(al)?( (.*))?$");
static QRegExp reSet("^set?( (.*))?$"); static QRegExp reSet("^set?( (.*))?$");
static QRegExp reWrite("^[wx]q?a?!?( (.*))?$"); static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
static QRegExp reSubstitute("^s(.)(.*)\\1(.*)\\1([gi]*)"); //static QRegExp reSubstitute("^s(.)(.*)\\1(.*)(\\1([gi]*))?$");
//reSubstitute.setMinimal(true);
QStringList arguments;
enterCommandMode(); enterCommandMode();
showBlackMessage(QString()); showBlackMessage(QString());
...@@ -2023,26 +2068,25 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) ...@@ -2023,26 +2068,25 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
leaveVisualMode(); leaveVisualMode();
setPosition(firstPositionInLine(beginLine)); setPosition(firstPositionInLine(beginLine));
//qDebug() << "FILTER: " << command; //qDebug() << "FILTER: " << command;
showBlackMessage(FakeVimHandler::tr("%n lines filtered", 0, text.count('\n'))); showBlackMessage(FakeVimHandler::tr("%n lines filtered", 0,
text.count('\n')));
} else if (cmd.startsWith(QLatin1Char('>'))) { } else if (cmd.startsWith(QLatin1Char('>'))) {
m_anchor = firstPositionInLine(beginLine); m_anchor = firstPositionInLine(beginLine);
setPosition(firstPositionInLine(endLine)); setPosition(firstPositionInLine(endLine));
shiftRegionRight(1); shiftRegionRight(1);
leaveVisualMode(); leaveVisualMode();
showBlackMessage(FakeVimHandler::tr("%n lines >ed %1 time", 0, (endLine - beginLine + 1)).arg(1)); showBlackMessage(FakeVimHandler::tr("%n lines >ed %1 time", 0,
(endLine - beginLine + 1)).arg(1));
} else if (cmd == "red" || cmd == "redo") { // :redo } else if (cmd == "red" || cmd == "redo") { // :redo
redo(); redo();
updateMiniBuffer(); updateMiniBuffer();
} else if (reNormal.indexIn(cmd) != -1) { // :normal } else if (reNormal.indexIn(cmd) != -1) { // :normal
//qDebug() << "REPLAY: " << reNormal.cap(3); //qDebug() << "REPLAY: " << reNormal.cap(3);
replay(reNormal.cap(3), 1); replay(reNormal.cap(3), 1);
} else if (reSubstitute.indexIn(cmd) != -1) { // :substitute } else if (isSubstitution(cmd, &arguments)) { // :substitute
QString needle = reSubstitute.cap(2); QString needle = arguments.at(0);
const QString replacement = reSubstitute.cap(3); const QString replacement = arguments.at(1);
QString flags = reSubstitute.cap(4); QString flags = arguments.at(2);
const bool startOfLineOnly = needle.startsWith('^');
if (startOfLineOnly)
needle.remove(0, 1);
needle.replace('$', '\n'); needle.replace('$', '\n');
needle.replace("\\\n", "\\$"); needle.replace("\\\n", "\\$");
QRegExp pattern(needle); QRegExp pattern(needle);
...@@ -2050,30 +2094,34 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) ...@@ -2050,30 +2094,34 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
pattern.setCaseSensitivity(Qt::CaseInsensitive); pattern.setCaseSensitivity(Qt::CaseInsensitive);
const bool global = flags.contains('g'); const bool global = flags.contains('g');
beginEditBlock(); beginEditBlock();
for (int line = beginLine; line <= endLine; ++line) { for (int line = endLine; line >= beginLine; --line) {
const int start = firstPositionInLine(line); QString origText = lineContents(line);
const int end = lastPositionInLine(line); QString text = origText;
for (int position = start; position <= end && position >= start; ) { int pos = 0;
position = pattern.indexIn(m_tc.document()->toPlainText(), position); while (true) {
if (startOfLineOnly && position != start) pos = pattern.indexIn(text, pos, QRegExp::CaretAtZero);
if (pos == -1)
break; break;
if (position != -1) { if (pattern.cap(0).isEmpty())
m_tc.setPosition(position); break;
m_tc.movePosition(QTextCursor::NextCharacter, QStringList caps = pattern.capturedTexts();
KeepAnchor, pattern.matchedLength()); QString matched = text.mid(pos, caps.at(0).size());
QString text = m_tc.selectedText(); QString repl = replacement;
if (text.endsWith(ParagraphSeparator)) { for (int i = 1; i < caps.size(); ++i)
text = replacement + "\n"; repl.replace("\\" + QString::number(i), caps.at(i));
} else { for (int i = 0; i < repl.size(); ++i) {
text.replace(ParagraphSeparator, "\n"); if (repl.at(i) == '&' && (i == 0 || repl.at(i - 1) != '\\')) {
text.replace(pattern, replacement); repl.replace(i, 1, caps.at(0));
i += caps.at(0).size();
} }
m_tc.removeSelectedText();
m_tc.insertText(text);
} }
text = text.left(pos) + repl + text.mid(pos + matched.size());
pos += matched.size();
if (!global) if (!global)
break; break;
} }
if (text != origText)
setLineContents(line, text);
} }
endEditBlock(); endEditBlock();
} else if (reSet.indexIn(cmd) != -1) { // :set } else if (reSet.indexIn(cmd) != -1) { // :set
...@@ -2715,6 +2763,21 @@ void FakeVimHandler::Private::fixMarks(int positionAction, int positionChange) ...@@ -2715,6 +2763,21 @@ void FakeVimHandler::Private::fixMarks(int positionAction, int positionChange)
} }
} }
QString FakeVimHandler::Private::lineContents(int line) const
{
return m_tc.document()->findBlockByNumber(line - 1).text();
}
void FakeVimHandler::Private::setLineContents(int line, const QString &contents) const
{
QTextBlock block = m_tc.document()->findBlockByNumber(line - 1);
QTextCursor tc = m_tc;
tc.setPosition(block.position());
tc.setPosition(block.position() + block.length() - 1, KeepAnchor);
tc.removeSelectedText();
tc.insertText(contents);
}
int FakeVimHandler::Private::firstPositionInLine(int line) const int FakeVimHandler::Private::firstPositionInLine(int line) const
{ {
return m_tc.document()->findBlockByNumber(line - 1).position(); return m_tc.document()->findBlockByNumber(line - 1).position();
......
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