OSDN Git Service

fakevim: synchronize with master
authorhjk <qtc-committer@nokia.com>
Wed, 2 Jun 2010 09:09:14 +0000 (11:09 +0200)
committerhjk <qtc-committer@nokia.com>
Wed, 2 Jun 2010 09:09:39 +0000 (11:09 +0200)
In theory this should have been a backport of the search-related commits
7e8c345a and 46fa3aa7, but they are buried in the middle of the Big
Commandline Reorganization. Just taking everything has lower risk and
is faster.

src/plugins/fakevim/fakevimactions.cpp
src/plugins/fakevim/fakevimactions.h
src/plugins/fakevim/fakevimhandler.cpp
src/plugins/fakevim/fakevimhandler.h
src/plugins/fakevim/fakevimoptions.ui
src/plugins/fakevim/fakevimplugin.cpp

index f19b5b1..a17fbe1 100644 (file)
@@ -209,10 +209,13 @@ FakeVimSettings *theFakeVimSettings()
     item->setSettingsKey(group, _("IsKeyword"));
     instance->insertItem(ConfigIsKeyword, item, _("iskeyword"), _("isk"));
 
+    // Invented here.
     item = new SavedAction(instance);
-    item->setText(QCoreApplication::translate("FakeVim::Internal",
-        "FakeVim properties..."));
-    instance->insertItem(SettingsDialog, item);
+    item->setDefaultValue(false);
+    item->setValue(false);
+    item->setSettingsKey(group, _("ShowMarks"));
+    item->setCheckable(true);
+    instance->insertItem(ConfigShowMarks, item, _("showmarks"), _("sm"));
 
     return instance;
 }
index a05e129..5929376 100644 (file)
@@ -64,7 +64,7 @@ enum FakeVimSettingsCode
     ConfigIsKeyword,
 
     // other actions
-    SettingsDialog,
+    ConfigShowMarks,
 };
 
 class FakeVimSettings : public QObject
index a215514..799e83f 100644 (file)
@@ -56,6 +56,7 @@
 //   m_tc.position() (== position()). The character below position() is not included
 //   if the last movement command was exclusive (MoveExclusive).
 //   The value of m_tc.anchor() is not used.
+//
 
 #include "fakevimhandler.h"
 #include "fakevimsyntax.h"
@@ -69,6 +70,7 @@
 #include <QtCore/QProcess>
 #include <QtCore/QRegExp>
 #include <QtCore/QTextStream>
+#include <QtCore/QTimer>
 #include <QtCore/QtAlgorithms>
 #include <QtCore/QStack>
 
@@ -126,51 +128,57 @@ namespace Internal {
 #define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
 
 const int ParagraphSeparator = 0x00002029;
+typedef QLatin1String _;
 
 using namespace Qt;
 
+/*! A \e Mode represents one of the basic modes of operation of FakeVim.
+*/
 
 enum Mode
 {
     InsertMode,
+    ReplaceMode,
     CommandMode,
     ExMode,
-    SearchForwardMode,
-    SearchBackwardMode,
 };
 
+/*! A \e SubMode is used for things that require one more data item
+    and are 'nested' behind a \l Mode.
+*/
 enum SubMode
 {
     NoSubMode,
-    ChangeSubMode,     // used for c
-    DeleteSubMode,     // used for d
-    FilterSubMode,     // used for !
-    IndentSubMode,     // used for =
-    RegisterSubMode,   // used for "
-    ReplaceSubMode,    // used for R and r
-    ShiftLeftSubMode,  // used for <
-    ShiftRightSubMode, // used for >
-    TransformSubMode,  // used for ~/gu/gU
-    WindowSubMode,     // used for Ctrl-w
-    YankSubMode,       // used for y
-    ZSubMode,          // used for z
-    CapitalZSubMode    // used for Z
+    ChangeSubMode,     // Used for c
+    DeleteSubMode,     // Used for d
+    FilterSubMode,     // Used for !
+    IndentSubMode,     // Used for =
+    RegisterSubMode,   // Used for "
+    ShiftLeftSubMode,  // Used for <
+    ShiftRightSubMode, // Used for >
+    TransformSubMode,  // Used for ~/gu/gU
+    WindowSubMode,     // Used for Ctrl-w
+    YankSubMode,       // Used for y
+    ZSubMode,          // Used for z
+    CapitalZSubMode,   // Used for Z
+    ReplaceSubMode,    // Used for r
 };
 
+/*! A \e SubSubMode is used for things that require one more data item
+    and are 'nested' behind a \l SubMode.
+*/
 enum SubSubMode
 {
-    // typically used for things that require one more data item
-    // and are 'nested' behind a mode
     NoSubSubMode,
-    FtSubSubMode,         // used for f, F, t, T
-    MarkSubSubMode,       // used for m
-    BackTickSubSubMode,   // used for `
-    TickSubSubMode,       // used for '
-    InvertCaseSubSubMode, // used for ~
-    DownCaseSubSubMode,   // used for gu
-    UpCaseSubSubMode,     // used for gU
-    ReplaceSubSubMode,    // used for r after visual mode
-    TextObjectSubSubMode, // used for thing like iw, aW, as etc.
+    FtSubSubMode,         // Used for f, F, t, T.
+    MarkSubSubMode,       // Used for m.
+    BackTickSubSubMode,   // Used for `.
+    TickSubSubMode,       // Used for '.
+    InvertCaseSubSubMode, // Used for ~.
+    DownCaseSubSubMode,   // Used for gu.
+    UpCaseSubSubMode,     // Used for gU.
+    TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
+    SearchSubSubMode,
 };
 
 enum VisualMode
@@ -188,14 +196,27 @@ enum MoveType
     MoveLineWise,
 };
 
-enum RangeMode
-{
-    RangeCharMode,  // v
-    RangeLineMode,  // V
-    RangeLineModeExclusive, // like above, but keep one newline when deleting
-    RangeBlockMode, // Ctrl-v
-    RangeBlockAndTailMode, // Ctrl-v for D and X
-};
+/*!
+    \enum RangeMode
+
+    The \e RangeMode serves as a means to define how the "Range" between
+    the \l cursor and the \l anchor position is to be interpreted.
+
+    \value RangeCharMode   Entered by pressing \key v. The range includes
+                           all characters between cursor and anchor.
+    \value RangeLineMode   Entered by pressing \key V. The range includes
+                           all lines between the line of the cursor and
+                           the line of the anchor.
+    \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one
+                           newline when deleting.
+    \value RangeBlockMode  Entered by pressing \key Ctrl-v. The range includes
+                           all characters with line and column coordinates
+                           between line and columns coordinates of cursor and
+                           anchor.
+    \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
+                           all characters in the affected lines up to the end
+                           of these lines.
+*/
 
 enum EventResult
 {
@@ -207,8 +228,8 @@ enum EventResult
 struct Column
 {
     Column(int p, int l) : physical(p), logical(l) {}
-    int physical; // number of characters in the data
-    int logical; // column on screen
+    int physical; // Number of characters in the data.
+    int logical; // Column on screen.
 };
 
 struct CursorPosition
@@ -223,33 +244,63 @@ struct CursorPosition
 struct Register
 {
     Register() : rangemode(RangeCharMode) {}
+    Register(const QString &c) : contents(c), rangemode(RangeCharMode) {}
     Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
     QString contents;
     RangeMode rangemode;
 };
 
-struct Range
+QDebug operator<<(QDebug ts, const Register &reg)
 {
-    Range()
-        : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
-    {}
+    return ts << reg.contents;
+}
 
-    Range(int b, int e, RangeMode m = RangeCharMode)
-        : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
-    {}
+struct SearchData
+{
+    SearchData() { init(); }
 
-    QString toString() const
-    {
-        return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
-            .arg(rangemode);
-    }
+    void init() { forward = true; mustMove = true; highlightMatches = true;
+        highlightCursor = true; }
 
-    int beginPos;
-    int endPos;
-    RangeMode rangemode;
+    QString needle;
+    bool forward;
+    bool mustMove;
+    bool highlightMatches;
+    bool highlightCursor;
 };
 
-QDebug &operator<<(QDebug &ts, const QList<QTextEdit::ExtraSelection> &sels)
+
+Range::Range()
+    : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
+{}
+
+Range::Range(int b, int e, RangeMode m)
+    : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
+{}
+
+QString Range::toString() const
+{
+    return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
+        .arg(rangemode);
+}
+
+QDebug operator<<(QDebug ts, const Range &range)
+{
+    return ts << '[' << range.beginPos << ',' << range.endPos << ']';
+}
+
+
+
+ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
+    : cmd(c), hasBang(false), args(a), range(r)
+{}
+
+QDebug operator<<(QDebug ts, const ExCommand &cmd)
+{
+    return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
+}
+
+QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
 {
     foreach (const QTextEdit::ExtraSelection &sel, sels)
         ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
@@ -260,11 +311,14 @@ QString quoteUnprintable(const QString &ba)
 {
     QString res;
     for (int i = 0, n = ba.size(); i != n; ++i) {
-        QChar c = ba.at(i);
+        const QChar c = ba.at(i);
+        const int cc = c.unicode();
         if (c.isPrint())
             res += c;
+        else if (cc == '\n')
+            res += _("<CR>");
         else
-            res += QString("\\x%1").arg(c.unicode(), 2, 16);
+            res += QString("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
     }
     return res;
 }
@@ -280,9 +334,9 @@ static bool startsWithWhitespace(const QString &str, int col)
     return true;
 }
 
-inline QString msgE20MarkNotSet(const QString &text)
+inline QString msgMarkNotSet(const QString &text)
 {
-    return FakeVimHandler::tr("E20: Mark '%1' not set").arg(text);
+    return FakeVimHandler::tr("Mark '%1' not set").arg(text);
 }
 
 class Input
@@ -294,7 +348,7 @@ public:
     explicit Input(QChar x)
         : m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x) {}
 
-    Input(int k, int m, QString t)
+    Input(int k, int m, const QString &t)
         : m_key(k), m_modifiers(m), m_text(t)
     {
         // m_xkey is only a cache.
@@ -305,11 +359,28 @@ public:
     {
         return m_xkey >= '0' && m_xkey <= '9';
     }
+
     bool isKey(int c) const
     {
         return !m_modifiers && m_key == c;
     }
 
+    bool isBackspace() const
+    {
+        return m_key == Key_Backspace || isControl('h');
+    }
+
+    bool isReturn() const
+    {
+        return m_key == Key_Return;
+    }
+
+    bool isEscape() const
+    {
+        return isKey(Key_Escape) || isKey(27) || isControl('c')
+            || isControl(Key_BracketLeft);
+    }
+
     bool is(int c) const
     {
         return m_xkey == c && (m_modifiers == 0 || m_modifiers == Qt::ShiftModifier);
@@ -328,13 +399,26 @@ public:
 
     bool operator==(const Input &a) const
     {
-        return a.m_key == m_key && m_text == a.m_text;
+        return a.m_key == m_key && a.m_modifiers == m_modifiers
+            && m_text == a.m_text;
     }
 
+    bool operator!=(const Input &a) const { return !operator==(a); }
+
     QString text() const { return m_text; }
 
+    QChar asChar() const
+    {
+        return (m_text.size() == 1 ? m_text.at(0) : QChar());
+    }
+
     int key() const { return m_key; }
 
+    QDebug dump(QDebug ts) const
+    {
+        return ts << m_key << '-' << m_modifiers << '-'
+            << quoteUnprintable(m_text);
+    }
 private:
     int m_key;
     int m_xkey;
@@ -342,10 +426,68 @@ private:
     QString m_text;
 };
 
-typedef QVector<Input> Inputs;
+QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
+
+class Inputs : public QVector<Input>
+{
+public:
+    Inputs() {}
+    explicit Inputs(const QString &str) { parseFrom(str); }
+    void parseFrom(const QString &str);
+};
+
+
+void Inputs::parseFrom(const QString &str)
+{
+    const int n = str.size();
+    for (int i = 0; i < n; ++i) {
+        uint c0 = str.at(i).unicode(), c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0;
+        if (i + 1 < n)
+            c1 = str.at(i + 1).unicode();
+        if (i + 2 < n)
+            c2 = str.at(i + 2).unicode();
+        if (i + 3 < n)
+            c3 = str.at(i + 3).unicode();
+        if (i + 4 < n)
+            c4 = str.at(i + 4).unicode();
+        if (i + 5 < n)
+            c5 = str.at(i + 5).unicode();
+        if (c0 == '<') {
+            if ((c1 == 'C' || c1 == 'c') && c2 == '-' && c4 == '>') {
+                uint c = (c3 < 90 ? c3 : c3 - 32);
+                append(Input(c, Qt::ControlModifier, QString(QChar(c - 64))));
+                i += 4;
+            } else {
+                append(Input(QLatin1Char(c0)));
+            }
+        } else {
+            append(Input(QLatin1Char(c0)));
+        }
+    }
+}
+
+class History
+{
+public:
+    History() : m_index(0) {}
+    void append(const QString &item)
+        { //qDebug() << "APP: " << item << m_items;
+            m_items.removeAll(item);
+            m_items.append(item); m_index = m_items.size() - 1;  }
+    void down() { m_index = qMin(m_index + 1, m_items.size()); }
+    void up() { m_index = qMax(m_index - 1, 0); }
+    //void clear() { m_items.clear(); m_index = 0; }
+    void restart() { m_index = m_items.size(); }
+    QString current() const { return m_items.value(m_index, QString()); }
+    QStringList items() const { return m_items; }
+private:
+    QStringList m_items;
+    int m_index;
+};
+
 
 // Mappings for a specific mode.
-class ModeMapping : private QList<QPair<Inputs, Inputs> >
+class ModeMapping : public QList<QPair<Inputs, Inputs> >
 {
 public:
     ModeMapping() { test(); }
@@ -375,24 +517,24 @@ public:
             }
     }
 
-    // Returns 'false' if more input input is needed to decide whether a
-    // mapping needs to be applied. If a decision can be made, return 'true',
+    // Returns 'false' if more input is needed to decide whether a mapping
+    // needs to be applied. If a decision can be made, return 'true',
     // and replace *input with the mapped data.
-    bool mappingDone(Inputs *input) const
+    bool mappingDone(Inputs *inputs) const
     {
-        Q_UNUSED(input);
         // FIXME: inefficient.
         for (int i = 0; i != size(); ++i) {
+            const Inputs &haystack = at(i).first;
             // A mapping
-            if (startsWith(at(i).first, *input)) {
-                if (at(i).first.size() != input->size())
+            if (startsWith(haystack, *inputs)) {
+                if (haystack.size() != inputs->size())
                     return false; // This can be extended.
                 // Actual mapping.
-                *input = at(i).second;
+                *inputs = at(i).second;
                 return true;
             }
         }
-        // No extensible mapping found. Use input as-is.
+        // No extensible mapping found. Use inputs as-is.
         return true;
     }
 
@@ -403,13 +545,14 @@ private:
         if (needle.size() > haystack.size())
             return false;
         for (int i = 0; i != needle.size(); ++i) {
-            if (needle.at(i).text() != haystack.at(i).text())
+            if (needle.at(i) != haystack.at(i))
                 return false;
         }
         return true;
     }
 };
 
+
 class FakeVimHandler::Private : public QObject
 {
     Q_OBJECT
@@ -422,7 +565,7 @@ public:
     void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
     void handleExCommand(const QString &cmd);
 
-    // updates marks positions by the difference in positionChange
+    // Updates marks positions by the difference in positionChange.
     void fixMarks(int positionAction, int positionChange);
 
     void installEventFilter();
@@ -436,14 +579,16 @@ public:
     EventResult handleKey(const Input &);
     Q_SLOT EventResult handleKey2();
     EventResult handleInsertMode(const Input &);
+    EventResult handleReplaceMode(const Input &);
     EventResult handleCommandMode(const Input &);
     EventResult handleRegisterMode(const Input &);
-    EventResult handleMiniBufferModes(const Input &);
+    EventResult handleExMode(const Input &);
+    EventResult handleSearchSubSubMode(const Input &);
     EventResult handleCommandSubSubMode(const Input &);
     void finishMovement(const QString &dotCommand = QString());
     void finishMovement(const QString &dotCommand, int count);
     void resetCommandMode();
-    void search(const QString &needle, bool forward, bool incSearch = false);
+    void search(const SearchData &sd);
     void highlightMatches(const QString &needle);
     void stopIncrementalFind();
 
@@ -455,18 +600,18 @@ public:
     bool atEndOfLine() const
         { return m_tc.atBlockEnd() && m_tc.block().length() > 1; }
 
-    int lastPositionInDocument() const; // last valid pos in doc
+    int lastPositionInDocument() const; // Returns last valid position in doc.
     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
+    void setLineContents(int line, const QString &contents); // 1 based line
 
     int linesOnScreen() const;
     int columnsOnScreen() const;
     int linesInDocument() const;
 
-    // all zero-based counting
+    // The following use all zero-based counting.
     int cursorLineOnScreen() const;
     int cursorLineInDocument() const;
     int physicalCursorColumnInDocument() const; // as stored in the data
@@ -482,7 +627,7 @@ public:
     void setCursorPosition(const CursorPosition &p)
         { setPosition(p.position); scrollToLineInDocument(p.scrollLine); }
 
-    // helper functions for indenting
+    // Helper functions for indenting/
     bool isElectricCharacter(QChar c) const;
     void indentSelectedText(QChar lastTyped = QChar());
     int indentText(const Range &range, QChar lastTyped = QChar());
@@ -500,7 +645,7 @@ public:
     void moveToMatchingParanthesis();
     void moveToWordBoundary(bool simple, bool forward, bool changeWord = false);
 
-    // to reduce line noise
+    // Convenience wrappers to reduce line noise.
     void moveToEndOfDocument() { m_tc.movePosition(EndOfDocument, MoveAnchor); }
     void moveToStartOfLine();
     void moveToEndOfLine();
@@ -515,11 +660,11 @@ public:
 
     bool handleFfTt(QString key);
 
-    // helper function for handleExCommand. return 1 based line index.
+    // Helper function for handleExCommand returning 1 based line index.
     int readLineCode(QString &cmd);
-    void selectRange(int beginLine, int endLine);
 
     void enterInsertMode();
+    void enterReplaceMode();
     void enterCommandMode();
     void enterExMode();
     void showRedMessage(const QString &msg);
@@ -527,20 +672,17 @@ public:
     void notImplementedYet();
     void updateMiniBuffer();
     void updateSelection();
+    void updateCursor();
     QWidget *editor() const;
     QChar characterAtCursor() const
         { return m_tc.document()->characterAt(m_tc.position()); }
     void beginEditBlock() { UNDO_DEBUG("BEGIN EDIT BLOCK"); m_tc.beginEditBlock(); }
     void beginEditBlock(int pos) { setUndoPosition(pos); beginEditBlock(); }
     void endEditBlock() { UNDO_DEBUG("END EDIT BLOCK"); m_tc.endEditBlock(); }
-    void joinPreviousEditBlock() { UNDO_DEBUG("JOIN EDIT BLOCK"); m_tc.joinPreviousEditBlock(); }
-
-    // this asks the layer above (e.g. the fake vim plugin or the
-    // stand-alone test application to handle the command)
-    void passUnknownExCommand(const QString &cmd);
-    // this asks the layer above (e.g. the fake vim plugin or the
-    // stand-alone test application to handle the set command)
-    void passUnknownSetCommand(const QString &cmd);
+    void joinPreviousEditBlock() { UNDO_DEBUG("JOIN"); m_tc.joinPreviousEditBlock(); }
+    void breakEditBlock()
+        { m_tc.beginEditBlock(); m_tc.insertText("x");
+          m_tc.deletePreviousChar(); m_tc.endEditBlock(); }
 
     bool isVisualMode() const { return m_visualMode != NoVisualMode; }
     bool isNoVisualMode() const { return m_visualMode == NoVisualMode; }
@@ -556,6 +698,8 @@ public:
     void selectBlockTextObject(bool inner, char left, char right);
     void selectQuotedStringTextObject(bool inner, int type);
 
+    Q_SLOT void importSelection();
+
 public:
     QTextEdit *m_textedit;
     QPlainTextEdit *m_plaintextedit;
@@ -570,7 +714,6 @@ public:
     QTextCursor m_tc;
     int m_oldPosition; // copy from last event to check for external changes
     int m_anchor;
-    static QHash<int, Register> m_registers;
     int m_register;
     QString m_mvcount;
     QString m_opcount;
@@ -582,10 +725,9 @@ public:
     bool m_anchorPastEnd;
     bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
 
-    bool isSearchMode() const
-        { return m_mode == SearchForwardMode || m_mode == SearchBackwardMode; }
     int m_gflag;  // whether current command started with 'g'
 
+    QString m_commandPrefix;
     QString m_commandBuffer;
     QString m_currentFileName;
     QString m_currentMessage;
@@ -593,34 +735,44 @@ public:
     bool m_lastSearchForward;
     bool m_findPending;
     QString m_lastInsertion;
+    QString m_lastDeletion;
 
     int anchor() const { return m_anchor; }
     int position() const { return m_tc.position(); }
 
-    typedef void (FakeVimHandler::Private::*Transformation)(int, QTextCursor *);
-    void transformText(const Range &range, Transformation transformation);
-
-    void removeSelectedText();
+    struct TransformationData
+    {
+        TransformationData(const QString &s, const QVariant &d)
+            : from(s), extraData(d) {}
+        QString from;
+        QString to;
+        QVariant extraData;
+    };
+    typedef void (Private::*Transformation)(TransformationData *td);
+    void transformText(const Range &range, Transformation transformation,
+        const QVariant &extraData = QVariant());
+
+    void insertText(const Register &reg);
     void removeText(const Range &range);
-    void removeTransform(int, QTextCursor *);
+    void removeTransform(TransformationData *td);
 
-    void invertCaseSelectedText();
-    void invertCaseTransform(int, QTextCursor *);
+    void invertCase(const Range &range);
+    void invertCaseTransform(TransformationData *td);
 
-    void upCaseSelectedText();
-    void upCaseTransform(int, QTextCursor *);
+    void upCase(const Range &range);
+    void upCaseTransform(TransformationData *td);
 
-    void downCaseSelectedText();
-    void downCaseTransform(int, QTextCursor *);
+    void downCase(const Range &range);
+    void downCaseTransform(TransformationData *td);
 
-    QChar m_replacingCharacter;
-    void replaceSelectedText(); // replace each character with m_replacingCharacter
-    void replaceTransform(int, QTextCursor *);
+    void replaceText(const Range &range, const QString &str);
+    void replaceByStringTransform(TransformationData *td);
+    void replaceByCharTransform(TransformationData *td);
 
-    QString selectedText() const { return text(Range(position(), anchor())); }
-    QString text(const Range &range) const;
+    QString selectText(const Range &range) const;
+    void setCurrentRange(const Range &range);
+    Range currentRange() const { return Range(position(), anchor(), m_rangemode); }
 
-    void yankSelectedText();
     void yankText(const Range &range, int toregister = '"');
 
     void pasteText(bool afterCursor);
@@ -630,37 +782,26 @@ public:
     void redo();
     void setUndoPosition(int pos);
     QMap<int, int> m_undoCursorPosition; // revision -> position
-    bool m_beginEditBlock;
 
     // extra data for '.'
     void replay(const QString &text, int count);
-    void setDotCommand(const QString &cmd) { m_dotCommand = cmd; }
-    void setDotCommand(const QString &cmd, int n) { m_dotCommand = cmd.arg(n); }
-    QString m_dotCommand;
-    bool m_inReplay; // true if we are executing a '.'
+    void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
+    void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
 
     // extra data for ';'
     QString m_semicolonCount;
     Input m_semicolonType;  // 'f', 'F', 't', 'T'
     QString m_semicolonKey;
 
-    // history for '/'
-    QString lastSearchString() const;
-    static QStringList m_searchHistory;
-    int m_searchHistoryIndex;
-
-    // history for ':'
-    static QStringList m_commandHistory;
-    int m_commandHistoryIndex;
-
     // visual line mode
     void enterVisualMode(VisualMode visualMode);
     void leaveVisualMode();
     VisualMode m_visualMode;
 
     // marks as lines
+    int mark(int code) const;
+    void setMark(int code, int position);
     QHash<int, int> m_marks;
-    QString m_oldNeedle;
 
     // vi style configuration
     QVariant config(int code) const { return theFakeVimSetting(code)->value(); }
@@ -670,7 +811,6 @@ public:
 
     int m_targetColumn; // -1 if past end of line
     int m_visualTargetColumn; // 'l' can move past eol in visual mode only
-
     int m_cursorWidth;
 
     // auto-indent
@@ -683,46 +823,67 @@ public:
     void handleStartOfLine();
 
     void recordJump();
-    void recordNewUndo();
     QVector<CursorPosition> m_jumpListUndo;
     QVector<CursorPosition> m_jumpListRedo;
 
     QList<QTextEdit::ExtraSelection> m_searchSelections;
+    QTextCursor m_searchCursor;
+    QString m_oldNeedle;
 
-    bool handleExCommandHelper(const QString &cmd); // Returns success.
-    QString extractCommand(const QString &line, int *beginLine, int *endLine);
-    bool handleExBangCommand(const QString &line);
-    bool handleExDeleteCommand(const QString &line);
-    bool handleExGotoCommand(const QString &line);
-    bool handleExHistoryCommand(const QString &line);
-    bool handleExMapCommand(const QString &line);
-    bool handleExNormalCommand(const QString &line);
-    bool handleExReadCommand(const QString &line);
-    bool handleExRedoCommand(const QString &line);
-    bool handleExSetCommand(const QString &line);
-    bool handleExShiftRightCommand(const QString &line);
-    bool handleExSourceCommand(const QString &line);
-    bool handleExSubstituteCommand(const QString &line);
-    bool handleExWriteCommand(const QString &line);
-
-    // All mappings.
-    typedef QHash<char, ModeMapping> Mappings;
-    static Mappings m_mappings;
-
-    QVector<Input> m_pendingInput;
+    bool handleExCommandHelper(const ExCommand &cmd); // Returns success.
+    bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
+    bool handleExBangCommand(const ExCommand &cmd);
+    bool handleExDeleteCommand(const ExCommand &cmd);
+    bool handleExGotoCommand(const ExCommand &cmd);
+    bool handleExHistoryCommand(const ExCommand &cmd);
+    bool handleExRegisterCommand(const ExCommand &cmd);
+    bool handleExMapCommand(const ExCommand &cmd);
+    bool handleExNormalCommand(const ExCommand &cmd);
+    bool handleExReadCommand(const ExCommand &cmd);
+    bool handleExRedoCommand(const ExCommand &cmd);
+    bool handleExSetCommand(const ExCommand &cmd);
+    bool handleExShiftCommand(const ExCommand &cmd);
+    bool handleExSourceCommand(const ExCommand &cmd);
+    bool handleExSubstituteCommand(const ExCommand &cmd);
+    bool handleExWriteCommand(const ExCommand &cmd);
 
     void timerEvent(QTimerEvent *ev);
-    int m_inputTimer;
 
     void setupCharClass();
     int charClass(QChar c, bool simple) const;
     signed char m_charClass[256];
+
+    static struct GlobalData
+    {
+        GlobalData()
+        {
+            inReplay = false;
+            inputTimer = -1;
+        }
+
+        // Input.
+        Inputs pendingInput;
+        int inputTimer;
+
+        // Repetition.
+        QString dotCommand;
+        bool inReplay; // true if we are executing a '.'
+
+        // History for searches.
+        History searchHistory;
+
+        // History for :ex commands.
+        History commandHistory;
+
+        QHash<int, Register> registers;
+
+        // All mappings.
+        typedef QHash<char, ModeMapping> Mappings;
+        Mappings mappings;
+    } g;
 };
 
-QStringList FakeVimHandler::Private::m_searchHistory;
-QStringList FakeVimHandler::Private::m_commandHistory;
-QHash<int, Register> FakeVimHandler::Private::m_registers;
-FakeVimHandler::Private::Mappings FakeVimHandler::Private::m_mappings;
+FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
 
 FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
 {
@@ -752,11 +913,8 @@ void FakeVimHandler::Private::init()
     m_movetype = MoveInclusive;
     m_anchor = 0;
     m_cursorWidth = EDITOR(cursorWidth());
-    m_inReplay = false;
     m_justAutoIndented = 0;
     m_rangemode = RangeCharMode;
-    m_beginEditBlock = true;
-    m_inputTimer = -1;
 
     setupCharClass();
 }
@@ -768,6 +926,8 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
     KEY_DEBUG("SHORTCUT OVERRIDE" << key << "  PASSING: " << m_passing);
 
     if (key == Key_Escape) {
+        if (m_subsubmode == SearchSubSubMode)
+            return true;
         // Not sure this feels good. People often hit Esc several times
         if (isNoVisualMode() && m_mode == CommandMode)
             return false;
@@ -830,7 +990,7 @@ EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
             // Try to compensate for code completion
             if (dist > 0 && dist <= physicalCursorColumnInDocument()) {
                 Range range(m_oldPosition, m_tc.position());
-                m_lastInsertion.append(text(range));
+                m_lastInsertion.append(selectText(range));
             }
         } else if (!isVisualMode()) {
             if (atEndOfLine())
@@ -851,8 +1011,8 @@ EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
     //    key = shift(key);
     //}
 
-    QTC_ASSERT(
-        !(m_mode != InsertMode && m_tc.atBlockEnd() && m_tc.block().length() > 1),
+    QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
+            || !m_tc.atBlockEnd() || m_tc.block().length() <= 1,
         qDebug() << "Cursor at EOL before key handler");
 
     EventResult result = handleKey(Input(key, mods, ev->text()));
@@ -862,8 +1022,8 @@ EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
         // We fake vi-style end-of-line behaviour
         m_fakeEnd = atEndOfLine() && m_mode == CommandMode && !isVisualBlockMode();
 
-        QTC_ASSERT(
-            !(m_mode != InsertMode && m_tc.atBlockEnd() && m_tc.block().length() > 1),
+        QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
+                || !m_tc.atBlockEnd() || m_tc.block().length() <= 1,
             qDebug() << "Cursor at EOL after key handler");
 
         if (m_fakeEnd)
@@ -872,43 +1032,65 @@ EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
         EDITOR(setTextCursor(m_tc));
         m_oldPosition = m_tc.position();
     }
+
+    if (hasConfig(ConfigShowMarks))
+        updateSelection();
+
     return result;
 }
 
 void FakeVimHandler::Private::installEventFilter()
 {
+    EDITOR(viewport()->installEventFilter(q));
     EDITOR(installEventFilter(q));
 }
 
 void FakeVimHandler::Private::setupWidget()
 {
     enterCommandMode();
-    //EDITOR(setCursorWidth(QFontMetrics(ed->font()).width(QChar('x')));
     if (m_textedit) {
         m_textedit->setLineWrapMode(QTextEdit::NoWrap);
     } else if (m_plaintextedit) {
         m_plaintextedit->setLineWrapMode(QPlainTextEdit::NoWrap);
     }
     m_wasReadOnly = EDITOR(isReadOnly());
-    //EDITOR(setReadOnly(true));
 
     updateEditor();
+    importSelection();
+    updateMiniBuffer();
+    updateCursor();
+}
 
+void FakeVimHandler::Private::importSelection()
+{
     QTextCursor tc = EDITOR(textCursor());
+    int pos = tc.position();
+    int anc = tc.anchor();
     if (tc.hasSelection()) {
-        int pos = tc.position();
-        int anc = tc.anchor();
-        m_marks['<'] = anc;
-        m_marks['>'] = pos;
-        m_anchor = anc;
-        m_visualMode = VisualCharMode;
-        tc.clearSelection();
-        EDITOR(setTextCursor(tc));
-        m_tc = tc; // needed in updateSelection
-        updateSelection();
+        // FIXME: Why?
+        if (pos < anc)
+            --anc;
+        else
+            tc.movePosition(Left, KeepAnchor);
     }
-
-    updateMiniBuffer();
+    setMark('<', anc);
+    setMark('>', pos);
+    m_anchor = anc;
+    Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
+    if (!tc.hasSelection())
+        m_visualMode = NoVisualMode;
+    else if (mods & Qt::ControlModifier)
+        m_visualMode = VisualBlockMode;
+    else if (mods & Qt::AltModifier)
+        m_visualMode = VisualBlockMode;
+    else if (mods & Qt::ShiftModifier)
+        m_visualMode = VisualLineMode;
+    else
+        m_visualMode = VisualCharMode;
+    m_tc = tc; // needed in updateSelection
+    tc.clearSelection();
+    EDITOR(setTextCursor(tc));
+    updateSelection();
 }
 
 void FakeVimHandler::Private::updateEditor()
@@ -924,45 +1106,50 @@ void FakeVimHandler::Private::restoreWidget(int tabSize)
     //showBlackMessage(QString());
     //updateMiniBuffer();
     //EDITOR(removeEventFilter(q));
-    EDITOR(setReadOnly(m_wasReadOnly));
-    EDITOR(setCursorWidth(m_cursorWidth));
-    EDITOR(setOverwriteMode(false));
+    //EDITOR(setReadOnly(m_wasReadOnly));
     const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
     EDITOR(setTabStopWidth(charWidth * tabSize));
 
     if (isVisualLineMode()) {
         m_tc = EDITOR(textCursor());
-        int beginLine = lineForPosition(m_marks['<']);
-        int endLine = lineForPosition(m_marks['>']);
+        int beginLine = lineForPosition(mark('<'));
+        int endLine = lineForPosition(mark('>'));
         m_tc.setPosition(firstPositionInLine(beginLine), MoveAnchor);
         m_tc.setPosition(lastPositionInLine(endLine), KeepAnchor);
         EDITOR(setTextCursor(m_tc));
-    } else if (isVisualCharMode()) {
+    } else if (isVisualCharMode() || isVisualBlockMode()) {
         m_tc = EDITOR(textCursor());
-        m_tc.setPosition(m_marks['<'], MoveAnchor);
-        m_tc.setPosition(m_marks['>'], KeepAnchor);
+        m_tc.setPosition(mark('<'), MoveAnchor);
+        m_tc.setPosition(mark('>'), KeepAnchor);
         EDITOR(setTextCursor(m_tc));
     }
 
     m_visualMode = NoVisualMode;
+    // Force "ordinary" cursor.
+    m_mode = InsertMode;
+    m_submode = NoSubMode;
+    m_subsubmode = NoSubSubMode;
+    updateCursor();
     updateSelection();
 }
 
 EventResult FakeVimHandler::Private::handleKey(const Input &input)
 {
-    if (m_mode == InsertMode || m_mode == CommandMode) {
-        m_pendingInput.append(input);
+    KEY_DEBUG("HANDLE INPUT: " << input);
+    if (m_mode == ExMode)
+        return handleExMode(input);
+    if (m_subsubmode == SearchSubSubMode)
+        return handleSearchSubSubMode(input);
+    if (m_mode == InsertMode || m_mode == ReplaceMode || m_mode == CommandMode) {
+        g.pendingInput.append(input);
         const char code = m_mode == InsertMode ? 'i' : 'n';
-        if (m_mappings[code].mappingDone(&m_pendingInput))
+        if (g.mappings[code].mappingDone(&g.pendingInput))
             return handleKey2();
-        if (m_inputTimer != -1)
-            killTimer(m_inputTimer);
-        m_inputTimer = startTimer(1000);
+        if (g.inputTimer != -1)
+            killTimer(g.inputTimer);
+        g.inputTimer = startTimer(1000);
         return EventHandled;
     }
-    if (m_mode == ExMode || m_mode == SearchForwardMode
-            || m_mode == SearchBackwardMode)
-        return handleMiniBufferModes(input);
     return EventUnhandled;
 }
 
@@ -971,22 +1158,32 @@ EventResult FakeVimHandler::Private::handleKey2()
     setUndoPosition(m_tc.position());
     if (m_mode == InsertMode) {
         EventResult result = EventUnhandled;
-        foreach (const Input &in, m_pendingInput) {
+        foreach (const Input &in, g.pendingInput) {
             EventResult r = handleInsertMode(in);
             if (r == EventHandled)
                 result = EventHandled;
         }
-        m_pendingInput.clear();
+        g.pendingInput.clear();
+        return result;
+    }
+    if (m_mode == ReplaceMode) {
+        EventResult result = EventUnhandled;
+        foreach (const Input &in, g.pendingInput) {
+            EventResult r = handleReplaceMode(in);
+            if (r == EventHandled)
+                result = EventHandled;
+        }
+        g.pendingInput.clear();
         return result;
     }
     if (m_mode == CommandMode) {
         EventResult result = EventUnhandled;
-        foreach (const Input &in, m_pendingInput) {
+        foreach (const Input &in, g.pendingInput) {
             EventResult r = handleCommandMode(in);
             if (r == EventHandled)
                 result = EventHandled;
         }
-        m_pendingInput.clear();
+        g.pendingInput.clear();
         return result;
     }
     return EventUnhandled;
@@ -1020,7 +1217,7 @@ void FakeVimHandler::Private::setAnchor()
     if (!isVisualMode()) {
         m_anchor = m_tc.position();
     } else {
-    //    m_marks['<'] = m_tc.position();
+    //    setMark('<', m_tc.position());
     }
 }
 
@@ -1085,14 +1282,14 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         enterExMode();
         m_currentMessage.clear();
         m_commandBuffer = QString(".,+%1!").arg(qAbs(endLine - beginLine));
-        m_commandHistory.append(QString());
-        m_commandHistoryIndex = m_commandHistory.size() - 1;
+        //g.commandHistory.append(QString());
         updateMiniBuffer();
+        updateCursor();
         return;
     }
 
     if (isVisualMode())
-        m_marks['>'] = m_tc.position();
+        setMark('>', m_tc.position());
 
     if (m_submode == ChangeSubMode
         || m_submode == DeleteSubMode
@@ -1107,7 +1304,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
 
         if (m_movetype == MoveInclusive) {
             if (anchor() <= position()) {
-                if ( !m_tc.atBlockEnd())
+                if (!m_tc.atBlockEnd())
                     moveRight(); // correction
             } else {
                 m_anchor++;
@@ -1124,9 +1321,9 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         }
 
         if (m_submode != TransformSubMode) {
-            yankSelectedText();
+            yankText(currentRange(), m_register);
             if (m_movetype == MoveLineWise)
-                m_registers[m_register].rangemode = RangeLineMode;
+                g.registers[m_register].rangemode = RangeLineMode;
         }
 
         m_positionPastEnd = m_anchorPastEnd = false;
@@ -1135,17 +1332,17 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
     if (m_submode == ChangeSubMode) {
         if (m_rangemode == RangeLineMode)
             m_rangemode = RangeLineModeExclusive;
-        removeSelectedText();
+        removeText(currentRange());
         if (!dotCommand.isEmpty())
             setDotCommand(QLatin1Char('c') + dotCommand);
         if (m_movetype == MoveLineWise)
             insertAutomaticIndentation(true);
         endEditBlock();
+        setUndoPosition(position());
         enterInsertMode();
-        m_beginEditBlock = false;
         m_submode = NoSubMode;
     } else if (m_submode == DeleteSubMode) {
-        removeSelectedText();
+        removeText(currentRange());
         if (!dotCommand.isEmpty())
             setDotCommand(QLatin1Char('d') + dotCommand);
         if (m_movetype == MoveLineWise)
@@ -1161,7 +1358,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         const int la = lineForPosition(anchor());
         const int lp = lineForPosition(position());
         if (m_register != '"') {
-            setPosition(m_marks[m_register]);
+            setPosition(mark(m_register));
             moveToStartOfLine();
         } else {
             if (anchor() <= position())
@@ -1171,21 +1368,17 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
             showBlackMessage(QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
     } else if (m_submode == TransformSubMode) {
         if (m_subsubmode == InvertCaseSubSubMode) {
-            invertCaseSelectedText();
+            invertCase(currentRange());
             if (!dotCommand.isEmpty())
                 setDotCommand(QLatin1Char('~') + dotCommand);
         } else if (m_subsubmode == UpCaseSubSubMode) {
-            upCaseSelectedText();
+            upCase(currentRange());
             if (!dotCommand.isEmpty())
                 setDotCommand("gU" + dotCommand);
         } else if (m_subsubmode == DownCaseSubSubMode) {
-            downCaseSelectedText();
+            downCase(currentRange());
             if (!dotCommand.isEmpty())
                 setDotCommand("gu" + dotCommand);
-        } else if (m_subsubmode == ReplaceSubSubMode) {
-            replaceSelectedText();
-            if (!dotCommand.isEmpty())
-                setDotCommand("r" + dotCommand);
         }
         m_submode = NoSubMode;
         m_subsubmode = NoSubSubMode;
@@ -1193,28 +1386,26 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
         if (m_movetype == MoveLineWise)
             handleStartOfLine();
         endEditBlock();
-    } else if (m_submode == ReplaceSubMode) {
-        m_submode = NoSubMode;
     } else if (m_submode == IndentSubMode) {
         recordJump();
         beginEditBlock();
         indentSelectedText();
         endEditBlock();
         m_submode = NoSubMode;
-        updateMiniBuffer();
     } else if (m_submode == ShiftRightSubMode) {
         recordJump();
         shiftRegionRight(1);
         m_submode = NoSubMode;
-        updateMiniBuffer();
     } else if (m_submode == ShiftLeftSubMode) {
         recordJump();
         shiftRegionLeft(1);
         m_submode = NoSubMode;
-        updateMiniBuffer();
     }
 
     resetCommandMode();
+    updateSelection();
+    updateMiniBuffer();
+    updateCursor();
 }
 
 void FakeVimHandler::Private::resetCommandMode()
@@ -1226,14 +1417,19 @@ void FakeVimHandler::Private::resetCommandMode()
     m_register = '"';
     m_tc.clearSelection();
     m_rangemode = RangeCharMode;
-
-    updateSelection();
-    updateMiniBuffer();
 }
 
 void FakeVimHandler::Private::updateSelection()
 {
     QList<QTextEdit::ExtraSelection> selections = m_searchSelections;
+    if (!m_searchCursor.isNull()) {
+        QTextEdit::ExtraSelection sel;
+        sel.cursor = m_searchCursor;
+        sel.format = m_searchCursor.blockCharFormat();
+        sel.format.setForeground(Qt::white);
+        sel.format.setBackground(Qt::black);
+        selections.append(sel);
+    }
     if (isVisualMode()) {
         QTextEdit::ExtraSelection sel;
         sel.cursor = m_tc;
@@ -1246,7 +1442,7 @@ void FakeVimHandler::Private::updateSelection()
         sel.format.setBackground(Qt::black);
 #endif
         const int cursorPos = m_tc.position();
-        const int anchorPos = m_marks['<'];
+        const int anchorPos = mark('<');
         //qDebug() << "POS: " << cursorPos << " ANCHOR: " << anchorPos;
         if (isVisualCharMode()) {
             sel.cursor.setPosition(qMin(cursorPos, anchorPos), MoveAnchor);
@@ -1287,6 +1483,19 @@ void FakeVimHandler::Private::updateSelection()
         }
     }
     //qDebug() << "SELECTION: " << selections;
+    if (hasConfig(ConfigShowMarks)) {
+        for (QHashIterator<int, int> it(m_marks); it.hasNext(); ) {
+            it.next();
+            QTextEdit::ExtraSelection sel;
+            sel.cursor = m_tc;
+            sel.cursor.setPosition(it.value(), MoveAnchor);
+            sel.cursor.setPosition(it.value() + 1, KeepAnchor);
+            sel.format = m_tc.blockCharFormat();
+            sel.format.setForeground(Qt::blue);
+            sel.format.setBackground(Qt::green);
+            selections.append(sel);
+        }
+    }
     emit q->selectionChanged(selections);
 }
 
@@ -1309,17 +1518,13 @@ void FakeVimHandler::Private::updateMiniBuffer()
             msg = "-- VISUAL BLOCK --";
         }
     } else if (m_mode == InsertMode) {
-        if (m_submode == ReplaceSubMode)
-            msg = "-- REPLACE --";
-        else
-            msg = "-- INSERT --";
-    } else {
-        if (m_mode == SearchForwardMode)
-            msg += '/';
-        else if (m_mode == SearchBackwardMode)
-            msg += '?';
-        else if (m_mode == ExMode)
-            msg += ':';
+        msg = "-- INSERT --";
+    } else if (m_mode == ReplaceMode) {
+        msg = "-- REPLACE --";
+    } else if (!m_commandPrefix.isEmpty()) {
+        //QTC_ASSERT(m_mode == ExMode || m_subsubmode == SearchSubSubMode,
+        //    qDebug() << "MODE: " << m_mode << m_subsubmode);
+        msg = m_commandPrefix;
         foreach (QChar c, m_commandBuffer) {
             if (c.unicode() < 32) {
                 msg += '^';
@@ -1330,6 +1535,9 @@ void FakeVimHandler::Private::updateMiniBuffer()
         }
         if (!msg.isEmpty() && m_mode != CommandMode)
             msg += QChar(10073); // '|'; // FIXME: Use a real "cursor"
+    } else {
+        QTC_ASSERT(m_mode == CommandMode && m_subsubmode != SearchSubSubMode, /**/);
+        msg = "-- COMMAND --";
     }
 
     emit q->commandBufferChanged(msg);
@@ -1337,7 +1545,8 @@ void FakeVimHandler::Private::updateMiniBuffer()
     int linesInDoc = linesInDocument();
     int l = cursorLineInDocument();
     QString status;
-    const QString pos = QString::fromLatin1("%1,%2").arg(l + 1).arg(physicalCursorColumnInDocument() + 1);
+    const QString pos = QString::fromLatin1("%1,%2")
+        .arg(l + 1).arg(physicalCursorColumnInDocument() + 1);
     // FIXME: physical "-" logical
     if (linesInDoc != 0) {
         status = FakeVimHandler::tr("%1%2%").arg(pos, -10).arg(l * 100 / linesInDoc, 4);
@@ -1422,28 +1631,19 @@ EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
             selectQuotedStringTextObject(m_subsubdata.is('i'), input.key());
         m_subsubmode = NoSubSubMode;
         finishMovement();
-    } else if (m_submode == TransformSubMode && m_subsubmode == ReplaceSubSubMode) {
-        if (isVisualLineMode())
-            m_rangemode = RangeLineMode;
-        else if (isVisualBlockMode())
-            m_rangemode = RangeBlockMode;
-        if (!input.text().isEmpty() && input.text().at(0).isPrint()) {
-            leaveVisualMode();
-            m_replacingCharacter = input.text().at(0);
-            finishMovement();
-        }
     } else if (m_subsubmode == MarkSubSubMode) {
-        m_marks[input.key()] = m_tc.position();
+        setMark(input.asChar().unicode(), m_tc.position());
         m_subsubmode = NoSubSubMode;
     } else if (m_subsubmode == BackTickSubSubMode
             || m_subsubmode == TickSubSubMode) {
-        if (m_marks.contains(input.key())) {
-            setPosition(m_marks[input.key()]);
+        int m = mark(input.asChar().unicode());
+        if (m != -1) {
+            setPosition(m);
             if (m_subsubmode == TickSubSubMode)
                 moveToFirstNonBlankOnLine();
             finishMovement();
         } else {
-            showRedMessage(msgE20MarkNotSet(input.text()));
+            showRedMessage(msgMarkNotSet(input.text()));
         }
         m_subsubmode = NoSubSubMode;
     } else {
@@ -1456,7 +1656,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
 {
     EventResult handled = EventHandled;
 
-    if (input.isKey(Key_Escape) || input.isControl(Key_BracketLeft)) {
+    if (input.isEscape()) {
         if (isVisualMode()) {
             leaveVisualMode();
         } else if (m_submode != NoSubMode) {
@@ -1465,6 +1665,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             finishMovement();
         } else {
             resetCommandMode();
+            updateSelection();
+            updateMiniBuffer();
         }
     } else if (m_subsubmode != NoSubSubMode) {
         handleCommandSubSubMode(input);
@@ -1472,9 +1674,33 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         emit q->windowCommandRequested(input.key());
         m_submode = NoSubMode;
     } else if (m_submode == RegisterSubMode) {
-        m_register = input.key();
+        m_register = input.asChar().unicode();
         m_submode = NoSubMode;
         m_rangemode = RangeLineMode;
+    } else if (m_submode == ReplaceSubMode) {
+        if (isVisualMode()) {
+            if (isVisualLineMode())
+                m_rangemode = RangeLineMode;
+            else if (isVisualBlockMode())
+                m_rangemode = RangeBlockMode;
+            else
+                m_rangemode = RangeCharMode;
+            leaveVisualMode();
+            Range range = currentRange();
+            Transformation tr =
+                &FakeVimHandler::Private::replaceByCharTransform;
+            transformText(range, tr, input.asChar());
+            setPosition(range.beginPos);
+        } else if (count() <= rightDist()) {
+            setAnchor();
+            moveRight(count());
+            replaceText(currentRange(), QString(count(), input.asChar()));
+            moveLeft();
+            setTargetColumn();
+            setDotCommand("%1r" + input.text(), count());
+        }
+        m_submode = NoSubMode;
+        finishMovement();
     } else if (m_submode == ChangeSubMode && input.is('c')) { // tested
         moveToStartOfLine();
         setAnchor();
@@ -1519,12 +1745,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         finishMovement();
     } else if (m_submode == ZSubMode) {
         //qDebug() << "Z_MODE " << cursorLineInDocument() << linesOnScreen();
-        if (input.isKey(Key_Return) || input.is('t')) {
+        if (input.isReturn() || input.is('t')) {
             // Cursor line to top of window.
             if (!m_mvcount.isEmpty())
                 setPosition(firstPositionInLine(count()));
             scrollUp(- cursorLineOnScreen());
-            if (input.isKey(Key_Return))
+            if (input.isReturn())
                 moveToFirstNonBlankOnLine();
             finishMovement();
         } else if (input.is('.') || input.is('z')) {
@@ -1554,23 +1780,6 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             handleExCommand(QString(QLatin1Char('x')));
         else if (input.is('Q'))
             handleExCommand("q!");
-    } else if (m_submode == ReplaceSubMode) {
-        const QString text = input.text();
-        if (count() <= (rightDist() + atEndOfLine()) && text.size() == 1
-                && (text.at(0).isPrint() || text.at(0).isSpace())) {
-            if (atEndOfLine())
-                moveLeft();
-            setAnchor();
-            moveRight(count());
-            removeSelectedText();
-            m_tc.insertText(QString(count(), text.at(0)));
-            m_movetype = MoveExclusive;
-            setDotCommand("%1r" + text, count());
-            moveLeft();
-        }
-        setTargetColumn();
-        m_submode = NoSubMode;
-        finishMovement();
     } else if (input.isDigit()) {
         if (input.is('0') && m_mvcount.isEmpty()) {
             m_movetype = MoveExclusive;
@@ -1601,18 +1810,18 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         finishMovement();
     } else if (input.is(':')) {
         enterExMode();
+        g.commandHistory.restart();
         m_currentMessage.clear();
         m_commandBuffer.clear();
         if (isVisualMode())
             m_commandBuffer = "'<,'>";
-        m_commandHistory.append(QString());
-        m_commandHistoryIndex = m_commandHistory.size() - 1;
         updateMiniBuffer();
     } else if (input.is('/') || input.is('?')) {
+        m_lastSearchForward = input.is('/');
+        g.searchHistory.restart();
         if (hasConfig(ConfigUseCoreSearch)) {
             // re-use the core dialog.
             m_findPending = true;
-            m_lastSearchForward = (input.is('/'));
             EDITOR(setTextCursor(m_tc));
             emit q->findRequested(!m_lastSearchForward);
             m_tc = EDITOR(textCursor());
@@ -1620,25 +1829,35 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         } else {
             // FIXME: make core find dialog sufficiently flexible to
             // produce the "default vi" behaviour too. For now, roll our own.
-            enterExMode(); // to get the cursor disabled
             m_currentMessage.clear();
-            m_mode = (input.is('/')) ? SearchForwardMode : SearchBackwardMode;
-            m_commandBuffer.clear();
-            m_searchHistory.append(QString());
-            m_searchHistoryIndex = m_searchHistory.size() - 1;
+            m_movetype = MoveExclusive;
+            m_subsubmode = SearchSubSubMode;
+            m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
+            m_commandBuffer = QString();
+            updateCursor();
             updateMiniBuffer();
         }
     } else if (input.is('`')) {
         m_subsubmode = BackTickSubSubMode;
     } else if (input.is('#') || input.is('*')) {
         // FIXME: That's not proper vim behaviour
-        m_tc.select(QTextCursor::WordUnderCursor);
-        QString needle = "\\<" + m_tc.selection().toPlainText() + "\\>";
-        m_searchHistory.append(needle);
-        m_lastSearchForward = (input.is('*'));
-        updateMiniBuffer();
-        search(needle, m_lastSearchForward);
-        recordJump();
+        QTextCursor tc = m_tc;
+        tc.select(QTextCursor::WordUnderCursor);
+        QString needle = "\\<" + tc.selection().toPlainText() + "\\>";
+        g.searchHistory.append(needle);
+        m_lastSearchForward = input.is('*');
+        m_currentMessage.clear();
+        m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
+        m_commandBuffer = needle;
+        SearchData sd;
+        sd.needle = needle;
+        sd.forward = m_lastSearchForward;
+        sd.highlightCursor = false;
+        sd.highlightMatches = true;
+        search(sd);
+        //m_searchCursor = QTextCursor();
+        //updateSelection();
+        //updateMiniBuffer();
     } else if (input.is('\'')) {
         m_subsubmode = TickSubSubMode;
     } else if (input.is('|')) {
@@ -1652,12 +1871,11 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         enterExMode();
         m_currentMessage.clear();
         m_commandBuffer = "'<,'>!";
-        m_commandHistory.append(QString());
-        m_commandHistoryIndex = m_commandHistory.size() - 1;
+        //g.commandHistory.append(QString());
         updateMiniBuffer();
     } else if (input.is('"')) {
         m_submode = RegisterSubMode;
-    } else if (input.isKey(Key_Return)) {
+    } else if (input.isReturn()) {
         moveToStartOfLine();
         moveDown();
         moveToFirstNonBlankOnLine();
@@ -1693,12 +1911,13 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
     } else if (input.is(',')) {
         passShortcuts(true);
     } else if (input.is('.')) {
-        //qDebug() << "REPEATING" << quoteUnprintable(m_dotCommand) << count();
-        QString savedCommand = m_dotCommand;
-        m_dotCommand.clear();
+        //qDebug() << "REPEATING" << quoteUnprintable(g.dotCommand) << count()
+        //    << input;
+        QString savedCommand = g.dotCommand;
+        g.dotCommand.clear();
         replay(savedCommand, count());
         enterCommandMode();
-        m_dotCommand = savedCommand;
+        g.dotCommand = savedCommand;
     } else if (input.is('<') && isNoVisualMode()) {
         m_submode = ShiftLeftSubMode;
     } else if (input.is('<') && isVisualMode()) {
@@ -1722,12 +1941,16 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         finishMovement();
     } else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) {
         leaveVisualMode();
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
         m_lastInsertion.clear();
         if (!atEndOfLine())
             moveRight();
         updateMiniBuffer();
     } else if (input.is('A')) {
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
         moveBehindEndOfLine();
         setDotCommand(QString(QLatin1Char('A')));
@@ -1791,14 +2014,14 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
     } else if ((input.is('d') || input.is('x')) && isVisualLineMode()) {
         leaveVisualMode();
         m_rangemode = RangeLineMode;
-        yankSelectedText();
-        removeSelectedText();
+        yankText(currentRange(), m_register);
+        removeText(currentRange());
         handleStartOfLine();
     } else if ((input.is('d') || input.is('x')) && isVisualBlockMode()) {
         leaveVisualMode();
         m_rangemode = RangeBlockMode;
-        yankSelectedText();
-        removeSelectedText();
+        yankText(currentRange(), m_register);
+        removeText(currentRange());
         setPosition(qMin(position(), anchor()));
     } else if (input.is('D') && isNoVisualMode()) {
         if (atEndOfLine())
@@ -1815,14 +2038,14 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         leaveVisualMode();
         m_rangemode = RangeLineMode;
         m_submode = NoSubMode;
-        yankSelectedText();
-        removeSelectedText();
+        yankText(currentRange(), m_register);
+        removeText(currentRange());
         moveToFirstNonBlankOnLine();
     } else if ((input.is('D') || input.is('X')) && isVisualBlockMode()) {
         leaveVisualMode();
         m_rangemode = RangeBlockAndTailMode;
-        yankSelectedText();
-        removeSelectedText();
+        yankText(currentRange(), m_register);
+        removeText(currentRange());
         setPosition(qMin(position(), anchor()));
     } else if (input.isControl('d')) {
         int sline = cursorLineOnScreen();
@@ -1874,8 +2097,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             m_tc.setPosition(firstPositionInLine(n), KeepAnchor);
         }
         finishMovement(dotCommand);
-    } else if (input.is('h') || input.isKey(Key_Left)
-            || input.isKey(Key_Backspace) || input.isControl('h')) {
+    } else if (input.is('h') || input.isKey(Key_Left) || input.isBackspace()) {
         m_movetype = MoveExclusive;
         int n = qMin(count(), leftDist());
         if (m_fakeEnd && m_tc.block().length() > 1)
@@ -1890,6 +2112,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         finishMovement();
     } else if (!isVisualMode() && (input.is('i') || input.isKey(Key_Insert))) {
         setDotCommand(QString(QLatin1Char('i'))); // setDotCommand("%1i", count());
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
         updateMiniBuffer();
         if (atEndOfLine())
@@ -1909,6 +2133,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             m_gflag = false;
             m_tc.clearSelection();
         }
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
     } else if (input.isControl('i')) {
         if (!m_jumpListRedo.isEmpty()) {
@@ -1930,12 +2156,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
                 setAnchor();
                 moveRight();
                 if (m_gflag) {
-                    removeSelectedText();
+                    removeText(currentRange());
                 } else {
                     while (characterAtCursor() == ' '
                         || characterAtCursor() == '\t')
                         moveRight();
-                    removeSelectedText();
+                    removeText(currentRange());
                     m_tc.insertText(QString(QLatin1Char(' ')));
                 }
             }
@@ -1973,8 +2199,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         handleStartOfLine();
         finishMovement();
     } else if (input.is('n') || input.is('N')) {
-        search(lastSearchString(), m_lastSearchForward);
-        recordJump();
+        SearchData sd;
+        sd.needle = g.searchHistory.current();
+        sd.forward = input.is('n') ? m_lastSearchForward : !m_lastSearchForward;
+        sd.highlightCursor = false;
+        sd.highlightMatches = true;
+        search(sd);
     } else if (isVisualMode() && (input.is('o') || input.is('O'))) {
         int pos = position();
         setPosition(anchor());
@@ -1985,20 +2215,28 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         if (m_positionPastEnd)
             m_visualTargetColumn = -1;
         updateSelection();
-    } else if (input.is('o') || input.is('O')) {
-        beginEditBlock();
+    } else if (input.is('o')) {
         setDotCommand("%1o", count());
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
-        m_beginEditBlock = false;
+        beginEditBlock(position());
         moveToFirstNonBlankOnLine();
-        if (input.is('O'))
-            moveToStartOfLine();
-        else
-            moveBehindEndOfLine();
-        m_tc.insertText("\n");
-        if (input.is('O'))
-            moveUp();
-        insertAutomaticIndentation(input.is('o'));
+        moveBehindEndOfLine();
+        insertText(Register("\n"));
+        insertAutomaticIndentation(true);
+        endEditBlock();
+    } else if (input.is('O')) {
+        setDotCommand("%1O", count());
+        setUndoPosition(position());
+        breakEditBlock();
+        enterInsertMode();
+        beginEditBlock(position());
+        moveToFirstNonBlankOnLine();
+        moveToStartOfLine();
+        insertText(Register("\n"));
+        moveUp();
+        insertAutomaticIndentation(false);
         endEditBlock();
     } else if (input.isControl('o')) {
         if (!m_jumpListUndo.isEmpty()) {
@@ -2011,19 +2249,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         setTargetColumn();
         setDotCommand("%1p", count());
         finishMovement();
-    } else if (isVisualMode() && input.is('r')) {
-        m_submode = TransformSubMode;
-        m_subsubmode = ReplaceSubSubMode;
     } else if (input.is('r')) {
         m_submode = ReplaceSubMode;
-        setDotCommand(QString(QLatin1Char('r')));
     } else if (!isVisualMode() && input.is('R')) {
-        // FIXME: right now we repeat the insertion count() times,
-        // but not the deletion
-        m_lastInsertion.clear();
-        enterInsertMode();
-        m_submode = ReplaceSubMode;
-        setDotCommand(QString(QLatin1Char('R')));
+        setUndoPosition(position());
+        breakEditBlock();
+        enterReplaceMode();
         updateMiniBuffer();
     } else if (input.isControl('r')) {
         redo();
@@ -2033,11 +2264,13 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             moveLeft();
         setAnchor();
         moveRight(qMin(count(), rightDist()));
-        yankSelectedText();
-        removeSelectedText();
+        yankText(currentRange(), m_register);
+        removeText(currentRange());
         setDotCommand("%1s", count());
         m_opcount.clear();
         m_mvcount.clear();
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
     } else if (input.is('S')) {
         if (!isVisualMode()) {
@@ -2046,8 +2279,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             setPosition(lastPositionInLine(line + count() - 1));
         }
         setDotCommand("%1S", count());
+        setUndoPosition(position());
+        breakEditBlock();
         enterInsertMode();
-        m_beginEditBlock = false;
         m_submode = ChangeSubMode;
         m_movetype = MoveLineWise;
         finishMovement();
@@ -2116,8 +2350,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         if (leftDist() > 0) {
             setAnchor();
             moveLeft(qMin(count(), leftDist()));
-            yankSelectedText();
-            removeSelectedText();
+            yankText(currentRange(), m_register);
+            removeText(currentRange());
         }
         finishMovement();
     } else if ((m_submode == YankSubMode && input.is('y'))
@@ -2143,14 +2377,14 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
             || (input.is('Y') && isVisualLineMode())
             || (input.is('Y') && isVisualCharMode())) {
         m_rangemode = RangeLineMode;
-        yankSelectedText();
+        yankText(currentRange(), m_register);
         setPosition(qMin(position(), anchor()));
         moveToStartOfLine();
         leaveVisualMode();
         finishMovement();
     } else if ((input.is('y') || input.is('Y')) && isVisualBlockMode()) {
         m_rangemode = RangeBlockMode;
-        yankSelectedText();
+        yankText(currentRange(), m_register);
         setPosition(qMin(position(), anchor()));
         leaveVisualMode();
         finishMovement();
@@ -2159,18 +2393,19 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
     } else if (input.is('Z')) {
         m_submode = CapitalZSubMode;
     } else if (!m_gflag && input.is('~') && !isVisualMode()) {
+        m_movetype = MoveExclusive;
         if (!atEndOfLine()) {
             beginEditBlock();
             setAnchor();
             moveRight(qMin(count(), rightDist()));
             if (input.is('~')) {
-                invertCaseSelectedText();
+                invertCase(currentRange());
                 setDotCommand("%1~", count());
             } else if (input.is('u')) {
-                downCaseSelectedText();
+                downCase(currentRange());
                 setDotCommand("%1gu", count());
             } else if (input.is('U')) {
-                upCaseSelectedText();
+                upCase(currentRange());
                 setDotCommand("%1gU", count());
             }
             endEditBlock();
@@ -2180,6 +2415,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         || (m_gflag && input.is('u') && !isVisualMode())
         || (m_gflag && input.is('U') && !isVisualMode())) {
         m_gflag = false;
+        m_movetype = MoveExclusive;
         if (atEndOfLine())
             moveLeft();
         setAnchor();
@@ -2194,6 +2430,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
         || (m_gflag && input.is('u') && isVisualMode())
         || (m_gflag && input.is('U') && isVisualMode())) {
         m_gflag = false;
+        m_movetype = MoveExclusive;
         if (isVisualLineMode())
             m_rangemode = RangeLineMode;
         else if (isVisualBlockMode())
@@ -2220,7 +2457,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
     } else if (input.is(Key_Delete)) {
         setAnchor();
         moveRight(qMin(1, rightDist()));
-        removeSelectedText();
+        removeText(currentRange());
     } else if (input.is(Key_BracketLeft) || input.is(Key_BracketRight)) {
 
     } else if (input.isControl(Key_BracketRight)) {
@@ -2241,13 +2478,36 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
     return handled;
 }
 
+EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
+{
+    if (input.isEscape()) {
+        moveLeft(qMin(1, leftDist()));
+        setTargetColumn();
+        m_submode = NoSubMode;
+        m_mode = CommandMode;
+        finishMovement();
+    } else {
+        joinPreviousEditBlock();
+        if (!atEndOfLine()) {
+            setAnchor();
+            moveRight();
+            m_lastDeletion += selectText(Range(position(), anchor()));
+            removeText(currentRange());
+        }
+        const QString text = input.text();
+        m_lastInsertion += text;
+        insertText(text);
+        endEditBlock();
+    }
+    return EventHandled;
+}
+
 EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
 {
     //const int key = input.key;
     //const QString &text = input.text;
 
-    if (input.isKey(Key_Escape) || input.isKey(27) || input.isControl('c') ||
-         input.isControl(Key_BracketLeft)) {
+    if (input.isEscape()) {
         if (isVisualBlockMode() && !m_lastInsertion.contains('\n')) {
             leaveVisualMode();
             joinPreviousEditBlock();
@@ -2257,7 +2517,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
             setTargetColumn();
             for (int i = 0; i < m_visualInsertCount; ++i) {
                 moveDown();
-                m_tc.insertText(m_lastInsertion);
+                insertText(m_lastInsertion);
             }
             moveLeft(1);
             Range range(pos, position(), RangeBlockMode);
@@ -2266,32 +2526,26 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
             setDotCommand("p");
             endEditBlock();
         } else {
-            // normal insertion. start with '1', as one instance was
-            // already physically inserted while typing
-            QString data = m_lastInsertion;
-            for (int i = 1; i < count(); ++i) {
-                m_tc.insertText(m_lastInsertion);
+            // Normal insertion. Start with '1', as one instance was
+            // already physically inserted while typing.
+            QString data;
+            for (int i = 1; i < count(); ++i)
                 data += m_lastInsertion;
-            }
+            insertText(data);
             moveLeft(qMin(1, leftDist()));
             setTargetColumn();
             leaveVisualMode();
-            recordNewUndo();
         }
-        m_dotCommand += m_lastInsertion;
-        m_dotCommand += QChar(27);
+        g.dotCommand += m_lastInsertion;
+        g.dotCommand += QChar(27);
         enterCommandMode();
         m_submode = NoSubMode;
     } else if (input.isKey(Key_Insert)) {
-        if (m_submode == ReplaceSubMode) {
-            EDITOR(setCursorWidth(m_cursorWidth));
-            EDITOR(setOverwriteMode(false));
-            m_submode = NoSubMode;
-        } else {
-            EDITOR(setCursorWidth(m_cursorWidth));
-            EDITOR(setOverwriteMode(true));
-            m_submode = ReplaceSubMode;
-        }
+        if (m_mode == ReplaceMode)
+            m_mode = InsertMode;
+        else
+            m_mode = ReplaceMode;
+        updateCursor();
     } else if (input.isKey(Key_Left)) {
         moveLeft(count());
         setTargetColumn();
@@ -2320,13 +2574,13 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
         moveBehindEndOfLine();
         setTargetColumn();
         m_lastInsertion.clear();
-    } else if (input.isKey(Key_Return)) {
+    } else if (input.isReturn()) {
         m_submode = NoSubMode;
-        m_tc.insertBlock();
+        insertText(Register("\n"));
         m_lastInsertion += "\n";
         insertAutomaticIndentation(true);
         setTargetColumn();
-    } else if (input.isKey(Key_Backspace) || input.isControl('h')) {
+    } else if (input.isBackspace()) {
         joinPreviousEditBlock();
         m_justAutoIndented = 0;
         if (!m_lastInsertion.isEmpty() || hasConfig(ConfigBackspace, "start")) {
@@ -2345,6 +2599,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
                 m_lastInsertion.clear(); // FIXME
             } else {
                 m_tc.deletePreviousChar();
+                fixMarks(position(), -1);
                 m_lastInsertion.chop(1);
             }
             setTargetColumn();
@@ -2367,7 +2622,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
         const int col = physicalCursorColumnInDocument();
         QString str = QString(ts - col % ts, ' ');
         m_lastInsertion.append(str);
-        m_tc.insertText(str);
+        insertText(str);
         setTargetColumn();
     } else if (input.isControl('d')) {
         // remove one level of indentation from the current line
@@ -2390,33 +2645,21 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
     //} else if (key >= control('a') && key <= control('z')) {
     //    // ignore these
     } else if (!input.text().isEmpty()) {
-        const QString text = input.text();
-        if (m_beginEditBlock) {
-            beginEditBlock();
-            m_beginEditBlock = false;
-        } else {
-            joinPreviousEditBlock();
-        }
+        joinPreviousEditBlock();
         m_justAutoIndented = 0;
+        const QString text = input.text();
         m_lastInsertion.append(text);
-        if (m_submode == ReplaceSubMode) {
-            if (atEndOfLine())
-                m_submode = NoSubMode;
-            else
-                m_tc.deleteChar();
-        }
-        m_tc.insertText(text);
+        insertText(text);
         if (hasConfig(ConfigSmartIndent) && isElectricCharacter(text.at(0))) {
             const QString leftText = m_tc.block().text()
                    .left(m_tc.position() - 1 - m_tc.block().position());
             if (leftText.simplified().isEmpty()) {
-                Range range(position(), position());
-                range.rangemode = m_rangemode;
+                Range range(position(), position(), m_rangemode);
                 indentText(range, text.at(0));
             }
         }
 
-        if (!m_inReplay)
+        if (!g.inReplay)
             emit q->completionRequested();
         setTargetColumn();
         endEditBlock();
@@ -2427,15 +2670,15 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
     return EventHandled;
 }
 
-EventResult FakeVimHandler::Private::handleMiniBufferModes(const Input &input)
+EventResult FakeVimHandler::Private::handleExMode(const Input &input)
 {
-    if (input.isKey(Key_Escape) || input.isControl('c')
-            || input.isControl(Key_BracketLeft)) {
+    if (input.isEscape()) {
         m_commandBuffer.clear();
         enterCommandMode();
         updateMiniBuffer();
-    } else if (input.isKey(Key_Backspace)) {
+    } else if (input.isBackspace()) {
         if (m_commandBuffer.isEmpty()) {
+            m_commandPrefix.clear();
             enterCommandMode();
         } else {
             m_commandBuffer.chop(1);
@@ -2446,74 +2689,109 @@ EventResult FakeVimHandler::Private::handleMiniBufferModes(const Input &input)
         if (!m_commandBuffer.isEmpty())
             m_commandBuffer.chop(1);
         updateMiniBuffer();
-    } else if (input.isKey(Key_Return) && m_mode == ExMode) {
+    } else if (input.isReturn()) {
         if (!m_commandBuffer.isEmpty()) {
-            m_commandHistory.takeLast();
-            m_commandHistory.append(m_commandBuffer);
+            //g.commandHistory.takeLast();
+            g.commandHistory.append(m_commandBuffer);
             handleExCommand(m_commandBuffer);
-            if (m_textedit || m_plaintextedit) {
+            if (m_textedit || m_plaintextedit)
                 leaveVisualMode();
-            }
         }
-    } else if (input.isKey(Key_Return) && isSearchMode()
-            && !hasConfig(ConfigIncSearch)) {
-        if (!m_commandBuffer.isEmpty()) {
-            m_searchHistory.takeLast();
-            m_searchHistory.append(m_commandBuffer);
-            m_lastSearchForward = (m_mode == SearchForwardMode);
-            search(lastSearchString(), m_lastSearchForward);
-            recordJump();
-        }
-        enterCommandMode();
         updateMiniBuffer();
-    } else if ((input.isKey(Key_Up) || input.isKey(Key_PageUp)) && isSearchMode()) {
-        // FIXME: This and the three cases below are wrong as vim
-        // takes only matching entries in the history into account.
-        if (m_searchHistoryIndex > 0) {
-            --m_searchHistoryIndex;
-            showBlackMessage(m_searchHistory.at(m_searchHistoryIndex));
-        }
-    } else if ((input.isKey(Key_Up) || input.isKey(Key_PageUp)) && m_mode == ExMode) {
-        if (m_commandHistoryIndex > 0) {
-            --m_commandHistoryIndex;
-            showBlackMessage(m_commandHistory.at(m_commandHistoryIndex));
-        }
-    } else if ((input.isKey(Key_Down) || input.isKey(Key_PageDown)) && isSearchMode()) {
-        if (m_searchHistoryIndex < m_searchHistory.size() - 1) {
-            ++m_searchHistoryIndex;
-            showBlackMessage(m_searchHistory.at(m_searchHistoryIndex));
-        }
-    } else if ((input.isKey(Key_Down) || input.isKey(Key_PageDown)) && m_mode == ExMode) {
-        if (m_commandHistoryIndex < m_commandHistory.size() - 1) {
-            ++m_commandHistoryIndex;
-            showBlackMessage(m_commandHistory.at(m_commandHistoryIndex));
-        }
+    } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
+        g.commandHistory.up();
+        m_commandBuffer = g.commandHistory.current();
+        updateMiniBuffer();
+    } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
+        g.commandHistory.down();
+        m_commandBuffer = g.commandHistory.current();
+        updateMiniBuffer();
     } else if (input.isKey(Key_Tab)) {
         m_commandBuffer += QChar(9);
         updateMiniBuffer();
-    } else if (input.isKey(Key_Return) && isSearchMode()
-            && hasConfig(ConfigIncSearch)) {
+    } else if (!input.text().isEmpty()) {
+        m_commandBuffer += input.text();
+        updateMiniBuffer();
+    } else {
+        qDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
+        return EventUnhandled;
+    }
+    return EventHandled;
+}
+
+EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
+{
+    if (input.isEscape()) {
+        m_commandBuffer.clear();
+        m_searchCursor = QTextCursor();
+        updateSelection();
+        enterCommandMode();
+        updateMiniBuffer();
+    } else if (input.isBackspace()) {
+        if (m_commandBuffer.isEmpty()) {
+            m_commandPrefix.clear();
+            m_searchCursor = QTextCursor();
+            enterCommandMode();
+        } else {
+            m_commandBuffer.chop(1);
+        }
+        updateMiniBuffer();
+    } else if (input.isKey(Key_Left)) {
+        if (!m_commandBuffer.isEmpty())
+            m_commandBuffer.chop(1);
+        updateMiniBuffer();
+    } else if (input.isReturn()) {
+        m_searchCursor = QTextCursor();
+        QString needle = m_commandBuffer;
+        if (!needle.isEmpty()) {
+            g.searchHistory.append(needle);
+            if (!hasConfig(ConfigIncSearch)) {
+                SearchData sd;
+                sd.needle = needle;
+                sd.forward = m_lastSearchForward;
+                sd.highlightCursor = false;
+                sd.highlightMatches = true;
+                search(sd);
+            }
+            finishMovement(m_commandPrefix + needle + "\n");
+        }
         enterCommandMode();
-        QString needle = m_commandBuffer.mid(1); // FIXME: why
         highlightMatches(needle);
         updateMiniBuffer();
-    } else if (isSearchMode() && hasConfig(ConfigIncSearch)) {
-        m_commandBuffer = m_commandBuffer.mid(1); // FIXME: why
-        QString needle = m_commandBuffer + input.text();
-        search(needle, m_lastSearchForward, true);
+    } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
+        // FIXME: This and the three cases below are wrong as vim
+        // takes only matching entries in the history into account.
+        g.searchHistory.up();
+        showBlackMessage(g.searchHistory.current());
+    } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
+        g.searchHistory.down();
+        showBlackMessage(g.searchHistory.current());
+    } else if (input.isKey(Key_Tab)) {
+        m_commandBuffer += QChar(9);
         updateMiniBuffer();
-        recordJump();
     } else if (!input.text().isEmpty()) {
         m_commandBuffer += input.text();
         updateMiniBuffer();
-    } else {
-        qDebug() << "IGNORED IN MINIBUFFER MODE: " << input.key() << input.text();
-        return EventUnhandled;
     }
+
+    if (hasConfig(ConfigIncSearch) && !input.isReturn() && !input.isEscape()) {
+        SearchData sd;
+        sd.needle = m_commandBuffer;
+        sd.forward = m_lastSearchForward;
+        sd.mustMove = false;
+        sd.highlightCursor = true;
+        sd.highlightMatches = false;
+        search(sd);
+    }
+
+    //else {
+    //   qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
+    //   return EventUnhandled;
+    //}
     return EventHandled;
 }
 
-// 1 based.
+// This uses 1 based line counting.
 int FakeVimHandler::Private::readLineCode(QString &cmd)
 {
     //qDebug() << "CMD: " << cmd;
@@ -2521,19 +2799,32 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
         return -1;
     QChar c = cmd.at(0);
     cmd = cmd.mid(1);
-    if (c == '.')
+    if (c == '.') {
+        if (cmd.isEmpty())
+            return cursorLineInDocument() + 1;
+        QChar c1 = cmd.at(0);
+        if (c1 == '+' || c1 == '-') {
+            // Repeat for things like  .+4
+            cmd = cmd.mid(1);
+            return cursorLineInDocument() + readLineCode(cmd);
+        }
         return cursorLineInDocument() + 1;
+    }
     if (c == '$')
         return linesInDocument();
     if (c == '\'' && !cmd.isEmpty()) {
-        int mark = m_marks.value(cmd.at(0).unicode());
-        if (!mark) {
-            showRedMessage(msgE20MarkNotSet(cmd.at(0)));
+        if (cmd.isEmpty()) {
+            showRedMessage(msgMarkNotSet(QString()));
+            return -1;
+        }
+        int m = mark(cmd.at(0).unicode());
+        if (m == -1) {
+            showRedMessage(msgMarkNotSet(cmd.at(0)));
             cmd = cmd.mid(1);
             return -1;
         }
         cmd = cmd.mid(1);
-        return lineForPosition(mark);
+        return lineForPosition(m);
     }
     if (c == '-') {
         int n = readLineCode(cmd);
@@ -2544,10 +2835,9 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
         return cursorLineInDocument() + 1 + (n == -1 ? 1 : n);
     }
     if (c == '\'' && !cmd.isEmpty()) {
-        int pos = m_marks.value(cmd.at(0).unicode(), -1);
-        //qDebug() << " MARK: " << cmd.at(0) << pos << lineForPosition(pos);
+        int pos = mark(cmd.at(0).unicode());
         if (pos == -1) {
-            showRedMessage(msgE20MarkNotSet(cmd.at(0)));
+            showRedMessage(msgMarkNotSet(cmd.at(0)));
             cmd = cmd.mid(1);
             return -1;
         }
@@ -2566,24 +2856,16 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
         //qDebug() << "N: " << n;
         return n;
     }
-    // not parsed
+    // Parsing failed.
     cmd = c + cmd;
     return -1;
 }
 
-void FakeVimHandler::Private::selectRange(int beginLine, int endLine)
+void FakeVimHandler::Private::setCurrentRange(const Range &range)
 {
-    if (beginLine == -1)
-        beginLine = cursorLineInDocument();
-    if (endLine == -1)
-        endLine = cursorLineInDocument();
-    if (beginLine > endLine)
-        qSwap(beginLine, endLine);
-    setAnchor(firstPositionInLine(beginLine));
-    if (endLine == linesInDocument())
-       setPosition(lastPositionInLine(endLine));
-    else
-       setPosition(firstPositionInLine(endLine + 1));
+    setAnchor(range.beginPos);
+    setPosition(range.endPos);
+    m_rangemode = range.rangemode;
 }
 
 // use handleExCommand for invoking commands that might move the cursor
@@ -2594,71 +2876,46 @@ void FakeVimHandler::Private::handleCommand(const QString &cmd)
     EDITOR(setTextCursor(m_tc));
 }
 
-QString FakeVimHandler::Private::extractCommand(const QString &line,
-    int *beginLine, int *endLine)
-{
-    QString cmd = line;
-    *beginLine = -1;
-    *endLine = -1;
-
-    // FIXME: that seems to be different for %w and %s
-    if (cmd.startsWith(QLatin1Char('%')))
-        cmd = "1,$" + cmd.mid(1);
-
-    int lineNumber = readLineCode(cmd);
-    if (lineNumber != -1)
-        *beginLine = lineNumber;
-
-    if (cmd.startsWith(',')) {
-        cmd = cmd.mid(1);
-        lineNumber = readLineCode(cmd);
-        if (lineNumber != -1)
-            *endLine = lineNumber;
-    }
-    //qDebug() << "RANGE: " << beginLine << endLine << cmd << lineNumber << m_marks;
-    return cmd;
-}
-
-bool FakeVimHandler::Private::handleExSubstituteCommand(const QString &line)
+bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
     // :substitute
 {
-    int beginLine, endLine;
-    QString cmd = extractCommand(line, &beginLine, &endLine);
-
-    if (cmd.startsWith(QLatin1String("substitute")))
-        cmd = cmd.mid(10);
-    else if (cmd.startsWith('s') && line.size() > 1
-            && !isalpha(cmd.at(1).unicode()))
-        cmd = cmd.mid(1);
+    QString line = cmd.cmd + ' ' + cmd.args;
+    line = line.trimmed();
+    if (line.startsWith(_("substitute")))
+        line = line.mid(10);
+    else if (line.startsWith('s') && line.size() > 1
+            && !isalpha(line.at(1).unicode()))
+        line = line.mid(1);
     else
         return false;
+
     // we have /{pattern}/{string}/[flags]  now
-    if (cmd.isEmpty())
+    if (line.isEmpty())
         return false;
-    const QChar separator = cmd.at(0);
+    const QChar separator = line.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) != '\\') {
+    for (i = 1; i < line.size(); ++i) {
+        if (line.at(i) == separator && line.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) != '\\') {
+    for (++i; i < line.size(); ++i) {
+        if (line.at(i) == separator && line.at(i - 1) != '\\') {
             pos2 = i;
             break;
         }
     }
     if (pos2 == -1)
-        pos2 = cmd.size();
+        pos2 = line.size();
 
-    QString needle = cmd.mid(1, pos1 - 1);
-    const QString replacement = cmd.mid(pos1 + 1, pos2 - pos1 - 1);
-    QString flags = cmd.mid(pos2 + 1);
+    QString needle = line.mid(1, pos1 - 1);
+    const QString replacement = line.mid(pos1 + 1, pos2 - pos1 - 1);
+    QString flags = line.mid(pos2 + 1);
 
     needle.replace('$', '\n');
     needle.replace("\\\n", "\\$");
@@ -2666,6 +2923,8 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const QString &line)
     if (flags.contains('i'))
         pattern.setCaseSensitivity(Qt::CaseInsensitive);
     const bool global = flags.contains('g');
+    const int beginLine = lineForPosition(cmd.range.beginPos);
+    const int endLine = lineForPosition(cmd.range.endPos);
     beginEditBlock();
     for (int line = endLine; line >= beginLine; --line) {
         QString origText = lineContents(line);
@@ -2700,19 +2959,12 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const QString &line)
     return true;
 }
 
-bool FakeVimHandler::Private::handleExMapCommand(const QString &line) // :map
+bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
 {
-    int pos1 = line.indexOf(QLatin1Char(' '));
-    if (pos1 == -1)
-        return false;
-    int pos2 = line.indexOf(QLatin1Char(' '), pos1 + 1);
-    if (pos2 == -1)
-        return false;
-
     QByteArray modes;
     enum Type { Map, Noremap, Unmap } type;
 
-    QByteArray cmd = line.left(pos1).toLatin1();
+    QByteArray cmd = cmd0.cmd.toLatin1();
 
     // Strange formatting. But everything else is even uglier.
     if (cmd == "map") { modes = "nvo"; type = Map; } else
@@ -2750,45 +3002,48 @@ bool FakeVimHandler::Private::handleExMapCommand(const QString &line) // :map
     else
         return false;
 
-    QString lhs = line.mid(pos1 + 1, pos2 - pos1 - 1);
-    QString rhs = line.mid(pos2 + 1);
+    const int pos = cmd0.args.indexOf(QLatin1Char(' '));
+    if (pos == -1) {
+        // FIXME: Dump mappings here.
+        //qDebug() << g.mappings;
+        return true;;
+    }
+
+    QString lhs = cmd0.args.left(pos);
+    QString rhs = cmd0.args.mid(pos + 1);
     Inputs key;
-    foreach (QChar c, lhs)
-        key.append(Input(c));
+    key.parseFrom(lhs);
     //qDebug() << "MAPPING: " << modes << lhs << rhs;
     switch (type) {
         case Unmap:
             foreach (char c, modes)
-                if (m_mappings.contains(c))
-                    m_mappings[c].remove(key);
+                if (g.mappings.contains(c))
+                    g.mappings[c].remove(key);
             break;
         case Map:
             rhs = rhs; // FIXME: expand rhs.
             // Fall through.
         case Noremap: {
-            Inputs inputs;
-            foreach (QChar c, rhs)
-                inputs.append(Input(c));
+            Inputs inputs(rhs);
             foreach (char c, modes)
-                m_mappings[c].insert(key, inputs);
+                g.mappings[c].insert(key, inputs);
             break;
         }
     }
     return true;
 }
 
-bool FakeVimHandler::Private::handleExHistoryCommand(const QString &cmd) // :history
+bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd)
 {
-    static QRegExp reHistory("^his(tory)?( (.*))?$");
-    if (reHistory.indexIn(cmd) == -1)
+    // :history
+    if (cmd.cmd != "his" && cmd.cmd != "history")
         return false;
 
-    QString arg = reHistory.cap(3);
-    if (arg.isEmpty()) {
+    if (cmd.args.isEmpty()) {
         QString info;
         info += "#  command history\n";
         int i = 0;
-        foreach (const QString &item, m_commandHistory) {
+        foreach (const QString &item, g.commandHistory.items()) {
             ++i;
             info += QString("%1 %2\n").arg(i, -8).arg(item);
         }
@@ -2800,164 +3055,176 @@ bool FakeVimHandler::Private::handleExHistoryCommand(const QString &cmd) // :his
     return true;
 }
 
-bool FakeVimHandler::Private::handleExSetCommand(const QString &cmd) // :set
+bool FakeVimHandler::Private::handleExRegisterCommand(const ExCommand &cmd)
+{
+    // :reg and :di[splay]
+    if (cmd.cmd != "reg" && cmd.cmd != "registers"
+            && cmd.cmd != "di" && cmd.cmd != "display")
+        return false;
+
+    QByteArray regs = cmd.args.toLatin1();
+    if (regs.isEmpty()) {
+        regs = "\"0123456789";
+        QHashIterator<int, Register> it(g.registers);
+        while (it.hasNext()) {
+            it.next();
+            if (it.key() > '9')
+                regs += char(it.key());
+        }
+    }
+    QString info;
+    info += "--- Registers ---\n";
+    foreach (char reg, regs) {
+        QString value = quoteUnprintable(g.registers[reg].contents);
+        info += QString("\"%1   %2\n").arg(reg).arg(value);
+    }
+    emit q->extraInformationChanged(info);
+    updateMiniBuffer();
+    return true;
+}
+
+bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
 {
-    static QRegExp reSet("^set?( (.*))?$");
-    if (reSet.indexIn(cmd) == -1)
+    // :set
+    if (cmd.cmd != "se" && cmd.cmd != "set")
         return false;
 
     showBlackMessage(QString());
-    QString arg = reSet.cap(2);
-    SavedAction *act = theFakeVimSettings()->item(arg);
-    if (arg.isEmpty()) {
-        theFakeVimSetting(SettingsDialog)->trigger(QVariant());
-    } else if (act && act->value().type() == QVariant::Bool) {
-        // boolean config to be switched on
+    SavedAction *act = theFakeVimSettings()->item(cmd.args);
+    QTC_ASSERT(!cmd.args.isEmpty(), /**/); // Handled by plugin.
+    if (act && act->value().type() == QVariant::Bool) {
+        // Boolean config to be switched on.
         bool oldValue = act->value().toBool();
         if (oldValue == false)
             act->setValue(true);
         else if (oldValue == true)
             {} // nothing to do
     } else if (act) {
-        // non-boolean to show
-        showBlackMessage(arg + '=' + act->value().toString());
-    } else if (arg.startsWith(QLatin1String("no"))
-            && (act = theFakeVimSettings()->item(arg.mid(2)))) {
-        // boolean config to be switched off
+        // Non-boolean to show.
+        showBlackMessage(cmd.args + '=' + act->value().toString());
+    } else if (cmd.args.startsWith(_("no"))
+            && (act = theFakeVimSettings()->item(cmd.args.mid(2)))) {
+        // Boolean config to be switched off.
         bool oldValue = act->value().toBool();
         if (oldValue == true)
             act->setValue(false);
         else if (oldValue == false)
             {} // nothing to do
-    } else if (arg.contains('=')) {
-        // non-boolean config to set
-        int p = arg.indexOf('=');
-        act = theFakeVimSettings()->item(arg.left(p));
+    } else if (cmd.args.contains('=')) {
+        // Non-boolean config to set.
+        int p = cmd.args.indexOf('=');
+        act = theFakeVimSettings()->item(cmd.args.left(p));
         if (act)
-            act->setValue(arg.mid(p + 1));
+            act->setValue(cmd.args.mid(p + 1));
     } else {
-        passUnknownSetCommand(arg);
+        showRedMessage(FakeVimHandler::tr("Unknown option: ") + cmd.args);
     }
     updateMiniBuffer();
     updateEditor();
     return true;
 }
 
-bool FakeVimHandler::Private::handleExNormalCommand(const QString &cmd) // :normal
+bool FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
 {
-    static QRegExp reNormal("^norm(al)?( (.*))?$");
-    if (reNormal.indexIn(cmd) == -1)
+    // :normal
+    if (cmd.cmd != "norm" && cmd.cmd != "normal") 
         return false;
-    //qDebug() << "REPLAY: " << reNormal.cap(3);
-    replay(reNormal.cap(3), 1);
+    //qDebug() << "REPLAY NORMAL: " << quoteUnprintable(reNormal.cap(3));
+    replay(cmd.args, 1);
     return true;
 }
 
-bool FakeVimHandler::Private::handleExDeleteCommand(const QString &line) // :d
+bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd)
 {
-    int beginLine, endLine;
-    QString cmd = extractCommand(line, &beginLine, &endLine);
-
-    static QRegExp reDelete("^d( (.*))?$");
-    if (reDelete.indexIn(cmd) != -1)
+    // :delete
+    if (cmd.cmd != "d" && cmd.cmd != "delete")
         return false;
 
-    selectRange(beginLine, endLine);
-    QString reg = reDelete.cap(2);
-    QString text = selectedText();
-    removeSelectedText();
+    setCurrentRange(cmd.range);
+    QString reg = cmd.args;
+    QString text = selectText(cmd.range);
+    removeText(currentRange());
     if (!reg.isEmpty()) {
-        Register &r = m_registers[reg.at(0).unicode()];
+        Register &r = g.registers[reg.at(0).unicode()];
         r.contents = text;
         r.rangemode = RangeLineMode;
     }
     return true;
 }
 
-bool FakeVimHandler::Private::handleExWriteCommand(const QString &line)
-    // :w, :x, :q, :wq, ...
+bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
 {
-    int beginLine, endLine;
-    QString cmd = extractCommand(line, &beginLine, &endLine);
-
-    static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
-    if (reWrite.indexIn(cmd) == -1) // :w and :x
+    // :w, :x, :wq, ...
+    //static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
+    if (cmd.cmd != "w" && cmd.cmd != "x" && cmd.cmd != "wq")
         return false;
 
-    bool noArgs = (beginLine == -1);
+    int beginLine = lineForPosition(cmd.range.beginPos);
+    int endLine = lineForPosition(cmd.range.endPos);
+    const bool noArgs = (beginLine == -1);
     if (beginLine == -1)
         beginLine = 0;
     if (endLine == -1)
         endLine = linesInDocument();
     //qDebug() << "LINES: " << beginLine << endLine;
-    int indexOfSpace = cmd.indexOf(QChar(' '));
-    QString prefix;
-    if (indexOfSpace < 0)
-        prefix = cmd;
-    else
-        prefix = cmd.left(indexOfSpace);
-    bool forced = prefix.contains(QChar('!'));
-    bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
-    bool quitAll = quit && prefix.contains(QChar('a'));
-    QString fileName = reWrite.cap(2);
+    QString prefix = cmd.args;
+    const bool forced = cmd.hasBang;
+    //const bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
+    //const bool quitAll = quit && prefix.contains(QChar('a'));
+    QString fileName = cmd.args;
     if (fileName.isEmpty())
         fileName = m_currentFileName;
     QFile file1(fileName);
-    bool exists = file1.exists();
+    const bool exists = file1.exists();
     if (exists && !forced && !noArgs) {
         showRedMessage(FakeVimHandler::tr
-            ("File '%1' exists (add ! to override)").arg(fileName));
+            ("File \"%1\" exists (add ! to override)").arg(fileName));
     } else if (file1.open(QIODevice::ReadWrite)) {
+        // Nobody cared, so act ourselves.
         file1.close();
         QTextCursor tc = m_tc;
         Range range(firstPositionInLine(beginLine),
             firstPositionInLine(endLine), RangeLineMode);
-        QString contents = text(range);
+        QString contents = selectText(range);
         m_tc = tc;
-        //qDebug() << "LINES: " << beginLine << endLine;
-        bool handled = false;
-        emit q->writeFileRequested(&handled, fileName, contents);
-        // nobody cared, so act ourselves
-        if (!handled) {
-            //qDebug() << "HANDLING MANUAL SAVE TO " << fileName;
-            QFile::remove(fileName);
-            QFile file2(fileName);
-            if (file2.open(QIODevice::ReadWrite)) {
-                QTextStream ts(&file2);
-                ts << contents;
-            } else {
-                showRedMessage(FakeVimHandler::tr
-                   ("Cannot open file '%1' for writing").arg(fileName));
-            }
+        QFile::remove(fileName);
+        QFile file2(fileName);
+        if (file2.open(QIODevice::ReadWrite)) {
+            QTextStream ts(&file2);
+            ts << contents;
+        } else {
+            showRedMessage(FakeVimHandler::tr
+               ("Cannot open file \"%1\" for writing").arg(fileName));
         }
-        // check result by reading back
+        // Check result by reading back.
         QFile file3(fileName);
         file3.open(QIODevice::ReadOnly);
         QByteArray ba = file3.readAll();
         showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
             .arg(fileName).arg(exists ? " " : " [New] ")
             .arg(ba.count('\n')).arg(ba.size()));
-        if (quitAll)
-            passUnknownExCommand(forced ? "qa!" : "qa");
-        else if (quit)
-            passUnknownExCommand(forced ? "q!" : "q");
+        //if (quitAll)
+        //    passUnknownExCommand(forced ? "qa!" : "qa");
+        //else if (quit)
+        //    passUnknownExCommand(forced ? "q!" : "q");
     } else {
         showRedMessage(FakeVimHandler::tr
-            ("Cannot open file '%1' for reading").arg(fileName));
+            ("Cannot open file \"%1\" for reading").arg(fileName));
     }
     return true;
 }
 
-bool FakeVimHandler::Private::handleExReadCommand(const QString &line) // :r
+bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
 {
-    if (!line.startsWith(QLatin1String("r ")))
+    // :read
+    if (cmd.cmd != "r" && cmd.cmd != "read")
         return false;
 
     beginEditBlock();
     moveToStartOfLine();
     setTargetColumn();
     moveDown();
-    m_currentFileName = line.mid(2);
+    m_currentFileName = cmd.args;
     QFile file(m_currentFileName);
     file.open(QIODevice::ReadOnly);
     QTextStream ts(&file);
@@ -2969,28 +3236,28 @@ bool FakeVimHandler::Private::handleExReadCommand(const QString &line) // :r
     return true;
 }
 
-bool FakeVimHandler::Private::handleExBangCommand(const QString &line) // :!
+bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
 {
-    int beginLine, endLine;
-    QString cmd = extractCommand(line, &beginLine, &endLine);
-
-    if (!cmd.startsWith(QLatin1Char('!')))
+    if (!cmd.cmd.startsWith(QLatin1Char('!')))
         return false;
 
-    selectRange(beginLine, endLine);
-    int targetPosition = firstPositionInLine(beginLine);
-    QString command = cmd.mid(1).trimmed();
-    QString text = selectedText();
+    setCurrentRange(cmd.range);
+    int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos));
+    QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
+    QString text = selectText(cmd.range);
     QProcess proc;
-    proc.start(cmd.mid(1));
+    proc.start(command);
     proc.waitForStarted();
+#ifdef Q_OS_WIN
+    text.replace(_("\n"), _("\r\n"));
+#endif
     proc.write(text.toUtf8());
     proc.closeWriteChannel();
     proc.waitForFinished();
     QString result = QString::fromUtf8(proc.readAllStandardOutput());
     beginEditBlock(targetPosition);
-    removeSelectedText();
-    m_tc.insertText(result);
+    removeText(currentRange());
+    insertText(result);
     setPosition(targetPosition);
     endEditBlock();
     leaveVisualMode();
@@ -3000,26 +3267,29 @@ bool FakeVimHandler::Private::handleExBangCommand(const QString &line) // :!
     return true;
 }
 
-bool FakeVimHandler::Private::handleExShiftRightCommand(const QString &line) // :>
+bool FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
 {
-    int beginLine, endLine;
-    QString cmd = extractCommand(line, &beginLine, &endLine);
-
-    if (!cmd.startsWith(QLatin1Char('>')))
+    if (cmd.cmd != "<" && cmd.cmd != ">")
         return false;
 
-    m_anchor = firstPositionInLine(beginLine);
-    setPosition(firstPositionInLine(endLine));
-    shiftRegionRight(1);
+    setCurrentRange(cmd.range);
+    int count = qMin(1, cmd.args.toInt());
+    if (cmd.cmd == "<")
+        shiftRegionLeft(count);
+    else
+        shiftRegionRight(count);
     leaveVisualMode();
-    showBlackMessage(FakeVimHandler::tr("%n lines >ed %1 time", 0,
-        (endLine - beginLine + 1)).arg(1));
+    const int beginLine = lineForPosition(cmd.range.beginPos);
+    const int endLine = lineForPosition(cmd.range.endPos);
+    showBlackMessage(FakeVimHandler::tr("%n lines %1ed %2 time", 0,
+        (endLine - beginLine + 1)).arg(cmd.cmd).arg(count));
     return true;
 }
 
-bool FakeVimHandler::Private::handleExRedoCommand(const QString &line) // :redo
+bool FakeVimHandler::Private::handleExRedoCommand(const ExCommand &cmd)
 {
-    if (line != "red" && line != "redo")
+    // :redo
+    if (cmd.cmd != "red" && cmd.cmd != "redo")
         return false;
 
     redo();
@@ -3027,26 +3297,25 @@ bool FakeVimHandler::Private::handleExRedoCommand(const QString &line) // :redo
     return true;
 }
 
-bool FakeVimHandler::Private::handleExGotoCommand(const QString &line) // :<nr>
+bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
 {
-    int beginLine, endLine;
-    QString cmd = extractCommand(line, &beginLine, &endLine);
-
-    if (!cmd.isEmpty())
+    // :<nr>
+    if (!cmd.cmd.isEmpty())
         return false;
 
+    const int beginLine = lineForPosition(cmd.range.beginPos);
     setPosition(firstPositionInLine(beginLine));
     showBlackMessage(QString());
     return true;
 }
 
-bool FakeVimHandler::Private::handleExSourceCommand(const QString &line) // :source
+bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
 {
-    int pos = line.indexOf(' ');
-    if (line.leftRef(pos) != "so" && line.leftRef(pos) != "source")
+    // :source
+    if (cmd.cmd != "so" && cmd.cmd != "source")
         return false;
 
-    QString fileName = line.mid(pos + 1);
+    QString fileName = cmd.args;
     QFile file(fileName);
     if (!file.open(QIODevice::ReadOnly)) {
         showRedMessage(FakeVimHandler::tr("Can't open file %1").arg(fileName));
@@ -3079,119 +3348,137 @@ bool FakeVimHandler::Private::handleExSourceCommand(const QString &line) // :sou
 void FakeVimHandler::Private::handleExCommand(const QString &line0)
 {
     QString line = line0; // Make sure we have a copy to prevent aliasing.
+    // FIXME: that seems to be different for %w and %s
+    if (line.startsWith(QLatin1Char('%')))
+        line = "1,$" + line.mid(1);
+
+    int beginLine = readLineCode(line);
+    int endLine = -1;
+    if (line.startsWith(',')) {
+        line = line.mid(1);
+        endLine = readLineCode(line);
+    }
+    if (beginLine != -1 && endLine == -1)
+        endLine = beginLine;
+    const int beginPos = firstPositionInLine(beginLine);
+    const int endPos = lastPositionInLine(endLine);
+    ExCommand cmd;
+    const QString arg0 = line.section(' ', 0, 0);
+    cmd.cmd = arg0;
+    cmd.args = line.mid(arg0.size() + 1).trimmed();
+    cmd.range = Range(beginPos, endPos, RangeLineMode);
+    cmd.hasBang = arg0.endsWith('!');
+    if (cmd.hasBang)
+        cmd.cmd.chop(1);
+    //qDebug() << "CMD: " << cmd;
+
     enterCommandMode();
     showBlackMessage(QString());
-    if (handleExCommandHelper(line))
-        return;
-    int beginLine, endLine;
-    passUnknownExCommand(extractCommand(line, &beginLine, &endLine));
+    if (!handleExCommandHelper(cmd))
+        showRedMessage(tr("Not an editor command: %1").arg(cmd.cmd));
 }
 
-bool FakeVimHandler::Private::handleExCommandHelper(const QString &line)
+bool FakeVimHandler::Private::handleExCommandHelper(const ExCommand &cmd)
 {
-    return handleExGotoCommand(line)
-        || handleExBangCommand(line)
-        || handleExHistoryCommand(line)
-        || handleExMapCommand(line)
-        || handleExNormalCommand(line)
-        || handleExReadCommand(line)
-        || handleExRedoCommand(line)
-        || handleExSetCommand(line)
-        || handleExShiftRightCommand(line)
-        || handleExSourceCommand(line)
-        || handleExSubstituteCommand(line)
-        || handleExWriteCommand(line);
+    return handleExPluginCommand(cmd)
+        || handleExGotoCommand(cmd)
+        || handleExBangCommand(cmd)
+        || handleExHistoryCommand(cmd)
+        || handleExRegisterCommand(cmd)
+        || handleExDeleteCommand(cmd)
+        || handleExMapCommand(cmd)
+        || handleExNormalCommand(cmd)
+        || handleExReadCommand(cmd)
+        || handleExRedoCommand(cmd)
+        || handleExSetCommand(cmd)
+        || handleExShiftCommand(cmd)
+        || handleExSourceCommand(cmd)
+        || handleExSubstituteCommand(cmd)
+        || handleExWriteCommand(cmd);
 }
 
-void FakeVimHandler::Private::passUnknownExCommand(const QString &cmd)
+bool FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
 {
     EDITOR(setTextCursor(m_tc));
-    emit q->handleExCommandRequested(cmd);
+    bool handled = false;
+    emit q->handleExCommandRequested(&handled, cmd);
     if (m_plaintextedit || m_textedit)
         m_tc = EDITOR(textCursor());
-}
-
-void FakeVimHandler::Private::passUnknownSetCommand(const QString &arg)
-{
-    bool handled = false;
-    emit q->handleSetCommandRequested(&handled, arg);
-    if (!handled) {
-        showRedMessage(FakeVimHandler::tr("E512: Unknown option: ") + arg);
-    }
+    //qDebug() << "HANDLER REQUEST: " << cmd.cmd << handled;
+    return handled;
 }
 
 static void vimPatternToQtPattern(QString *needle, QTextDocument::FindFlags *flags)
 {
     // FIXME: Rough mapping of a common case
-    if (needle->startsWith(QLatin1String("\\<")) && needle->endsWith(QLatin1String("\\>")))
+    if (needle->startsWith(_("\\<")) && needle->endsWith(_("\\>")))
         (*flags) |= QTextDocument::FindWholeWords;
-    needle->remove(QLatin1String("\\<")); // start of word
-    needle->remove(QLatin1String("\\>")); // end of word
+    needle->remove(_("\\<")); // start of word
+    needle->remove(_("\\>")); // end of word
     //qDebug() << "NEEDLE " << needle0 << needle;
 }
 
-void FakeVimHandler::Private::search(const QString &needle0, bool forward,
-    bool incSearch)
+void FakeVimHandler::Private::search(const SearchData &sd)
 {
-    showBlackMessage((forward ? '/' : '?') + needle0);
-    CursorPosition origPosition = cursorPosition();
+    if (sd.needle.isEmpty())
+        return;
+
+    const bool incSearch = hasConfig(ConfigIncSearch);
     QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
-    if (!forward)
+    if (!sd.forward)
         flags |= QTextDocument::FindBackward;
 
-    QString needle = needle0;
+    QString needle = sd.needle;
     vimPatternToQtPattern(&needle, &flags);
 
-    if (forward)
-        m_tc.movePosition(Right, MoveAnchor, 1);
+    const int oldLine = cursorLineInDocument() - cursorLineOnScreen();
 
-    int oldLine = cursorLineInDocument() - cursorLineOnScreen();
+    int startPos = position();
+    if (sd.mustMove)
+        sd.forward ? ++startPos : --startPos;
 
-    EDITOR(setTextCursor(m_tc));
-    if (EDITOR(find(needle, flags))) {
-        m_tc = EDITOR(textCursor());
-        m_tc.setPosition(m_tc.anchor());
-        // making this unconditional feels better, but is not "vim like"
-        if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
-            scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
-        if (!incSearch)
-            highlightMatches(needle);
-    } else {
-        m_tc.setPosition(forward ? 0 : lastPositionInDocument());
-        EDITOR(setTextCursor(m_tc));
-        if (EDITOR(find(needle, flags))) {
-            m_tc = EDITOR(textCursor());
-            m_tc.setPosition(m_tc.anchor());
-            if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
-                scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
+    m_searchCursor = QTextCursor();
+    QTextCursor tc = m_tc.document()->find(needle, startPos, flags);
+    if (tc.isNull()) {
+        int startPos = sd.forward ? 0 : lastPositionInDocument();
+        tc = m_tc.document()->find(needle, startPos, flags);
+        if (tc.isNull()) {
             if (!incSearch) {
-                if (forward)
-                    showRedMessage(FakeVimHandler::tr("search hit BOTTOM, continuing at TOP"));
-                else
-                    showRedMessage(FakeVimHandler::tr("search hit TOP, continuing at BOTTOM"));
-                highlightMatches(needle);
-            }
-        } else {
-            if (!incSearch)
                 highlightMatches(QString());
-            setCursorPosition(origPosition);
-            if (!incSearch)
                 showRedMessage(FakeVimHandler::tr("Pattern not found: ") + needle);
+            }
+            updateSelection();
+            return;
+        }
+        if (!incSearch) {
+            QString msg = sd.forward
+                ? FakeVimHandler::tr("search hit BOTTOM, continuing at TOP")
+                : FakeVimHandler::tr("search hit TOP, continuing at BOTTOM");
+            showRedMessage(msg);
         }
     }
-    if (incSearch) {
-        QTextEdit::ExtraSelection sel;
-        sel.cursor = m_tc;
-        sel.format = m_tc.blockCharFormat();
-        sel.format.setForeground(Qt::white);
-        sel.format.setBackground(Qt::black);
-        sel.cursor.setPosition(m_tc.position(), MoveAnchor);
-        sel.cursor.setPosition(m_tc.position() + needle.size(), KeepAnchor);
-        QList<QTextEdit::ExtraSelection> selections = m_searchSelections;
-        selections.append(sel);
-        emit q->selectionChanged(selections);
+
+    // Set Cursor.
+    tc.setPosition(qMin(tc.position(), tc.anchor()), MoveAnchor);
+    tc.clearSelection();
+    m_tc = tc;
+    EDITOR(setTextCursor(m_tc));
+
+    // Making this unconditional feels better, but is not "vim like".
+    if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
+        scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
+
+    if (incSearch && sd.highlightCursor) {
+        m_searchCursor = m_tc;
+        m_searchCursor.setPosition(m_tc.position(), MoveAnchor);
+        m_searchCursor.setPosition(m_tc.position() + needle.size(), KeepAnchor);
     }
     setTargetColumn();
+
+    if (sd.highlightMatches)
+        highlightMatches(needle);
+    updateSelection();
+    recordJump();
 }
 
 void FakeVimHandler::Private::highlightMatches(const QString &needle0)
@@ -3202,7 +3489,6 @@ void FakeVimHandler::Private::highlightMatches(const QString &needle0)
         return;
     m_oldNeedle = needle0;
     m_searchSelections.clear();
-
     if (!needle0.isEmpty()) {
         QTextCursor tc = m_tc;
         tc.movePosition(StartOfDocument, MoveAnchor);
@@ -3210,18 +3496,16 @@ void FakeVimHandler::Private::highlightMatches(const QString &needle0)
         QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
         QString needle = needle0;
         vimPatternToQtPattern(&needle, &flags);
-
-
-        EDITOR(setTextCursor(tc));
-        while (EDITOR(find(needle, flags))) {
-            tc = EDITOR(textCursor());
+        while (1) {
+            tc = tc.document()->find(needle, tc.position(), flags);
+            if (tc.isNull())
+                break;
             QTextEdit::ExtraSelection sel;
             sel.cursor = tc;
             sel.format = tc.blockCharFormat();
             sel.format.setBackground(QColor(177, 177, 0));
             m_searchSelections.append(sel);
             tc.movePosition(Right, MoveAnchor);
-            EDITOR(setTextCursor(tc));
         }
     }
     updateSelection();
@@ -3247,8 +3531,7 @@ void FakeVimHandler::Private::indentSelectedText(QChar typedChar)
     int beginLine = qMin(lineForPosition(position()), lineForPosition(anchor()));
     int endLine = qMax(lineForPosition(position()), lineForPosition(anchor()));
 
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
+    Range range(anchor(), position(), m_rangemode);
     indentText(range, typedChar);
 
     setPosition(firstPositionInLine(beginLine));
@@ -3266,8 +3549,10 @@ int FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
 
     int amount = 0;
     // lineForPosition has returned 1-based line numbers
-    emit q->indentRegion(&amount, beginLine-1, endLine-1, typedChar);
-
+    emit q->indentRegion(&amount, beginLine - 1, endLine - 1, typedChar);
+    fixMarks(firstPositionInLine(beginLine), amount);
+    if (beginLine != endLine)
+        showBlackMessage("MARKS ARE OFF NOW");
     return amount;
 }
 
@@ -3313,7 +3598,7 @@ void FakeVimHandler::Private::shiftRegionLeft(int repeat)
     int targetPos = anchor();
     if (beginLine > endLine) {
         qSwap(beginLine, endLine);
-        targetPos = position(); 
+        targetPos = position();
     }
     const int shift = config(ConfigShiftWidth).toInt() * repeat;
     const int tab = config(ConfigTabStop).toInt();
@@ -3635,12 +3920,7 @@ int FakeVimHandler::Private::lastPositionInDocument() const
     return block.position() + block.length() - 1;
 }
 
-QString FakeVimHandler::Private::lastSearchString() const
-{
-     return m_searchHistory.empty() ? QString() : m_searchHistory.back();
-}
-
-QString FakeVimHandler::Private::text(const Range &range) const
+QString FakeVimHandler::Private::selectText(const Range &range) const
 {
     if (range.rangemode == RangeCharMode) {
         QTextCursor tc = m_tc;
@@ -3688,34 +3968,35 @@ QString FakeVimHandler::Private::text(const Range &range) const
     return contents;
 }
 
-void FakeVimHandler::Private::yankSelectedText()
-{
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
-    yankText(range, m_register);
-}
-
 void FakeVimHandler::Private::yankText(const Range &range, int toregister)
 {
-    Register &reg = m_registers[toregister];
-    reg.contents = text(range);
+    Register &reg = g.registers[toregister];
+    reg.contents = selectText(range);
     reg.rangemode = range.rangemode;
     //qDebug() << "YANKED: " << reg.contents;
 }
 
 void FakeVimHandler::Private::transformText(const Range &range,
-    Transformation transformFunc)
+    Transformation transformFunc, const QVariant &extra)
 {
     QTextCursor tc = m_tc;
     switch (range.rangemode) {
         case RangeCharMode: {
+            // This can span multiple lines.
+            beginEditBlock();
             tc.setPosition(range.beginPos, MoveAnchor);
             tc.setPosition(range.endPos, KeepAnchor);
-            (this->*transformFunc)(range.beginPos, &tc);
+            TransformationData td(tc.selectedText(), extra);
+            (this->*transformFunc)(&td);
+            tc.removeSelectedText();
+            fixMarks(range.beginPos, td.to.size() - td.from.size());
+            tc.insertText(td.to);
+            endEditBlock();
             return;
         }
         case RangeLineMode:
         case RangeLineModeExclusive: {
+            beginEditBlock(range.beginPos);
             tc.setPosition(range.beginPos, MoveAnchor);
             tc.movePosition(StartOfLine, MoveAnchor);
             tc.setPosition(range.endPos, KeepAnchor);
@@ -3737,7 +4018,12 @@ void FakeVimHandler::Private::transformText(const Range &range,
                     tc.movePosition(Right, KeepAnchor, 1);
                 }
             }
-            (this->*transformFunc)(range.beginPos, &tc);
+            TransformationData td(tc.selectedText(), extra);
+            (this->*transformFunc)(&td);
+            tc.removeSelectedText();
+            fixMarks(range.beginPos, td.to.size() - td.from.size());
+            tc.insertText(td.to);
+            endEditBlock();
             return;
         }
         case RangeBlockAndTailMode:
@@ -3757,7 +4043,11 @@ void FakeVimHandler::Private::transformText(const Range &range,
                 int eCol = qMin(endColumn + 1, block.length() - 1);
                 tc.setPosition(block.position() + bCol, MoveAnchor);
                 tc.setPosition(block.position() + eCol, KeepAnchor);
-                (this->*transformFunc)(block.position() + bCol, &tc);
+                TransformationData td(tc.selectedText(), extra);
+                (this->*transformFunc)(&td);
+                tc.removeSelectedText();
+                fixMarks(block.position() + bCol, td.to.size() - td.from.size());
+                tc.insertText(td.to);
                 block = block.previous();
             }
             endEditBlock();
@@ -3765,11 +4055,12 @@ void FakeVimHandler::Private::transformText(const Range &range,
     }
 }
 
-void FakeVimHandler::Private::removeSelectedText()
+void FakeVimHandler::Private::insertText(const Register &reg)
 {
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
-    removeText(range);
+    QTC_ASSERT(reg.rangemode == RangeCharMode,
+        qDebug() << "WRONG INSERT MODE: " << reg.rangemode; return);
+    fixMarks(position(), reg.contents.length());
+    m_tc.insertText(reg.contents);
 }
 
 void FakeVimHandler::Private::removeText(const Range &range)
@@ -3777,100 +4068,69 @@ void FakeVimHandler::Private::removeText(const Range &range)
     transformText(range, &FakeVimHandler::Private::removeTransform);
 }
 
-void FakeVimHandler::Private::removeTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::removeTransform(TransformationData *td)
 {
-    fixMarks(updateMarksAfter, tc->selectionStart() - tc->selectionEnd());
-    tc->removeSelectedText();
+    Q_UNUSED(td);
 }
 
-void FakeVimHandler::Private::downCaseSelectedText()
+void FakeVimHandler::Private::downCase(const Range &range)
 {
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
     transformText(range, &FakeVimHandler::Private::downCaseTransform);
 }
 
-void FakeVimHandler::Private::downCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::downCaseTransform(TransformationData *td)
 {
-    Q_UNUSED(updateMarksAfter);
-    QString str = tc->selectedText();
-    tc->removeSelectedText();
-    for (int i = str.size(); --i >= 0; ) {
-        QChar c = str.at(i);
-        str[i] = c.toLower();
-    }
-    tc->insertText(str);
+    td->to = td->from.toLower();
 }
 
-void FakeVimHandler::Private::upCaseSelectedText()
+void FakeVimHandler::Private::upCase(const Range &range)
 {
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
     transformText(range, &FakeVimHandler::Private::upCaseTransform);
 }
 
-void FakeVimHandler::Private::upCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::upCaseTransform(TransformationData *td)
 {
-    Q_UNUSED(updateMarksAfter);
-    QString str = tc->selectedText();
-    tc->removeSelectedText();
-    for (int i = str.size(); --i >= 0; ) {
-        QChar c = str.at(i);
-        str[i] = c.toUpper();
-    }
-    tc->insertText(str);
+    td->to = td->from.toUpper();
 }
 
-void FakeVimHandler::Private::invertCaseSelectedText()
+void FakeVimHandler::Private::invertCase(const Range &range)
 {
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
     transformText(range, &FakeVimHandler::Private::invertCaseTransform);
 }
 
-void FakeVimHandler::Private::invertCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::invertCaseTransform(TransformationData *td)
 {
-    Q_UNUSED(updateMarksAfter);
-    QString str = tc->selectedText();
-    tc->removeSelectedText();
-    for (int i = str.size(); --i >= 0; ) {
-        QChar c = str.at(i);
-        str[i] = c.isUpper() ? c.toLower() : c.toUpper();
-    }
-    tc->insertText(str);
+    foreach (QChar c, td->from)
+        td->to += c.isUpper() ? c.toLower() : c.toUpper();
 }
 
-void FakeVimHandler::Private::replaceSelectedText()
+void FakeVimHandler::Private::replaceText(const Range &range, const QString &str)
 {
-    Range range(anchor(), position());
-    range.rangemode = m_rangemode;
-    transformText(range, &FakeVimHandler::Private::replaceTransform);
+    Transformation tr = &FakeVimHandler::Private::replaceByStringTransform;
+    transformText(range, tr, str);
 }
 
-void FakeVimHandler::Private::replaceTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::replaceByStringTransform(TransformationData *td)
 {
-    Q_UNUSED(updateMarksAfter);
-    QString str = tc->selectedText();
-    tc->removeSelectedText();
-    for (int i = str.size(); --i >= 0; ) {
-        QChar c = str.at(i);
-        str[i] = (c.toAscii() == '\n' || c.toAscii() == '\0') ? QChar('\n') : m_replacingCharacter;
-    }
-    tc->insertText(str);
+    td->to = td->extraData.toString();
+}
+
+void FakeVimHandler::Private::replaceByCharTransform(TransformationData *td)
+{
+    td->to = QString(td->from.size(), td->extraData.toChar());
 }
 
 void FakeVimHandler::Private::pasteText(bool afterCursor)
 {
-    const QString text = m_registers[m_register].contents;
+    const QString text = g.registers[m_register].contents;
     const QStringList lines = text.split(QChar('\n'));
-    switch (m_registers[m_register].rangemode) {
+    switch (g.registers[m_register].rangemode) {
         case RangeCharMode: {
             m_targetColumn = 0;
             for (int i = count(); --i >= 0; ) {
                 if (afterCursor && rightDist() > 0)
                     moveRight();
-                fixMarks(position(), text.length());
-                m_tc.insertText(text);
+                insertText(text);
                 if (!afterCursor && atEndOfLine())
                     moveLeft();
                 moveLeft();
@@ -3884,8 +4144,7 @@ void FakeVimHandler::Private::pasteText(bool afterCursor)
             for (int i = count(); --i >= 0; ) {
                 if (afterCursor)
                     moveDown();
-                fixMarks(position(), text.length());
-                m_tc.insertText(text);
+                insertText(text);
                 moveUp(lines.size() - 1);
             }
             moveToFirstNonBlankOnLine();
@@ -3910,8 +4169,8 @@ void FakeVimHandler::Private::pasteText(bool afterCursor)
                 } else {
                     tc.movePosition(Right, MoveAnchor, col - 1 + afterCursor);
                 }
-                qDebug() << "INSERT " << line << " AT " << tc.position()
-                    << "COL: " << col;
+                //qDebug() << "INSERT " << line << " AT " << tc.position()
+                //    << "COL: " << col;
                 fixMarks(position(), line.length());
                 tc.insertText(line);
                 tc.movePosition(StartOfLine, MoveAnchor);
@@ -3931,16 +4190,38 @@ void FakeVimHandler::Private::pasteText(bool afterCursor)
 }
 
 //FIXME: This needs to called after undo/insert
-void FakeVimHandler::Private::fixMarks(int positionAction, int positionChange)
-{
-    QHashIterator<int, int> i(m_marks);
-    while (i.hasNext()) {
-        i.next();
-        if (i.value() >= positionAction) {
-            if (i.value() + positionChange > 0)
-                m_marks[i.key()] = i.value() + positionChange;
-            else
-                m_marks.remove(i.key());
+// The position 'from' is the cursor position after the change. If 'delta'
+// is positive there was a string of size 'delta' inserted after 'from'
+// and consequently all marks beyond 'from + delta' need to be incremented
+// by 'delta'. If text was removed, 'delta' is negative. All marks between
+// 'from' and 'from - delta' need to be removed, everything behing
+// 'from - delta' adjusted by 'delta'.
+void FakeVimHandler::Private::fixMarks(int from, int delta)
+{
+    //qDebug() << "ADJUSTING MARKS FROM " << from << " BY " << delta;
+    if (delta == 0)
+        return;
+    QHashIterator<int, int> it(m_marks);
+    while (it.hasNext()) {
+        it.next();
+        int pos = it.value();
+        if (delta > 0) {
+            // Inserted text.
+            if (pos >= from) {
+                //qDebug() << "MODIFIED: " << it.key() << pos;
+                setMark(it.key(), pos + delta);
+            }
+        } else {
+            // Removed text.
+            if (pos < from) {
+                // Nothing to do.
+            } else if (pos < from - delta) {
+                //qDebug() << "GONE: " << it.key();
+                m_marks.remove(it.key());
+            } else {
+                //qDebug() << "MODIFIED: " << it.key() << pos;
+                setMark(it.key(), pos + delta);
+             }
         }
     }
 }
@@ -3950,13 +4231,14 @@ 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
+void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
 {
     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();
+    fixMarks(block.position(), block.length() - contents.size());
     tc.insertText(contents);
 }
 
@@ -3983,8 +4265,8 @@ void FakeVimHandler::Private::enterVisualMode(VisualMode visualMode)
     setAnchor();
     m_positionPastEnd = m_anchorPastEnd = false;
     m_visualMode = visualMode;
-    m_marks['<'] = m_tc.position();
-    m_marks['>'] = m_tc.position();
+    setMark('<', m_tc.position());
+    setMark('>', m_tc.position());
     updateMiniBuffer();
     updateSelection();
 }
@@ -4011,11 +4293,16 @@ QWidget *FakeVimHandler::Private::editor() const
 void FakeVimHandler::Private::undo()
 {
     //qDebug() << " CURSOR POS: " << m_undoCursorPosition;
-    int current = m_tc.document()->availableUndoSteps();
-    //endEditBlock();
+    QTextDocument *doc = m_tc.document();
+    // FIXME: That's only an approximaxtion. The real solution might
+    // be to store marks and old userData with QTextBlock setUserData
+    // and retrieve them afterward.
+    const int current = doc->availableUndoSteps();
+    const int oldCount = doc->characterCount();
     EDITOR(undo());
-    //beginEditBlock();
-    int rev = m_tc.document()->availableUndoSteps();
+    const int delta = doc->characterCount() - oldCount;
+    fixMarks(position(), delta);
+    const int rev = doc->availableUndoSteps();
     if (current == rev)
         showBlackMessage(FakeVimHandler::tr("Already at oldest change"));
     else
@@ -4023,17 +4310,20 @@ void FakeVimHandler::Private::undo()
 
     if (m_undoCursorPosition.contains(rev))
         m_tc.setPosition(m_undoCursorPosition[rev]);
+    setTargetColumn();
     if (atEndOfLine())
         moveLeft();
 }
 
 void FakeVimHandler::Private::redo()
 {
-    int current = m_tc.document()->availableUndoSteps();
-    //endEditBlock();
+    QTextDocument *doc = m_tc.document();
+    const int current = m_tc.document()->availableUndoSteps();
+    const int oldCount = doc->characterCount();
     EDITOR(redo());
-    //beginEditBlock();
-    int rev = m_tc.document()->availableUndoSteps();
+    const int delta = doc->characterCount() - oldCount;
+    fixMarks(position(), delta);
+    const int rev = doc->availableUndoSteps();
     if (rev == current)
         showBlackMessage(FakeVimHandler::tr("Already at newest change"));
     else
@@ -4041,32 +4331,63 @@ void FakeVimHandler::Private::redo()
 
     if (m_undoCursorPosition.contains(rev))
         m_tc.setPosition(m_undoCursorPosition[rev]);
+    setTargetColumn();
+}
+
+void FakeVimHandler::Private::updateCursor()
+{
+    if (m_mode == ExMode || m_subsubmode == SearchSubSubMode) {
+        EDITOR(setCursorWidth(0));
+        EDITOR(setOverwriteMode(false));
+    } else if (m_mode == InsertMode) {
+        EDITOR(setCursorWidth(m_cursorWidth));
+        EDITOR(setOverwriteMode(false));
+    } else {
+        EDITOR(setCursorWidth(m_cursorWidth));
+        EDITOR(setOverwriteMode(true));
+    }
+}
+
+void FakeVimHandler::Private::enterReplaceMode()
+{
+    m_mode = ReplaceMode;
+    m_submode = NoSubMode;
+    m_subsubmode = NoSubSubMode;
+    m_commandPrefix.clear();
+    m_lastInsertion.clear();
+    m_lastDeletion.clear();
+    updateCursor();
 }
 
 void FakeVimHandler::Private::enterInsertMode()
 {
-    EDITOR(setCursorWidth(m_cursorWidth));
-    EDITOR(setOverwriteMode(false));
-    //leaveVisualMode();
     m_mode = InsertMode;
+    m_submode = NoSubMode;
+    m_subsubmode = NoSubSubMode;
+    m_commandPrefix.clear();
     m_lastInsertion.clear();
-    m_beginEditBlock = true;
+    m_lastDeletion.clear();
+    updateCursor();
 }
 
 void FakeVimHandler::Private::enterCommandMode()
 {
-    EDITOR(setCursorWidth(m_cursorWidth));
-    EDITOR(setOverwriteMode(true));
     if (atEndOfLine())
         moveLeft();
     m_mode = CommandMode;
+    m_submode = NoSubMode;
+    m_subsubmode = NoSubSubMode;
+    m_commandPrefix.clear();
+    updateCursor();
 }
 
 void FakeVimHandler::Private::enterExMode()
 {
-    EDITOR(setCursorWidth(0));
-    EDITOR(setOverwriteMode(false));
     m_mode = ExMode;
+    m_submode = NoSubMode;
+    m_subsubmode = NoSubSubMode;
+    m_commandPrefix = ":";
+    updateCursor();
 }
 
 void FakeVimHandler::Private::recordJump()
@@ -4076,13 +4397,6 @@ void FakeVimHandler::Private::recordJump()
     UNDO_DEBUG("jumps: " << m_jumpListUndo);
 }
 
-void FakeVimHandler::Private::recordNewUndo()
-{
-    //endEditBlock();
-    UNDO_DEBUG("---- BREAK ----");
-    //beginEditBlock();
-}
-
 Column FakeVimHandler::Private::indentation(const QString &line) const
 {
     int ts = config(ConfigTabStop).toInt();
@@ -4128,7 +4442,7 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown)
             ++pos;
         text.truncate(pos);
         // FIXME: handle 'smartindent' and 'cindent'
-        m_tc.insertText(text);
+        insertText(text);
         m_justAutoIndented = text.size();
     }
 }
@@ -4139,6 +4453,7 @@ bool FakeVimHandler::Private::removeAutomaticIndentation()
         return false;
     m_tc.movePosition(StartOfLine, KeepAnchor);
     m_tc.removeSelectedText();
+    fixMarks(m_tc.position(), -m_justAutoIndented);
     m_lastInsertion.chop(m_justAutoIndented);
     m_justAutoIndented = 0;
     return true;
@@ -4152,18 +4467,17 @@ void FakeVimHandler::Private::handleStartOfLine()
 
 void FakeVimHandler::Private::replay(const QString &command, int n)
 {
-    //qDebug() << "REPLAY: " << command;
-    m_inReplay = true;
+    //qDebug() << "REPLAY: " << quoteUnprintable(command);
+    g.inReplay = true;
     for (int i = n; --i >= 0; ) {
         foreach (QChar c, command) {
             //qDebug() << "  REPLAY: " << QString(c);
             handleKey(Input(c));
         }
     }
-    m_inReplay = false;
+    g.inReplay = false;
 }
 
-
 void FakeVimHandler::Private::selectWordTextObject(bool inner)
 {
     Q_UNUSED(inner); // FIXME
@@ -4172,7 +4486,7 @@ void FakeVimHandler::Private::selectWordTextObject(bool inner)
     setAnchor();
     // FIXME: Rework the 'anchor' concept.
     if (isVisualMode())
-        m_marks['<'] = m_tc.position();
+        setMark('<', m_tc.position());
     moveToWordBoundary(false, true, true);
     m_movetype = MoveInclusive;
 }
@@ -4185,7 +4499,7 @@ void FakeVimHandler::Private::selectWORDTextObject(bool inner)
     setAnchor();
     // FIXME: Rework the 'anchor' concept.
     if (isVisualMode())
-        m_marks['<'] = m_tc.position();
+        setMark('<', m_tc.position());
     moveToWordBoundary(true, true, true);
     m_movetype = MoveInclusive;
 }
@@ -4213,6 +4527,18 @@ void FakeVimHandler::Private::selectQuotedStringTextObject(bool inner, int type)
     Q_UNUSED(type);
 }
 
+int FakeVimHandler::Private::mark(int code) const
+{
+    // FIXME: distinguish local and global marks.
+    //qDebug() << "MARK: " << code << m_marks.value(code, -1) << m_marks;
+    return m_marks.value(code, -1);
+}
+
+void FakeVimHandler::Private::setMark(int code, int position)
+{
+    // FIXME: distinguish local and global marks.
+    m_marks[code] = position;
+}
 
 ///////////////////////////////////////////////////////////////////////
 //
@@ -4240,6 +4566,30 @@ bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
 {
     bool active = theFakeVimSetting(ConfigUseFakeVim)->value().toBool();
 
+    // Catch mouse events on the viewport.
+    QWidget *viewport = 0;
+    if (d->m_plaintextedit)
+        viewport = d->m_plaintextedit->viewport();
+    else if (d->m_textedit)
+        viewport = d->m_textedit->viewport();
+    if (ob == viewport) {
+        if (active && ev->type() == QEvent::MouseButtonRelease) {
+            QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
+            if (mev->button() == Qt::LeftButton) {
+                d->importSelection();
+                //return true;
+            }
+        }
+        if (active && ev->type() == QEvent::MouseButtonPress) {
+            QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
+            if (mev->button() == Qt::LeftButton) {
+                d->m_visualMode = NoVisualMode;
+                d->updateSelection();
+            }
+        }
+        return QObject::eventFilter(ob, ev);
+    }
+
     if (active && ev->type() == QEvent::Shortcut) {
         d->passShortcuts(false);
         return false;
@@ -4314,6 +4664,11 @@ void FakeVimHandler::setCurrentFileName(const QString &fileName)
    d->m_currentFileName = fileName;
 }
 
+QString FakeVimHandler::currentFileName() const
+{
+    return d->m_currentFileName;
+}
+
 void FakeVimHandler::showBlackMessage(const QString &msg)
 {
    d->showBlackMessage(msg);
index 7543d8c..4e877d3 100644 (file)
 namespace FakeVim {
 namespace Internal {
 
+enum RangeMode
+{
+    RangeCharMode,         // v
+    RangeLineMode,         // V
+    RangeLineModeExclusive,
+    RangeBlockMode,        // Ctrl-v
+    RangeBlockAndTailMode, // Ctrl-v for D and X
+};
+
+struct Range
+{
+    Range();
+    Range(int b, int e, RangeMode m = RangeCharMode);
+    QString toString() const;
+
+    int beginPos;
+    int endPos;
+    RangeMode rangemode;
+};
+
+struct ExCommand
+{
+    ExCommand() : hasBang(false) {}
+    ExCommand(const QString &cmd, const QString &args = QString(),
+        const Range &range = Range());
+
+    QString cmd;
+    bool hasBang;
+    QString args;
+    Range range;
+};
+
 class FakeVimHandler : public QObject
 {
     Q_OBJECT
@@ -53,6 +85,8 @@ public:
 
 public slots:
     void setCurrentFileName(const QString &fileName);
+    QString currentFileName() const;
+
     void showBlackMessage(const QString &msg);
     void showRedMessage(const QString &msg);
 
@@ -76,8 +110,6 @@ signals:
     void statusDataChanged(const QString &msg);
     void extraInformationChanged(const QString &msg);
     void selectionChanged(const QList<QTextEdit::ExtraSelection> &selection);
-    void writeFileRequested(bool *handled,
-        const QString &fileName, const QString &contents);
     void writeAllRequested(QString *error);
     void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
     void checkForElectricCharacter(bool *result, QChar c);
@@ -86,8 +118,7 @@ signals:
     void windowCommandRequested(int key);
     void findRequested(bool reverse);
     void findNextRequested(bool reverse);
-    void handleExCommandRequested(const QString &cmd);
-    void handleSetCommandRequested(bool *handled, const QString &cmd);
+    void handleExCommandRequested(bool *handled, const ExCommand &cmd);
 
 public:
     class Private;
@@ -101,4 +132,7 @@ private:
 } // namespace Internal
 } // namespace FakeVim
 
+Q_DECLARE_METATYPE(FakeVim::Internal::ExCommand);
+
+
 #endif // FAKEVIM_HANDLER_H
index 6a9f52b..450a862 100644 (file)
         </property>
        </widget>
       </item>
+      <item row="2" column="2">
+       <widget class="QCheckBox" name="checkBoxShowMarks">
+        <property name="text">
+         <string>Show position of text marks</string>
+        </property>
+       </widget>
+      </item>
       <item row="3" column="0" colspan="2">
        <widget class="QCheckBox" name="checkBoxSmartTab">
         <property name="text">
index a698cb0..5c66cec 100644 (file)
@@ -73,9 +73,9 @@
 #include <indenter.h>
 
 #include <QtCore/QDebug>
+#include <QtCore/QFile>
 #include <QtCore/QtPlugin>
 #include <QtCore/QObject>
-#include <QtCore/QPoint>
 #include <QtCore/QSettings>
 #include <QtCore/QTextStream>
 
@@ -120,6 +120,8 @@ const char * const CMD_FILE_PREV                  = "FakeVim.SwitchFilePrev";
 namespace FakeVim {
 namespace Internal {
 
+typedef QMap<QString, QRegExp> CommandMap;
+
 class FakeVimOptionPage : public Core::IOptionsPage
 {
     Q_OBJECT
@@ -168,6 +170,8 @@ QWidget *FakeVimOptionPage::createPage(QWidget *parent)
         m_ui.checkBoxHlSearch);
     m_group.insert(theFakeVimSetting(ConfigShiftWidth),
         m_ui.spinBoxShiftWidth);
+    m_group.insert(theFakeVimSetting(ConfigShowMarks),
+        m_ui.checkBoxShowMarks);
 
     m_group.insert(theFakeVimSetting(ConfigSmartTab),
         m_ui.checkBoxSmartTab);
@@ -199,8 +203,8 @@ QWidget *FakeVimOptionPage::createPage(QWidget *parent)
         QTextStream(&m_searchKeywords)
             << ' ' << m_ui.checkBoxAutoIndent->text()
             << ' ' << m_ui.checkBoxExpandTab->text()
+            << ' ' << m_ui.checkBoxShowMarks->text()
             << ' ' << m_ui.checkBoxSmartIndent->text()
-            << ' ' << m_ui.checkBoxExpandTab->text()
             << ' ' << m_ui.checkBoxHlSearch->text()
             << ' ' << m_ui.checkBoxIncSearch->text()
             << ' ' << m_ui.checkBoxSmartTab->text()
@@ -278,16 +282,12 @@ Q_DECLARE_METATYPE(CommandItem*);
 namespace FakeVim {
 namespace Internal {
 
-static QMap<QString, QRegExp> s_exCommandMap;
-static QMap<QString, QRegExp> s_defaultExCommandMap;
-
-
 class FakeVimExCommandsPage : public Core::CommandMappings
 {
     Q_OBJECT
 
 public:
-    FakeVimExCommandsPage() {}
+    FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
 
     // IOptionsPage
     QString id() const { return QLatin1String(Constants::SETTINGS_EX_CMDS_ID); }
@@ -298,6 +298,8 @@ public:
 
     QWidget *createPage(QWidget *parent);
     void initialize();
+    CommandMap &exCommandMap();
+    CommandMap &defaultExCommandMap();
 
 public slots:
     void commandChanged(QTreeWidgetItem *current);
@@ -309,6 +311,7 @@ public slots:
 private:
     void setRegex(const QString &regex);
     QList<CommandItem *> m_citems;
+    FakeVimPluginPrivate *m_q;
 };
 
 QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
@@ -341,15 +344,16 @@ void FakeVimExCommandsPage::initialize()
         QTreeWidgetItem *item = new QTreeWidgetItem;
         ci->m_cmd = c;
         ci->m_item = item;
-        m_citems << ci;
+        m_citems.append(ci);
 
         const QString name = uidm->stringForUniqueIdentifier(c->id());
         const int pos = name.indexOf(QLatin1Char('.'));
         const QString section = name.left(pos);
-        const QString subId = name.mid(pos+1);
+        const QString subId = name.mid(pos + 1);
 
         if (!sections.contains(section)) {
-            QTreeWidgetItem *categoryItem = new QTreeWidgetItem(commandList(), QStringList() << section);
+            QTreeWidgetItem *categoryItem =
+                new QTreeWidgetItem(commandList(), QStringList() << section);
             QFont f = categoryItem->font(0);
             f.setBold(true);
             categoryItem->setFont(0, f);
@@ -361,14 +365,16 @@ void FakeVimExCommandsPage::initialize()
         item->setText(0, subId);
 
         if (c->action()) {
-            QString text = c->hasAttribute(Command::CA_UpdateText) && !c->defaultText().isNull() ? c->defaultText() : c->action()->text();
+            QString text = c->hasAttribute(Command::CA_UpdateText)
+                    && !c->defaultText().isNull()
+                ? c->defaultText() : c->action()->text();
             text.remove(QRegExp("&(?!&)"));
             item->setText(1, text);
         } else {
             item->setText(1, c->shortcut()->whatsThis());
         }
-        if (s_exCommandMap.contains(name)) {
-            ci->m_regex = s_exCommandMap[name].pattern();
+        if (exCommandMap().contains(name)) {
+            ci->m_regex = exCommandMap()[name].pattern();
         } else {
             ci->m_regex.clear();
         }
@@ -376,7 +382,7 @@ void FakeVimExCommandsPage::initialize()
         item->setText(2, ci->m_regex);
         item->setData(0, Qt::UserRole, qVariantFromValue(ci));
 
-        if (ci->m_regex != s_defaultExCommandMap[name].pattern())
+        if (ci->m_regex != defaultExCommandMap()[name].pattern())
             setModified(item, true);
     }
 
@@ -407,10 +413,10 @@ void FakeVimExCommandsPage::targetIdentifierChanged()
     if (current->data(0, Qt::UserRole).isValid()) {
         citem->m_regex = targetEdit()->text();
         current->setText(2, citem->m_regex);
-        s_exCommandMap[name] = QRegExp(citem->m_regex);
+        exCommandMap()[name] = QRegExp(citem->m_regex);
     }
 
-    if (citem->m_regex != s_defaultExCommandMap[name].pattern())
+    if (citem->m_regex != defaultExCommandMap()[name].pattern())
         setModified(current, true);
     else
         setModified(current, false);
@@ -429,8 +435,8 @@ void FakeVimExCommandsPage::resetTargetIdentifier()
     if (current && current->data(0, Qt::UserRole).isValid()) {
         CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
         const QString &name = uidm->stringForUniqueIdentifier(citem->m_cmd->id());
-        if (s_defaultExCommandMap.contains(name))
-            setRegex(s_defaultExCommandMap[name].pattern());
+        if (defaultExCommandMap().contains(name))
+            setRegex(defaultExCommandMap()[name].pattern());
         else
             setRegex(QString());
     }
@@ -446,8 +452,8 @@ void FakeVimExCommandsPage::defaultAction()
     UniqueIDManager *uidm = UniqueIDManager::instance();
     foreach (CommandItem *item, m_citems) {
         const QString &name = uidm->stringForUniqueIdentifier(item->m_cmd->id());
-        if (s_defaultExCommandMap.contains(name)) {
-            item->m_regex = s_defaultExCommandMap[name].pattern();
+        if (defaultExCommandMap().contains(name)) {
+            item->m_regex = defaultExCommandMap()[name].pattern();
         } else {
             item->m_regex.clear();
         }
@@ -479,6 +485,7 @@ public:
     FakeVimPluginPrivate(FakeVimPlugin *);
     ~FakeVimPluginPrivate();
     friend class FakeVimPlugin;
+    friend class FakeVimExCommandsPage;
 
     bool initialize();
     void aboutToShutdown();
@@ -499,12 +506,10 @@ private slots:
     void showCommandBuffer(const QString &contents);
     void showExtraInformation(const QString &msg);
     void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
-    void writeFile(bool *handled, const QString &fileName, const QString &contents);
     void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
     void checkForElectricCharacter(bool *result, QChar c);
     void indentRegion(int *amount, int beginLine, int endLine,  QChar typedChar);
-    void handleExCommand(const QString &cmd);
-    void handleSetCommand(bool *handled, QString cmd);
+    void handleExCommand(bool *handled, const ExCommand &cmd);
 
     void handleDelayedQuitAll(bool forced);
     void handleDelayedQuit(bool forced, Core::IEditor *editor);
@@ -523,11 +528,16 @@ private:
     FakeVimExCommandsPage *m_fakeVimExCommandsPage;
     QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
 
-    void triggerAction(const QStringcode);
-    void setActionChecked(const QStringcode, bool check);
+    void triggerAction(const QString &code);
+    void setActionChecked(const QString &code, bool check);
 
     void readSettings(QSettings *settings);
     void writeSettings(QSettings *settings);
+
+    CommandMap &exCommandMap() { return m_exCommandMap; }
+    CommandMap &defaultExCommandMap() { return m_exCommandMap; }
+    CommandMap m_exCommandMap;
+    CommandMap m_defaultExCommandMap;
 };
 
 } // namespace Internal
@@ -538,23 +548,22 @@ FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
     q = plugin;
     m_fakeVimOptionsPage = 0;
     m_fakeVimExCommandsPage = 0;
-
-    s_defaultExCommandMap[Constants::CMD_FILE_NEXT] =
+    defaultExCommandMap()[Constants::CMD_FILE_NEXT] =
         QRegExp("^n(ext)?!?( (.*))?$");
-    s_defaultExCommandMap[Constants::CMD_FILE_PREV] =
+    defaultExCommandMap()[Constants::CMD_FILE_PREV] =
         QRegExp("^(N(ext)?|prev(ious)?)!?( (.*))?$");
-    s_defaultExCommandMap[CppTools::Constants::SWITCH_HEADER_SOURCE] =
+    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
         QRegExp("^A$");
-    s_defaultExCommandMap[ProjectExplorer::Constants::BUILD] =
-        QRegExp("^make$");
-    s_defaultExCommandMap["Coreplugin.OutputPane.previtem"] =
+    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
         QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
-    s_defaultExCommandMap["Coreplugin.OutputPane.nextitem"] =
+    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
         QRegExp("^cn(ext)?!?( (.*))?$");
-    s_defaultExCommandMap[CppEditor::Constants::JUMP_TO_DEFINITION] =
+    defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
         QRegExp("^tag?$");
-    s_defaultExCommandMap[Core::Constants::GO_BACK] =
+    defaultExCommandMap()[Core::Constants::GO_BACK] =
         QRegExp("^pop?$");
+    defaultExCommandMap()[QLatin1String("QtCreator.Locate")] =
+        QRegExp("^e$");
 }
 
 FakeVimPluginPrivate::~FakeVimPluginPrivate()
@@ -587,7 +596,7 @@ bool FakeVimPluginPrivate::initialize()
     q->addObject(m_fakeVimOptionsPage);
     theFakeVimSettings()->readSettings(Core::ICore::instance()->settings());
 
-    m_fakeVimExCommandsPage = new FakeVimExCommandsPage;
+    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
     q->addObject(m_fakeVimExCommandsPage);
     readSettings(Core::ICore::instance()->settings());
 
@@ -607,8 +616,6 @@ bool FakeVimPluginPrivate::initialize()
     connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
         this, SLOT(editorOpened(Core::IEditor*)));
 
-    connect(theFakeVimSetting(SettingsDialog), SIGNAL(triggered()),
-        this, SLOT(showSettingsDialog()));
     connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
         this, SLOT(setUseFakeVim(QVariant)));
     connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
@@ -624,7 +631,7 @@ bool FakeVimPluginPrivate::initialize()
     cmd->setAttribute(Command::CA_Hide);
     connect(switchFilePrevAction, SIGNAL(triggered()), this, SLOT(switchFilePrev()));
 
-    // Delayed operatiosn
+    // Delayed operations.
     connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
         this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
     connect(this, SIGNAL(delayedQuitAllRequested(bool)),
@@ -644,14 +651,14 @@ void FakeVimPluginPrivate::writeSettings(QSettings *settings)
     settings->beginWriteArray(QLatin1String(exCommandMapGroup));
 
     int count = 0;
-    typedef QMap<QString, QRegExp>::const_iterator Iterator;
-    const Iterator end = s_exCommandMap.constEnd();
-    for (Iterator it = s_exCommandMap.constBegin(); it != end; ++it) {
+    typedef CommandMap::const_iterator Iterator;
+    const Iterator end = exCommandMap().constEnd();
+    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
         const QString &id = it.key();
         const QRegExp &re = it.value();
 
-        if ((s_defaultExCommandMap.contains(id) && s_defaultExCommandMap[id] != re)
-            || (!s_defaultExCommandMap.contains(id) && !re.pattern().isEmpty())) {
+        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
+            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
             settings->setArrayIndex(count);
             settings->setValue(QLatin1String(idKey), id);
             settings->setValue(QLatin1String(reKey), re.pattern());
@@ -664,14 +671,14 @@ void FakeVimPluginPrivate::writeSettings(QSettings *settings)
 
 void FakeVimPluginPrivate::readSettings(QSettings *settings)
 {
-    s_exCommandMap = s_defaultExCommandMap;
+    exCommandMap() = defaultExCommandMap();
 
     int size = settings->beginReadArray(QLatin1String(exCommandMapGroup));
     for (int i = 0; i < size; ++i) {
         settings->setArrayIndex(i);
         const QString id = settings->value(QLatin1String(idKey)).toString();
         const QString re = settings->value(QLatin1String(reKey)).toString();
-        s_exCommandMap[id] = QRegExp(re);
+        exCommandMap()[id] = QRegExp(re);
     }
     settings->endArray();
 }
@@ -702,18 +709,18 @@ void FakeVimPluginPrivate::showSettingsDialog()
         QLatin1String(Constants::SETTINGS_ID));
 }
 
-void FakeVimPluginPrivate::triggerAction(const QStringcode)
+void FakeVimPluginPrivate::triggerAction(const QString &code)
 {
     Core::ActionManager *am = Core::ICore::instance()->actionManager();
     QTC_ASSERT(am, return);
     Core::Command *cmd = am->command(code);
-    QTC_ASSERT(cmd, return);
+    QTC_ASSERT(cmd, qDebug() << "UNKNOW CODE: " << code; return);
     QAction *action = cmd->action();
     QTC_ASSERT(action, return);
     action->trigger();
 }
 
-void FakeVimPluginPrivate::setActionChecked(const QStringcode, bool check)
+void FakeVimPluginPrivate::setActionChecked(const QString &code, bool check)
 {
     Core::ActionManager *am = Core::ICore::instance()->actionManager();
     QTC_ASSERT(am, return);
@@ -826,8 +833,6 @@ void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
         this, SLOT(showExtraInformation(QString)));
     connect(handler, SIGNAL(commandBufferChanged(QString)),
         this, SLOT(showCommandBuffer(QString)));
-    connect(handler, SIGNAL(writeFileRequested(bool*,QString,QString)),
-        this, SLOT(writeFile(bool*,QString,QString)));
     connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
         this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
     connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
@@ -845,10 +850,8 @@ void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
     connect(handler, SIGNAL(findNextRequested(bool)),
         this, SLOT(findNext(bool)));
 
-    connect(handler, SIGNAL(handleExCommandRequested(QString)),
-        this, SLOT(handleExCommand(QString)));
-    connect(handler, SIGNAL(handleSetCommandRequested(bool *,QString)),
-        this, SLOT(handleSetCommand(bool *,QString)));
+    connect(handler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
+        this, SLOT(handleExCommand(bool*,ExCommand)));
 
     handler->setCurrentFileName(editor->file()->fileName());
     handler->installEventFilter();
@@ -909,33 +912,12 @@ void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
         *result = bt->isElectricCharacter(c);
 }
 
-void FakeVimPluginPrivate::writeFile(bool *handled,
-    const QString &fileName, const QString &contents)
-{
-    Q_UNUSED(contents)
-
-    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
-    if (!handler)
-        return;
-
-    Core::IEditor *editor = m_editorToHandler.key(handler);
-    if (editor && editor->file()->fileName() == fileName) {
-        // Handle that as a special case for nicer interaction with core
-        Core::IFile *file = editor->file();
-        Core::ICore::instance()->fileManager()->blockFileChange(file);
-        file->save(fileName);
-        Core::ICore::instance()->fileManager()->unblockFileChange(file);
-        *handled = true;
-    }
-}
-
-void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
+void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
 {
-    static QRegExp reWriteAll("^wa(ll)?!?$");
-    static QRegExp reQuit("^q!?$");
-    static QRegExp reQuitAll("^qa!?$");
-
     using namespace Core;
+    //qDebug() << "PLUGIN HANDLE: " << cmd.cmd;
+
+    *handled = false;
 
     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
     if (!handler)
@@ -944,7 +926,27 @@ void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
     EditorManager *editorManager = EditorManager::instance();
     QTC_ASSERT(editorManager, return);
 
-    if (reWriteAll.indexIn(cmd) != -1) {
+    *handled = true;
+    if (cmd.cmd == "w" || cmd.cmd == "write") {
+        Core::IEditor *editor = m_editorToHandler.key(handler);
+        const QString fileName = handler->currentFileName();
+        if (editor && editor->file()->fileName() == fileName) {
+            // Handle that as a special case for nicer interaction with core
+            Core::IFile *file = editor->file();
+            Core::ICore::instance()->fileManager()->blockFileChange(file);
+            file->save(fileName);
+            Core::ICore::instance()->fileManager()->unblockFileChange(file);
+            // Check result by reading back.
+            QFile file3(fileName);
+            file3.open(QIODevice::ReadOnly);
+            QByteArray ba = file3.readAll();
+            handler->showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
+                .arg(fileName).arg(" ")
+                .arg(ba.count('\n')).arg(ba.size()));
+        } else {
+            handler->showRedMessage(tr("File not saved"));
+        }
+    } else if (cmd.cmd == "wa" || cmd.cmd == "wall") {
         // :wa
         FileManager *fm = ICore::instance()->fileManager();
         QList<IFile *> toSave = fm->modifiedFiles();
@@ -953,43 +955,47 @@ void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
             handler->showBlackMessage(tr("Saving succeeded"));
         else
             handler->showRedMessage(tr("%n files not saved", 0, failed.size()));
-    } else if (reQuit.indexIn(cmd) != -1) {
-        // :q
-        bool forced = cmd.contains(QChar('!'));
-        emit delayedQuitRequested(forced, m_editorToHandler.key(handler));
-    } else if (reQuitAll.indexIn(cmd) != -1) {
+    } else if (cmd.cmd == "q" || cmd.cmd == "quit") {
+        // :q[uit]
+        emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+    } else if (cmd.cmd == "qa" || cmd.cmd == "qall") {
         // :qa
-        bool forced = cmd.contains(QChar('!'));
-        emit delayedQuitAllRequested(forced);
+        emit delayedQuitAllRequested(cmd.hasBang);
+    } else if (cmd.cmd == "sp" || cmd.cmd == "split") {
+        // :sp[lit]
+        triggerAction(Core::Constants::SPLIT);
+    } else if (cmd.cmd == "vs" || cmd.cmd == "vsplit") {
+        // :vs[plit]
+        triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
+    } else if (cmd.cmd == "mak" || cmd.cmd == "make") {
+        // :mak[e][!] [arguments]
+        triggerAction(ProjectExplorer::Constants::BUILD);
+    } else if (cmd.cmd == "se" || cmd.cmd == "set") {
+        if (cmd.args.isEmpty()) {
+            // :set
+            showSettingsDialog();
+        } else if (cmd.args == "ic" || cmd.args == "ignorecase") {
+            // :set noic
+            setActionChecked(Find::Constants::CASE_SENSITIVE, false);
+            *handled = false; // Let the handler see it as well.
+        } else if (cmd.args == "noic" || cmd.args == "noignorecase") {
+            // :set noic
+            setActionChecked(Find::Constants::CASE_SENSITIVE, true);
+            *handled = false; // Let the handler see it as well.
+        }
     } else {
-        typedef QMap<QString, QRegExp>::const_iterator Iterator;
-        const Iterator end = s_exCommandMap.constEnd();
-        for (Iterator it = s_exCommandMap.constBegin(); it != end; ++it) {
+        // Check whether one of the configure commands matches.
+        typedef CommandMap::const_iterator Iterator;
+        const Iterator end = exCommandMap().constEnd();
+        for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
             const QString &id = it.key();
             const QRegExp &re = it.value();
-
-            if (!re.pattern().isEmpty() && re.indexIn(cmd) != -1) {
+            if (!re.pattern().isEmpty() && re.indexIn(cmd.cmd) != -1) {
                 triggerAction(id);
                 return;
             }
         }
-
-        handler->showRedMessage(tr("Not an editor command: %1").arg(cmd));
-    }
-}
-
-void FakeVimPluginPrivate::handleSetCommand(bool *handled, QString cmd)
-{
-    *handled = false;
-    bool value = true;
-    if (cmd.startsWith("no")) {
-        value = false;
-        cmd = cmd.mid(2);
-    }
-
-    if (cmd == "ic" || cmd == "ignorecase") {
-        setActionChecked(Find::Constants::CASE_SENSITIVE, value);
-        *handled = true;
+        *handled = false;
     }
 }
 
@@ -1064,8 +1070,7 @@ void FakeVimPluginPrivate::indentRegion(int *amount, int beginLine, int endLine,
     const QTextDocument *doc = bt->document();
     const TextEditor::TextBlockIterator docStart(doc->begin());
     QTextBlock cur = doc->findBlockByNumber(beginLine);
-    for(int i = beginLine; i<= endLine; ++i)
-    {
+    for (int i = beginLine; i <= endLine; ++i) {
         if (typedChar == 0 && cur.text().simplified().isEmpty()) {
             // clear empty lines
             *amount = 0;
@@ -1130,6 +1135,17 @@ void FakeVimPluginPrivate::switchFilePrev()
     switchFile(true);
 }
 
+CommandMap &FakeVimExCommandsPage::exCommandMap()
+{
+    return m_q->exCommandMap();
+}
+
+CommandMap &FakeVimExCommandsPage::defaultExCommandMap()
+{
+    return m_q->defaultExCommandMap();
+}
+
+
 ///////////////////////////////////////////////////////////////////////
 //
 // FakeVimPlugin