diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp
index 5a663e1132dc2938ca86252b2002deb3f2f1c7e7..24bd9c79bcca4573ab781a066a3c2ee212311c14 100644
--- a/src/plugins/fakevim/fakevim_test.cpp
+++ b/src/plugins/fakevim/fakevim_test.cpp
@@ -274,6 +274,7 @@ void FakeVimPlugin::setup(TestData *data)
 {
     setupTest(&data->title, &data->handler, &data->edit);
     data->reset();
+    data->doCommand("set nopasskeys | set nopasscontrolkey");
 }
 
 
diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp
index 989f916ab6d2bc3530215e826fd2bf263da2cdb9..22aad3fc26d860bed3dec39ca1e74b7c20fc470c 100644
--- a/src/plugins/fakevim/fakevimactions.cpp
+++ b/src/plugins/fakevim/fakevimactions.cpp
@@ -180,7 +180,7 @@ FakeVimSettings *theFakeVimSettings()
 #endif
     createAction(s, ConfigShowMarks,      false, _("ShowMarks"),      _("sm"));
     createAction(s, ConfigPassControlKey, false, _("PassControlKey"), _("pck"));
-    createAction(s, ConfigPassNewLine,    false, _("PassNewLine"),    _("pnl"));
+    createAction(s, ConfigPassKeys,       true,  _("PassKeys"),       _("pk"));
 
     // Emulated Vim setting
     createAction(s, ConfigStartOfLine,    true,  _("StartOfLine"),   _("sol"));
diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h
index ce20850c52d29c2564f7044ef3574423671c94fa..c0b37867f9866871c6df2022bbb8ba30bb6bbc3b 100644
--- a/src/plugins/fakevim/fakevimactions.h
+++ b/src/plugins/fakevim/fakevimactions.h
@@ -93,7 +93,7 @@ enum FakeVimSettingsCode
     // other actions
     ConfigShowMarks,
     ConfigPassControlKey,
-    ConfigPassNewLine,
+    ConfigPassKeys,
     ConfigClipboard,
     ConfigShowCmd,
     ConfigScrollOff
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 91a35db9056add73daa39d5dfe28891e4df4b127..3dd5695efcbb45abb6aa4b15b72255b6579aa16d 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -949,6 +949,8 @@ public:
 
     int key() const { return m_key; }
 
+    int modifiers() const { return m_modifiers; }
+
     // Return raw character for macro recording or dot command.
     QChar raw() const
     {
@@ -1708,6 +1710,7 @@ public:
     int m_oldInternalPosition; // copy from last event to check for external changes
     int m_oldInternalAnchor;
     int m_oldPosition; // FIXME: Merge with above.
+    int m_oldDocumentLength;
     int m_register;
     QString m_mvcount;
     QString m_opcount;
@@ -1772,6 +1775,11 @@ public:
 
     void insertNewLine();
 
+    bool handleInsertInEditor(const Input &input, QString *insert);
+    bool passEventToEditor(QEvent &event); // Pass event to editor widget without filtering. Returns true if event was processed.
+    // Guess insert command for text modification which happened externally (e.g. code-completion).
+    QString guessInsertCommand(int pos1, int pos2, int len1, int len2);
+
     // undo handling
     int revision() const { return document()->availableUndoSteps(); }
     void undoRedo(bool undo);
@@ -1974,6 +1982,7 @@ void FakeVimHandler::Private::init()
     m_oldExternalAnchor = -1;
     m_oldExternalPosition = -1;
     m_oldPosition = -1;
+    m_oldDocumentLength = -1;
     m_breakEditBlock = false;
     m_searchStartPosition = 0;
     m_searchFromScreenLine = 0;
@@ -2255,6 +2264,7 @@ void FakeVimHandler::Private::recordInsertion(const QString &insert)
         m_oldPosition = pos;
         setTargetColumn();
     }
+    m_oldDocumentLength = document()->characterCount();
 }
 
 void FakeVimHandler::Private::ensureCursorVisible()
@@ -3601,7 +3611,9 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
         //    << input;
         QString savedCommand = g.dotCommand;
         g.dotCommand.clear();
+        beginEditBlock();
         replay(savedCommand);
+        endEditBlock();
         resetCommandMode();
         g.dotCommand = savedCommand;
     } else if (input.is('<') || input.is('>') || input.is('=')) {
@@ -4305,16 +4317,17 @@ EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
 EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
 {
     bool clearLastInsertion = m_breakEditBlock;
-    if (m_oldPosition != position()) {
+    int pos2 = position();
+    int len2 = document()->characterCount();
+    if (m_oldPosition != pos2 || m_oldDocumentLength != len2) {
         if (clearLastInsertion) {
             clearLastInsertion = false;
             m_lastInsertion.clear();
         }
-        recordInsertion();
+        recordInsertion(guessInsertCommand(m_oldPosition, pos2, m_oldDocumentLength, len2));
     }
 
     QString insert;
-    bool move = false;
     if (input.isEscape()) {
         // Repeat insertion [count] times.
         // One instance was already physically inserted while typing.
@@ -4357,6 +4370,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
             m_lastInsertion.remove(0, 1);
         g.dotCommand += m_lastInsertion + _("<ESC>");
         enterCommandMode();
+        setTargetColumn();
         m_ctrlVActive = false;
         m_visualBlockInsert = false;
     } else if (m_ctrlVActive) {
@@ -4381,86 +4395,82 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
         insert = _("<INSERT>");
     } else if (input.isKey(Key_Left)) {
         moveLeft(count());
-        move = true;
         setTargetColumn();
     } else if (input.isControl(Key_Left)) {
         moveToNextWordStart(count(), false, false);
-        move = true;
         setTargetColumn();
     } else if (input.isKey(Key_Down)) {
         //removeAutomaticIndentation();
         m_submode = NoSubMode;
         moveDown(count());
-        move = true;
     } else if (input.isKey(Key_Up)) {
         //removeAutomaticIndentation();
         m_submode = NoSubMode;
         moveUp(count());
-        move = true;
     } else if (input.isKey(Key_Right)) {
         moveRight(count());
-        move = true;
         setTargetColumn();
     } else if (input.isControl(Key_Right)) {
         moveToNextWordStart(count(), false, true);
         moveRight(); // we need one more move since we are in insert mode
-        move = true;
         setTargetColumn();
     } else if (input.isKey(Key_Home)) {
         moveToStartOfLine();
-        move = true;
         setTargetColumn();
     } else if (input.isKey(Key_End)) {
         if (count() > 1)
             moveDown(count() - 1);
         moveBehindEndOfLine();
-        move = true;
         setTargetColumn();
         m_targetColumn = -1;
     } else if (input.isReturn() || input.isControl('j') || input.isControl('m')) {
-        joinPreviousEditBlock();
-        m_submode = NoSubMode;
-        insertNewLine();
-        insert = _("\n");
-        endEditBlock();
+        if (!input.isReturn() || !handleInsertInEditor(input, &insert)) {
+            joinPreviousEditBlock();
+            m_submode = NoSubMode;
+            insertNewLine();
+            insert = _("\n");
+            endEditBlock();
+        }
     } else if (input.isBackspace()) {
-        joinPreviousEditBlock();
-        m_justAutoIndented = 0;
-        if (!m_lastInsertion.isEmpty()
-                || hasConfig(ConfigBackspace, "start")
-                || hasConfig(ConfigBackspace, "2")) {
-            const int line = cursorLine() + 1;
-            const Column col = cursorColumn();
-            QString data = lineContents(line);
-            const Column ind = indentation(data);
-            if (col.logical <= ind.logical && col.logical
-                    && startsWithWhitespace(data, col.physical)) {
-                const int ts = config(ConfigTabStop).toInt();
-                const int newl = col.logical - 1 - (col.logical - 1) % ts;
-                const QString prefix = tabExpand(newl);
-                setLineContents(line, prefix + data.mid(col.physical));
-                moveToStartOfLine();
-                moveRight(prefix.size());
-            } else {
-                setAnchor();
-                cursor().deletePreviousChar();
+        if (!handleInsertInEditor(input, &insert)) {
+            joinPreviousEditBlock();
+            m_justAutoIndented = 0;
+            if (!m_lastInsertion.isEmpty()
+                    || hasConfig(ConfigBackspace, "start")
+                    || hasConfig(ConfigBackspace, "2")) {
+                const int line = cursorLine() + 1;
+                const Column col = cursorColumn();
+                QString data = lineContents(line);
+                const Column ind = indentation(data);
+                if (col.logical <= ind.logical && col.logical
+                        && startsWithWhitespace(data, col.physical)) {
+                    const int ts = config(ConfigTabStop).toInt();
+                    const int newl = col.logical - 1 - (col.logical - 1) % ts;
+                    const QString prefix = tabExpand(newl);
+                    setLineContents(line, prefix + data.mid(col.physical));
+                    moveToStartOfLine();
+                    moveRight(prefix.size());
+                } else {
+                    setAnchor();
+                    cursor().deletePreviousChar();
+                }
             }
+            insert = _("<BS>");
+            endEditBlock();
         }
-        insert = _("<BS>");
-        endEditBlock();
     } else if (input.isKey(Key_Delete)) {
-        joinPreviousEditBlock();
-        cursor().deleteChar();
-        insert = _("<DELETE>");
-        endEditBlock();
+        if (!handleInsertInEditor(input, &insert)) {
+            joinPreviousEditBlock();
+            cursor().deleteChar();
+            insert = _("<DELETE>");
+            endEditBlock();
+        }
     } else if (input.isKey(Key_PageDown) || input.isControl('f')) {
         removeAutomaticIndentation();
         moveDown(count() * (linesOnScreen() - 2));
-        move = true;
     } else if (input.isKey(Key_PageUp) || input.isControl('b')) {
         removeAutomaticIndentation();
         moveUp(count() * (linesOnScreen() - 2));
-        move = true;
     } else if (input.isKey(Key_Tab)) {
         m_justAutoIndented = 0;
         if (hasConfig(ConfigExpandTab)) {
@@ -4511,16 +4521,20 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
         if (data && data->hasText())
             insertInInsertMode(data->text());
         insert = _("<S-INSERT>");
-    } else if (!input.text().isEmpty()) {
-        insert = input.text();
-        insertInInsertMode(insert);
-        insert.replace(_("<"), _("<LT>"));
     } else {
-        // We don't want fancy stuff in insert mode.
-        return EventHandled;
+        if (!handleInsertInEditor(input, &insert)) {
+            insert = input.text();
+            if (!insert.isEmpty()) {
+                insertInInsertMode(insert);
+                insert.replace(_("<"), _("<LT>"));
+            } else {
+                // We don't want fancy stuff in insert mode.
+                return EventHandled;
+            }
+        }
     }
 
-    if (move) {
+    if (insert.isNull()) {
         breakEditBlock();
         m_oldPosition = position();
     } else {
@@ -6664,26 +6678,113 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
 
 void FakeVimHandler::Private::insertNewLine()
 {
-    if ( hasConfig(ConfigPassNewLine) ) {
+    if ( hasConfig(ConfigPassKeys) ) {
         QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, QLatin1String("\n"));
+        if (passEventToEditor(event))
+            return;
+    }
 
-        removeEventFilter();
+    insertText(QString::fromLatin1("\n"));
+    insertAutomaticIndentation(true);
+}
 
-        QTextCursor tc = m_cursor;
-        tc.setPosition(tc.position());
-        EDITOR(setTextCursor(tc));
-        bool accepted = QApplication::sendEvent(editor(), &event);
+bool FakeVimHandler::Private::handleInsertInEditor(const Input &input, QString *insert)
+{
+    if (!hasConfig(ConfigPassKeys) || m_editBlockLevel > 0)
+        return false;
 
-        installEventFilter();
+    const int pos1 = position();
+    const int len1 = lastPositionInDocument();
 
-        if (accepted) {
-            setPosition(EDITOR(textCursor()).position());
-            return;
+    QKeyEvent event(QEvent::KeyPress, input.key(),
+                    static_cast<Qt::KeyboardModifiers>(input.modifiers()), input.text());
+    if (!passEventToEditor(event))
+        return false;
+
+    const int pos2 = position();
+    const int len2 = lastPositionInDocument();
+
+    *insert = guessInsertCommand(pos1, pos2, len1, len2);
+
+    return true;
+}
+
+bool FakeVimHandler::Private::passEventToEditor(QEvent &event)
+{
+    removeEventFilter();
+
+    QTextCursor tc = m_cursor;
+    tc.setPosition(tc.position());
+    EDITOR(setTextCursor(tc));
+    bool accepted = QApplication::sendEvent(editor(), &event);
+
+    installEventFilter();
+
+    if (accepted)
+        setPosition(EDITOR(textCursor()).position());
+
+    return accepted;
+}
+
+QString FakeVimHandler::Private::guessInsertCommand(int pos1, int pos2, int len1, int len2)
+{
+    QString insert;
+
+    // Guess the inserted/deleted text.
+    if (len1 > len2) {
+        // Text deleted.
+        if (pos1 == pos2) {
+            // Text after cursor deleted.
+            insert = QString(_("<C-O>%1x")).arg(len1 - len2);
+        } else if (pos1 > pos2) {
+            // Text in front of cursor deleted.
+            const int backspaces = pos1 - pos2;
+            insert = QString(_("<BS>")).repeated(backspaces);
+            // Some text after cursor may have beed deleted too.
+            const int deletes = len1 - len2 - backspaces;
+            if (deletes > 0)
+                insert.append(QString(_("<C-O>%1x")).arg(deletes));
+        }
+    } else if (len1 < len2) {
+        // Text inserted.
+        if (pos1 < pos2) {
+            QTextCursor tc = cursor();
+            tc.setPosition(pos1);
+            tc.setPosition(pos2, KeepAnchor);
+            insert = QString(tc.selectedText()).replace(_("<"), _("<LT>"));
+
+            const int textLen = pos2 - pos1;
+            const int rest = len2 - len1 - textLen;
+            if (rest > 0) {
+                // Text inserted after new cursor position.
+                // On dot command, cursor must insert the same text and move in front of it.
+                tc.setPosition(pos2);
+                tc.setPosition(pos2 + rest, KeepAnchor);
+                insert.append(QString(tc.selectedText()).replace(_("<"), _("<LT>")));
+
+                const int up = document()->findBlock(pos2).blockNumber()
+                        - document()->findBlock(pos1).blockNumber();
+                if (up > 0)
+                    insert.append(QString(_("<UP>")).repeated(up));
+                insert.append(_("<END>"));
+                const int right = rightDist();
+                if (right > 0)
+                    insert.append(QString(_("<LEFT>")).repeated(right));
+            }
         }
+    } else {
+        // Document length is unchanged so assume that no text inserted or deleted.
+        // Check if cursor moved.
+        const int right = pos2 - pos1;
+        if (right > 0)
+            insert = QString(_("<RIGHT>")).repeated(right);
+        else if (right < 0)
+            insert = QString(_("<LEFT>")).repeated(-right);
+        else
+            insert = _(""); // Empty non-null string.
     }
 
-    insertText(QString::fromLatin1("\n"));
-    insertAutomaticIndentation(true);
+    return insert;
 }
 
 QString FakeVimHandler::Private::lineContents(int line) const
@@ -7000,6 +7101,7 @@ void FakeVimHandler::Private::enterInsertMode()
     m_subsubmode = NoSubSubMode;
     m_lastInsertion.clear();
     m_oldPosition = position();
+    m_oldDocumentLength = document()->characterCount();
     if (g.returnToMode != InsertMode) {
         g.returnToMode = InsertMode;
         // If entering insert mode from command mode, m_targetColumn shouldn't be -1 (end of line).
diff --git a/src/plugins/fakevim/fakevimoptions.ui b/src/plugins/fakevim/fakevimoptions.ui
index 5114db3108765a38127654c1b952bfcad8ad82e7..b19a21a5ff7caf51214c13ed25e7ad0ebc5a9256 100644
--- a/src/plugins/fakevim/fakevimoptions.ui
+++ b/src/plugins/fakevim/fakevimoptions.ui
@@ -2,6 +2,14 @@
 <ui version="4.0">
  <class>FakeVim::Internal::FakeVimOptionPage</class>
  <widget class="QWidget" name="FakeVim::Internal::FakeVimOptionPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>580</width>
+    <height>479</height>
+   </rect>
+  </property>
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <widget class="QCheckBox" name="checkBoxUseFakeVim">
@@ -113,12 +121,12 @@
          </widget>
         </item>
         <item row="7" column="0">
-         <widget class="QCheckBox" name="checkBoxPassNewLine">
+         <widget class="QCheckBox" name="checkBoxPassKeys">
           <property name="toolTip">
-           <string>Let Qt Creator handle new lines so that comments or code blocks can be properly completed and expanded.</string>
+           <string>Let Qt Creator handle some key presses in insert mode so that code can be properly completed and expanded.</string>
           </property>
           <property name="text">
-           <string>Pass new line</string>
+           <string>Pass keys in insert mode</string>
           </property>
          </widget>
         </item>
@@ -363,7 +371,7 @@
   <tabstop>checkBoxHlSearch</tabstop>
   <tabstop>checkBoxShowCmd</tabstop>
   <tabstop>checkBoxStartOfLine</tabstop>
-  <tabstop>checkBoxPassNewLine</tabstop>
+  <tabstop>checkBoxPassKeys</tabstop>
   <tabstop>checkBoxIncSearch</tabstop>
   <tabstop>checkBoxUseCoreSearch</tabstop>
   <tabstop>checkBoxIgnoreCase</tabstop>
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 682aadf23563d7348bd4030a03100dab4d25dd79..a982c8121a777a3efb0c7f3acef3125cc3fe530c 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -292,8 +292,8 @@ QWidget *FakeVimOptionPage::createPage(QWidget *parent)
         m_ui.checkBoxSmartTab);
     m_group.insert(theFakeVimSetting(ConfigStartOfLine),
         m_ui.checkBoxStartOfLine);
-    m_group.insert(theFakeVimSetting(ConfigPassNewLine),
-        m_ui.checkBoxPassNewLine);
+    m_group.insert(theFakeVimSetting(ConfigPassKeys),
+        m_ui.checkBoxPassKeys);
     m_group.insert(theFakeVimSetting(ConfigTabStop),
         m_ui.spinBoxTabStop);
     m_group.insert(theFakeVimSetting(ConfigScrollOff),
@@ -352,7 +352,7 @@ QWidget *FakeVimOptionPage::createPage(QWidget *parent)
                 << sep << m_ui.checkBoxSmartCase->text()
                 << sep << m_ui.checkBoxShowMarks->text()
                 << sep << m_ui.checkBoxPassControlKey->text()
-                << sep << m_ui.checkBoxPassNewLine->text()
+                << sep << m_ui.checkBoxPassKeys->text()
                 << sep << m_ui.checkBoxIgnoreCase->text()
                 << sep << m_ui.checkBoxWrapScan->text()
                 << sep << m_ui.checkBoxShowCmd->text()
@@ -391,7 +391,7 @@ void FakeVimOptionPage::setQtStyle()
     m_ui.checkBoxSmartIndent->setChecked(true);
     m_ui.checkBoxIncSearch->setChecked(true);
     m_ui.lineEditBackspace->setText(_("indent,eol,start"));
-    m_ui.checkBoxPassNewLine->setChecked(true);
+    m_ui.checkBoxPassKeys->setChecked(true);
 }
 
 void FakeVimOptionPage::setPlainStyle()
@@ -404,7 +404,7 @@ void FakeVimOptionPage::setPlainStyle()
     m_ui.checkBoxSmartIndent->setChecked(false);
     m_ui.checkBoxIncSearch->setChecked(false);
     m_ui.lineEditBackspace->setText(QString());
-    m_ui.checkBoxPassNewLine->setChecked(false);
+    m_ui.checkBoxPassKeys->setChecked(false);
 }
 
 void FakeVimOptionPage::openVimRc()