diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 89225732b89135a788f3e67ae4168a6c02b0481c..89ead723ad35c43e800aa8bbe5bd3027ecc0561d 100755
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -214,7 +214,7 @@ struct Range
         : 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)
             .arg(rangemode);
@@ -291,6 +291,8 @@ public:
     int firstPositionInLine(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
+    QString lineContents(int line) const; // 1 based line
+    void setLineContents(int line, const QString &contents) const; // 1 based line
 
     // all zero-based counting
     int cursorLineOnScreen() const;
@@ -1893,9 +1895,50 @@ void FakeVimHandler::Private::handleCommand(const QString &cmd)
     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)
 {
     QString cmd = cmd0;
+    // FIXME: that seems to be different for %w and %s
     if (cmd.startsWith(QLatin1Char('%')))
         cmd = "1,$" + cmd.mid(1);
 
@@ -1921,7 +1964,9 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
     static QRegExp reNormal("^norm(al)?( (.*))?$");
     static QRegExp reSet("^set?( (.*))?$");
     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();
     showBlackMessage(QString());
@@ -2023,26 +2068,25 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
         leaveVisualMode();
         setPosition(firstPositionInLine(beginLine));
         //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('>'))) {
         m_anchor = firstPositionInLine(beginLine);
         setPosition(firstPositionInLine(endLine));
         shiftRegionRight(1);
         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
         redo();
         updateMiniBuffer();
     } else if (reNormal.indexIn(cmd) != -1) { // :normal
         //qDebug() << "REPLAY: " << reNormal.cap(3);
         replay(reNormal.cap(3), 1);
-    } else if (reSubstitute.indexIn(cmd) != -1) { // :substitute
-        QString needle = reSubstitute.cap(2);
-        const QString replacement = reSubstitute.cap(3);
-        QString flags = reSubstitute.cap(4);
-        const bool startOfLineOnly = needle.startsWith('^');
-        if (startOfLineOnly)
-           needle.remove(0, 1);
+    } else if (isSubstitution(cmd, &arguments)) { // :substitute
+        QString needle = arguments.at(0);
+        const QString replacement = arguments.at(1);
+        QString flags = arguments.at(2);
         needle.replace('$', '\n');
         needle.replace("\\\n", "\\$");
         QRegExp pattern(needle);
@@ -2050,30 +2094,34 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0)
             pattern.setCaseSensitivity(Qt::CaseInsensitive);
         const bool global = flags.contains('g');
         beginEditBlock();
-        for (int line = beginLine; line <= endLine; ++line) {
-            const int start = firstPositionInLine(line);
-            const int end = lastPositionInLine(line);
-            for (int position = start; position <= end && position >= start; ) {
-                position = pattern.indexIn(m_tc.document()->toPlainText(), position);
-                if (startOfLineOnly && position != start)
+        for (int line = endLine; line >= beginLine; --line) {
+            QString origText = lineContents(line);
+            QString text = origText;
+            int pos = 0;
+            while (true) {
+                pos = pattern.indexIn(text, pos, QRegExp::CaretAtZero);
+                if (pos == -1)
                     break;
-                if (position != -1) {
-                    m_tc.setPosition(position);
-                    m_tc.movePosition(QTextCursor::NextCharacter,
-                        KeepAnchor, pattern.matchedLength());
-                    QString text = m_tc.selectedText();
-                    if (text.endsWith(ParagraphSeparator)) {
-                        text = replacement + "\n";
-                    } else {
-                        text.replace(ParagraphSeparator, "\n");
-                        text.replace(pattern, replacement);
+                if (pattern.cap(0).isEmpty())
+                    break;
+                QStringList caps = pattern.capturedTexts();
+                QString matched = text.mid(pos, caps.at(0).size());
+                QString repl = replacement;
+                for (int i = 1; i < caps.size(); ++i)
+                    repl.replace("\\" + QString::number(i), caps.at(i));
+                for (int i = 0; i < repl.size(); ++i) {
+                    if (repl.at(i) == '&' && (i == 0 || repl.at(i - 1) != '\\')) {
+                        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)
                     break;
             }
+            if (text != origText)
+                setLineContents(line, text);
         }
         endEditBlock();
     } else if (reSet.indexIn(cmd) != -1) { // :set
@@ -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
 {
     return m_tc.document()->findBlockByNumber(line - 1).position();