diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp
index 92c3fe80987d0454184dfac04753a1d3f6764763..1bb564abe42373ab0fa4d506ff68b9687439fb75 100644
--- a/src/plugins/fakevim/fakevim_test.cpp
+++ b/src/plugins/fakevim/fakevim_test.cpp
@@ -122,14 +122,12 @@ struct TestData
 
     void setText(const QString &text)
     {
-        QTextCursor tc = cursor();
         QString str = text;
         int i = str.indexOf(cursorString);
         if (!cursorString.isEmpty() && i != -1)
             str.remove(i, 1);
         edit->document()->setPlainText(str);
-        tc.setPosition(qMax(0, i));
-        edit->setTextCursor(tc);
+        handler->setTextCursorPosition(i);
     }
 
     void doCommand(const QString &cmd) { handler->handleCommand(cmd); }
@@ -404,19 +402,50 @@ void FakeVimPlugin::test_vim_block_selection()
 
 void FakeVimPlugin::test_vim_repeat()
 {
-    NOT_IMPLEMENTED
-
     TestData data;
     setup(&data);
 
-    data.setText("test text");
-    KEYS("ciwWORD", "WOR" X "D text");
+    // delete line
+    data.setText("abc" N "def" N "ghi");
+    KEYS("dd", X "def" N "ghi");
+    KEYS(".", X "ghi");
+
+    // delete to next word
+    data.setText("abc def ghi jkl");
+    KEYS("dw", X "def ghi jkl");
+    KEYS("w.", "def " X "jkl");
+    KEYS("gg.", X "jkl");
+
+    // change in word
+    data.setText("WORD text");
+    KEYS("ciwWORD<esc>", "WOR" X "D text");
     KEYS("w.", "WORD WOR" X "D");
 
     /* QTCREATORBUG-7248 */
     data.setText("test tex" X "t");
-    KEYS("vbcWORD", "test " "WOR" X "D");
-    KEYS("bb.", X "WORD WORD");
+    KEYS("vbcWORD<esc>", "test " "WOR" X "D");
+    KEYS("bb.", "WOR" X "D WORD");
+
+    // delete selected range
+    data.setText("abc def ghi jkl");
+    KEYS("viwd", X " def ghi jkl");
+    KEYS(".", X "f ghi jkl");
+    KEYS(".", X "hi jkl");
+
+    // delete two lines
+    data.setText("abc" N "def" N "ghi" N "jkl" N "mno");
+    KEYS("Vjx", X "ghi" N "jkl" N "mno");
+    KEYS(".", X "mno");
+
+    // delete three lines
+    data.setText("abc" N "def" N "ghi" N "jkl" N "mno" N "pqr" N "stu");
+    KEYS("d2j", X "jkl" N "mno" N "pqr" N "stu");
+    KEYS(".", X "stu");
+
+    // replace block selection
+    data.setText("abcd" N "d" X "efg" N "ghij" N "jklm");
+    KEYS("<c-v>jlrX", "abcd" N "d" X "XXg" N "gXXj" N "jklm");
+    KEYS("gg.", "XXcd" N "XXXg" N "gXXj" N "jklm");
 }
 
 void FakeVimPlugin::test_vim_search()
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index f3b0b3cc690e0341263f25fe6b137ef9ffc71dba..852a07e7e4f9091bcdaad4e2244ee8814a88c7e3 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -1157,8 +1157,8 @@ public:
     EventResult handleCloseSquareSubMode(const Input &);
     EventResult handleSearchSubSubMode(const Input &);
     EventResult handleCommandSubSubMode(const Input &);
-    void finishMovement(const QString &dotCommand = QString());
-    void finishMovement(const QString &dotCommand, int count);
+    void finishMovement(const QString &dotCommandMovement = QString());
+    void finishMovement(const QString &dotCommandMovement, int count);
     void resetCommandMode();
     void search(const SearchData &sd, bool showMessages = true);
     void searchNext(bool forward = true);
@@ -1438,6 +1438,7 @@ public:
     void replay(const QString &text, int count);
     void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
     void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
+    QString visualDotCommand() const;
 
     // extra data for ';'
     QString m_semicolonCount;
@@ -2156,12 +2157,12 @@ void FakeVimHandler::Private::moveToStartOfLine()
 #endif
 }
 
-void FakeVimHandler::Private::finishMovement(const QString &dotCommand, int count)
+void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement, int count)
 {
-    finishMovement(dotCommand.arg(count));
+    finishMovement(dotCommandMovement.arg(count));
 }
 
-void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
+void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
 {
     //dump("FINISH MOVEMENT");
     if (m_submode == FilterSubMode) {
@@ -2250,8 +2251,8 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         if (m_rangemode == RangeLineMode)
             m_rangemode = RangeLineModeExclusive;
         removeText(currentRange());
-        if (!dotCommand.isEmpty())
-            setDotCommand(QLatin1Char('c') + dotCommand);
+        if (!dotCommandMovement.isEmpty())
+            setDotCommand(QLatin1Char('c') + dotCommandMovement);
         if (m_movetype == MoveLineWise)
             insertAutomaticIndentation(true);
         endEditBlock();
@@ -2261,8 +2262,8 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         setUndoPosition();
         Range range = currentRange();
         removeText(range);
-        if (!dotCommand.isEmpty())
-            setDotCommand(QLatin1Char('d') + dotCommand);
+        if (!dotCommandMovement.isEmpty())
+            setDotCommand(QLatin1Char('d') + dotCommandMovement);
         if (m_movetype == MoveLineWise)
             handleStartOfLine();
         m_submode = NoSubMode;
@@ -2287,16 +2288,16 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
     } else if (m_submode == TransformSubMode) {
         if (m_subsubmode == InvertCaseSubSubMode) {
             invertCase(currentRange());
-            if (!dotCommand.isEmpty())
-                setDotCommand(QLatin1Char('~') + dotCommand);
+            if (!dotCommandMovement.isEmpty())
+                setDotCommand(QLatin1Char('~') + dotCommandMovement);
         } else if (m_subsubmode == UpCaseSubSubMode) {
             upCase(currentRange());
-            if (!dotCommand.isEmpty())
-                setDotCommand("gU" + dotCommand);
+            if (!dotCommandMovement.isEmpty())
+                setDotCommand("gU" + dotCommandMovement);
         } else if (m_subsubmode == DownCaseSubSubMode) {
             downCase(currentRange());
-            if (!dotCommand.isEmpty())
-                setDotCommand("gu" + dotCommand);
+            if (!dotCommandMovement.isEmpty())
+                setDotCommand("gu" + dotCommandMovement);
         }
         m_submode = NoSubMode;
         m_subsubmode = NoSubSubMode;
@@ -2309,22 +2310,22 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         setUndoPosition();
         indentSelectedText();
         m_submode = NoSubMode;
-        if (!dotCommand.isEmpty())
-            setDotCommand('=' + dotCommand);
+        if (!dotCommandMovement.isEmpty())
+            setDotCommand('=' + dotCommandMovement);
     } else if (m_submode == ShiftRightSubMode) {
         recordJump();
         setUndoPosition();
         shiftRegionRight(1);
         m_submode = NoSubMode;
-        if (!dotCommand.isEmpty())
-            setDotCommand('>' + dotCommand);
+        if (!dotCommandMovement.isEmpty())
+            setDotCommand('>' + dotCommandMovement);
     } else if (m_submode == ShiftLeftSubMode) {
         recordJump();
         setUndoPosition();
         shiftRegionLeft(1);
         m_submode = NoSubMode;
-        if (!dotCommand.isEmpty())
-            setDotCommand('<' + dotCommand);
+        if (!dotCommandMovement.isEmpty())
+            setDotCommand('<' + dotCommandMovement);
     }
 
     resetCommandMode();
@@ -2589,6 +2590,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         m_submode = NoSubMode;
         m_rangemode = RangeLineMode;
     } else if (m_submode == ReplaceSubMode) {
+        setDotCommand(visualDotCommand() + 'r' + input.asChar());
         if (isVisualMode()) {
             setUndoPosition();
             if (isVisualLineMode())
@@ -2837,7 +2839,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         //    << input;
         QString savedCommand = g.dotCommand;
         g.dotCommand.clear();
-        replay(savedCommand, count());
+        replay(savedCommand, 1);
         enterCommandMode();
         g.dotCommand = savedCommand;
     } else if (input.is('<')) {
@@ -2872,6 +2874,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         setUndoPosition();
         breakEditBlock();
         enterInsertMode();
+        setDotCommand(QString(QLatin1Char('a')));
         m_lastInsertion.clear();
         if (!atEndOfLine())
             moveRight();
@@ -2887,6 +2890,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         updateMiniBuffer();
     } else if (input.isControl('a')) {
         changeNumberTextObject(true);
+        setDotCommand("%1<c-a>", count());
     } else if (input.is('b') || input.isShift(Key_Left)) {
         m_movetype = MoveExclusive;
         moveToNextWordStart(count(), false, false);
@@ -2905,6 +2909,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         m_submode = ChangeSubMode;
     } else if ((input.is('c') || input.is('C') || input.is('s') || input.is('R'))
           && (isVisualCharMode() || isVisualLineMode())) {
+        setDotCommand(visualDotCommand() + input.asChar());
         if ((input.is('c')|| input.is('s')) && isVisualCharMode()) {
             leaveVisualMode();
             m_rangemode = RangeCharMode;
@@ -2942,6 +2947,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
     } else if ((input.is('d') || input.is('x') || input.isKey(Key_Delete))
             && isVisualMode()) {
         setUndoPosition();
+        setDotCommand(visualDotCommand() + 'x');
         if (isVisualCharMode()) {
             leaveVisualMode();
             m_submode = DeleteSubMode;
@@ -2972,6 +2978,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         finishMovement();
     } else if ((input.is('D') || input.is('X')) &&
          (isVisualCharMode() || isVisualLineMode())) {
+        setDotCommand(visualDotCommand() + 'X');
         leaveVisualMode();
         m_rangemode = RangeLineMode;
         m_submode = NoSubMode;
@@ -2979,6 +2986,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         removeText(currentRange());
         moveToFirstNonBlankOnLine();
     } else if ((input.is('D') || input.is('X')) && isVisualBlockMode()) {
+        setDotCommand(visualDotCommand() + 'X');
         leaveVisualMode();
         m_rangemode = RangeBlockAndTailMode;
         yankText(currentRange(), m_register);
@@ -3102,7 +3110,6 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
         moveDown(count());
         finishMovement("%1j", count());
     } else if (input.is('J')) {
-        setDotCommand("%1J", count());
         beginEditBlock();
         if (m_submode == NoSubMode) {
             for (int i = qMax(count(), 2) - 1; --i >= 0; ) {
@@ -3123,7 +3130,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
                 moveLeft();
         }
         endEditBlock();
-        finishMovement();
+        finishMovement("%1J");
     } else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) {
         m_movetype = MoveLineWise;
         moveUp(count());
@@ -3142,6 +3149,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
         moveUp(qMax(count(), 1));
         handleStartOfLine();
         finishMovement();
+        finishMovement("%1L");
     } else if (input.isControl('l')) {
         // screen redraw. should not be needed
     } else if (input.is('m')) {
@@ -3335,6 +3343,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
         finishMovement();
     } else if (input.isControl('x')) {
         changeNumberTextObject(false);
+        setDotCommand("%1<c-a>", count());
     } else if (input.is('X')) {
         if (leftDist() > 0) {
             setAnchor();
@@ -3447,12 +3456,12 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
         moveDown(count() * (linesOnScreen() - 2) - cursorLineOnScreen());
         scrollToLine(cursorLine());
         handleStartOfLine();
-        finishMovement();
+        finishMovement("%1f", count());
     } else if (input.isKey(Key_PageUp) || input.isControl('b')) {
         moveUp(count() * (linesOnScreen() - 2) + cursorLineOnScreen());
         scrollToLine(cursorLine() + linesOnScreen() - 2);
         handleStartOfLine();
-        finishMovement();
+        finishMovement("%1b", count());
     } else if (input.isKey(Key_Delete)) {
         setAnchor();
         moveRight(qMin(1, rightDist()));
@@ -5923,14 +5932,35 @@ void FakeVimHandler::Private::handleStartOfLine()
 void FakeVimHandler::Private::replay(const QString &command, int n)
 {
     //qDebug() << "REPLAY: " << quoteUnprintable(command);
+    Inputs inputs(command);
     for (int i = n; --i >= 0; ) {
-        foreach (QChar c, command) {
-            //qDebug() << "  REPLAY: " << c.unicode();
-            handleDefaultKey(Input(c));
+        foreach (Input in, inputs) {
+            handleDefaultKey(in);
         }
     }
 }
 
+QString FakeVimHandler::Private::visualDotCommand() const
+{
+    QTextCursor start(cursor());
+    QTextCursor end(start);
+    end.setPosition(end.anchor());
+
+    if (isVisualCharMode())
+        return QString("v%1l").arg(qAbs(start.position() - end.position()));
+
+    if (isVisualLineMode())
+        return QString("V%1j").arg(qAbs(start.blockNumber() - end.blockNumber()));
+
+    if (isVisualBlockMode()) {
+        return QString("<c-v>%1l%2j")
+            .arg(qAbs(start.positionInBlock() - end.positionInBlock()))
+            .arg(qAbs(start.blockNumber() - end.blockNumber()));
+    }
+
+    return QString();
+}
+
 void FakeVimHandler::Private::selectTextObject(bool simple, bool inner)
 {
     bool setupAnchor = (position() == anchor());
@@ -6401,6 +6431,16 @@ void FakeVimHandler::miniBufferTextEdited(const QString &text, int cursorPos)
     d->miniBufferTextEdited(text, cursorPos);
 }
 
+void FakeVimHandler::setTextCursorPosition(int position)
+{
+    int pos = qMax(0, qMin(position, d->lastPositionInDocument()));
+    if (d->isVisualMode())
+        d->setPosition(pos);
+    else
+        d->setAnchorAndPosition(pos, pos);
+    d->setTargetColumn();
+}
+
 } // namespace Internal
 } // namespace FakeVim
 
diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h
index 7c72e755b981dc302c267301df6f53893989bf51..940e6acd17ee82b10d8c0b5a88cd915ea02843c3 100644
--- a/src/plugins/fakevim/fakevimhandler.h
+++ b/src/plugins/fakevim/fakevimhandler.h
@@ -131,6 +131,9 @@ public slots:
 
     void miniBufferTextEdited(const QString &text, int cursorPos);
 
+    // Set text cursor position. Keeps anchor if in visual mode.
+    void setTextCursorPosition(int position);
+
 signals:
     void commandBufferChanged(const QString &msg, int pos, int messageLevel, QObject *eventFilter);
     void statusDataChanged(const QString &msg);